Jupyter筆記本結合IPython提供互動式程式碼執行、結果顯示與檔案分享,適合各種資料分析任務。搭配NumPy,更能發揮其在科學計算和資料分析的強大功能。NumPy 提供高效的數值運算和資料儲存方式,讓使用者能輕鬆進行資料分析和科學計算,並運用各種工具和函式庫進行資料視覺化和機器學習。理解Python內建資料型別和NumPy資料型別的差異,對於提升程式碼執行效率至關重要。NumPy 陣列的固定型別特性,相較於 Python 列表的動態型別,在處理大量資料時能顯著提升效能。

從 Python 列表的動態型別到 NumPy 陣列的固定型別,效能提升顯著。利用 NumPy 的內建函式,可以高效地建立各種陣列,例如零陣列、一陣列、指定值的陣列、線性序列陣列和均勻分佈陣列。此外,NumPy 也提供了生成隨機陣列和矩陣的功能,包含均勻分佈、正態分佈、整數隨機數、單位矩陣和未初始化的陣列,方便進行模擬和測試。瞭解 NumPy 的標準資料型別,例如布林值、整數和浮點數等,對於選擇正確的資料型別以避免溢位和其他問題至關重要。

瞭解Jupyter筆記本的應用

Jupyter筆記本是一種強大的工具,能夠讓使用者以互動方式進行資料分析和科學計算。透過瀏覽網際網路上可用的Jupyter筆記本,使用者可以看到其他人如何使用IPython進行各種資料分析任務。

Jupyter筆記本的特點

Jupyter筆記本提供了一種獨特的方式來展示資料分析過程,允許使用者以互動方式執行程式碼、顯示結果和建立可分享的檔案。這些筆記本可以包含從簡單示例到完整課程和書籍等各種內容。

IPython的應用

IPython是一種根據網頁的互動式計算環境,能夠讓使用者以更直觀的方式進行資料分析和科學計算。透過IPython,使用者可以輕鬆地建立和分享Jupyter筆記本,同時也能夠使用各種工具和函式庫來進行資料分析。

NumPy的介紹

NumPy是一種根據Python的數值計算函式庫,能夠提供高效的數值運算和資料儲存。透過NumPy,使用者可以輕鬆地進行資料分析和科學計算,同時也能夠使用各種工具和函式庫來進行資料視覺化和機器學習。

import numpy as np

# 建立一個NumPy陣列
array = np.array([1, 2, 3, 4, 5])

# 顯示陣列的內容
print(array)

內容解密:

上述程式碼建立了一個NumPy陣列,並顯示其內容。NumPy陣列是一種高效的數值儲存和運算方式,能夠讓使用者快速地進行資料分析和科學計算。

NumPy的應用

NumPy在資料分析和科學計算中有廣泛的應用,包括資料儲存、數值運算、資料視覺化和機器學習等。透過NumPy,使用者可以輕鬆地進行各種資料分析任務,同時也能夠使用各種工具和函式庫來進行更深入的分析。

圖表翻譯:

上述圖表顯示了NumPy在資料分析和科學計算中的應用流程。從資料儲存開始,使用者可以使用NumPy進行數值運算、資料視覺化和機器學習等任務。這個流程能夠讓使用者快速地進行各種資料分析任務,同時也能夠提供更深入的瞭解。

資料型別在Python中的理解

在進行資料驅動的科學計算和資料分析時,瞭解資料如何儲存和操作是非常重要的。本章將探討Python語言本身如何處理資料陣列,以及NumPy如何改進這些處理方式。瞭解這些差異是理解本文後續內容的基礎。

Python使用者通常被其動態型別(dynamic typing)所吸引,這意味著變數不需要明確宣告其資料型別。與靜態型別語言(如C或Java)相比,後者要求每個變數都必須明確宣告其型別,動態型別語言則跳過了這一步。例如,在C語言中,你可能會這樣指定一個運算:

int result = 0;
for (int i = 0; i < 100; i++) {
    result += i;
}

而在Python中,等效的運算可以這樣寫:

result = 0
for i in range(100):
    result += i

注意到主要的差異:在C語言中,每個變數的資料型別都需要明確宣告,而在Python中,資料型別是動態推斷的。這意味著,你可以將任何型別的資料賦給任何變數:

x = 4
x = "four"

在這裡,我們將x的內容從整數改為字串。在C語言中,相同的操作將導致編譯錯誤或其他意外後果:

int x = 4;
x = "four"; // 錯誤

這種靈活性是使Python和其他動態型別語言方便易用的原因之一。但是,這也意味著Python變數不僅僅是其值,它們還包含了關於值型別的額外資訊。在後續章節中,我們將更深入地探討這一點。

資料型別的重要性

在進行資料分析時,瞭解資料型別的重要性不言而喻。不同的資料型別有不同的儲存和操作方式,瞭解這些差異可以幫助你更有效地分析資料。NumPy作為一個高效的數值計算函式庫,提供了強大的支援來處理不同資料型別。

Python 整數:不僅僅是一個整數

在標準的 Python 實作中,所有物件都是以 C 結構實作的。這意味著每個 Python 物件不僅包含其值,也包含其他資訊。例如,當我們在 Python 中定義一個整數,如 x = 10000x 不僅是一個「原始」整數,而是一個指向複合 C 結構的指標,這個結構包含多個值。

透過檢視 Python 3.10 的原始碼,我們發現整數(long)型別的定義實際上如下所示(當 C 宏被展開後):

struct _longobject {
    long ob_refcnt;
    PyTypeObject *ob_type;
    size_t ob_size;
    long ob_digit[1];
};

一個 Python 3.10 中的單個整數實際上包含四個部分:

  • ob_refcnt:一個參照計數,幫助 Python 靜默地處理記憶體組態和釋放。
  • ob_type:編碼變數的型別。
  • ob_size:指定後續資料成員的大小。
  • ob_digit:包含我們期望 Python 變數表示的實際整數值。

這意味著在 Python 中儲存整數與在編譯語言(如 C)中儲存整數相比,有一些額外的開銷,如圖 4-1 所示。

圖 4-1:C 和 Python 整數之間的差異

在這裡,PyObject_HEAD 是結構中包含參照計數、型別程式碼和之前提到的其他部分。

注意這裡的差異:C 整數基本上是對記憶體中某個位置的標籤,其位元組編碼整數值。Python 整數是指向記憶體中某個位置的指標,該位置包含所有 Python 物件資訊,包括包含整數值的位元組。Python 整數結構中的這些額外資訊使 Python 能夠如此自由和動態地編碼。然而,這些額外資訊也帶來了成本,特別是在結合了許多這些物件的結構中尤其明顯。

Python 列表:不僅僅是一個列表

現在讓我們考慮一下使用一個包含多個 Python 物件的 Python 資料結構會發生什麼。Python 中的標準可變多元素容器是列表。我們可以如下所示建立一個整數列表:

L = list(range(10))
print(L)

輸出:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

或者,我們可以建立一個字串列表:

L2 = [str(c) for c in L]
print(L2)

輸出:

['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

內容解密:

上述程式碼展示瞭如何在 Python 中建立整數和字串列表。list(range(10)) 建立了一個包含從 0 到 9 的整數的列表,而 [str(c) for c in L] 建立了一個包含相同整數的字串表示的列表。

圖表翻譯:

此圖表展示了 Python 列表如何包含多個整數,每個整數都具有額外資訊,如參照計數、型別程式碼、大小和實際值。這些額外資訊使 Python 能夠高效地管理記憶體和執行動態操作。

什麼是動態型別列表和固定型別陣列?

在 Python 中,列表(list)是一種動態型別的資料結構,可以儲存不同型別的資料。然而,這種靈活性是以效率為代價的,因為每個列表元素都必須包含其自己的型別、參照計數和其他資訊。相比之下,固定型別陣列(如 NumPy 陣列)可以更有效地儲存相同型別的資料。

固定型別陣列的優點

固定型別陣列的優點在於它們可以更有效地儲存和操作資料。由於所有元素都是相同型別的,因此可以省略每個元素的型別資訊,從而節省空間和時間。另外,固定型別陣列還可以提供更快的資料存取和操作速度。

Python 中的固定型別陣列

Python 提供了多種選擇來儲存資料在有效的固定型別資料緩衝區中。內建的 array 模組(自 Python 3.3 起可用)可以用來建立密集陣列的統一型別。例如:

import array
L = list(range(10))
A = array.array('i', L)
print(A)  # array('i', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

其中,'i' 是一個型別程式碼,指示內容是整數。

NumPy 陣列

NumPy 陣列是 Python 中最常用的固定型別陣列實作。NumPy 陣列提供了高效的資料儲存和操作功能。例如:

import numpy as np
np.array([1, 4, 2, 5, 3])  # array([1, 4, 2, 5, 3])

注意,NumPy 陣列只能包含相同型別的資料。如果型別不匹配,NumPy 會根據其型別提升規則進行提升。例如:

np.array([3.14, 4, 2, 3])  # array([3.14, 4., 2., 3. ])

如果需要明確設定結果陣列的資料型別,可以使用 dtype 引數:

np.array([3.14, 4, 2, 3], dtype=float)  # array([3.14, 4., 2., 3. ])

圖表翻譯:

這個圖表展示了 Python 列表、固定型別陣列和 NumPy 陣列之間的關係。Python 列表是一種動態型別的資料結構,而固定型別陣列可以更有效地儲存相同型別的資料。NumPy 陣列是 Python 中最常用的固定型別陣列實作,提供了高效的資料儲存和操作功能。

使用NumPy建立陣列

NumPy陣列是多維度的,可以使用列表或其他方法來初始化。以下是使用列表初始化一個二維陣列的例子:

import numpy as np

# 使用巢狀列表初始化二維陣列
array_2d = np.array([range(i, i + 3) for i in [2, 4, 6]])
print(array_2d)

輸出結果:

[[2 3 4]
 [4 5 6]
 [6 7 8]]

內部列表被視為結果陣列的行。

從頭開始建立陣列

尤其是對於大型陣列,使用NumPy內建的函式來建立陣列更為高效。以下是一些例子:

# 建立一個長度為10的整數陣列,填充0
array_zeros = np.zeros(10, dtype=int)
print(array_zeros)

# 建立一個3x5的浮點數陣列,填充1
array_ones = np.ones((3, 5), dtype=float)
print(array_ones)

# 建立一個3x5的陣列,填充3.14
array_full = np.full((3, 5), 3.14)
print(array_full)

# 建立一個線性序列的陣列,從0開始,到20結束,步長為2
array_arange = np.arange(0, 20, 2)
print(array_arange)

# 建立一個包含5個值的陣列,均勻分佈在0到1之間
array_linspace = np.linspace(0, 1, 5)
print(array_linspace)

輸出結果:

[0 0 0 0 0 0 0 0 0 0]
[[1. 1. 1. 1. 1.]
 [1. 1. 1. 1. 1.]
 [1. 1. 1. 1. 1.]]
[[3.14 3.14 3.14 3.14 3.14]
 [3.14 3.14 3.14 3.14 3.14]
 [3.14 3.14 3.14 3.14 3.14]]
[ 0  2  4  6  8 10 12 14 16 18]
[0.   0.25 0.5  0.75 1.  ]

內容解密:

上述程式碼示範了使用NumPy建立不同型別的陣列。np.zeros()函式建立一個填充0的陣列,np.ones()函式建立一個填充1的陣列,np.full()函式建立一個填充指定值的陣列。np.arange()函式建立一個線性序列的陣列,np.linspace()函式建立一個均勻分佈的陣列。

圖表翻譯:

此圖表展示了使用NumPy建立不同型別的陣列的流程。每個函式都對應著不同的建立方式,最終產生不同的陣列結果。

生成隨機陣列和矩陣

NumPy 提供了多種方法來生成隨機陣列和矩陣。以下是幾個例子:

1. 均勻分佈的隨機陣列

可以使用 np.random.random() 函式來生成均勻分佈的隨機陣列。例如:

import numpy as np

# 生成一個 1x5 的陣列,包含 0 到 1 之間的均勻分佈隨機數
arr = np.array([0, 0.25, 0.5, 0.75, 1])
print(arr)

輸出:

[0.  0.25 0.5  0.75 1.  ]

2. 正態分佈的隨機陣列

可以使用 np.random.normal() 函式來生成正態分佈的隨機陣列。例如:

import numpy as np

# 生成一個 3x3 的陣列,包含均值為 0、標準差為 1 的正態分佈隨機數
arr = np.random.normal(0, 1, (3, 3))
print(arr)

輸出:

[[-0.46652655 -0.59158776 -1.05392451]
 [-1.72634268  0.03194069 -0.51048869]
 [ 1.41240208  1.77734462 -0.43820037]]

3. 整數隨機陣列

可以使用 np.random.randint() 函式來生成整數隨機陣列。例如:

import numpy as np

# 生成一個 3x3 的陣列,包含 0 到 10 之間的整數隨機數
arr = np.random.randint(0, 10, (3, 3))
print(arr)

輸出:

[[4 3 8]
 [6 5 0]
 [1 1 4]]

4. 單位矩陣

可以使用 np.eye() 函式來生成單位矩陣。例如:

import numpy as np

# 生成一個 3x3 的單位矩陣
arr = np.eye(3)
print(arr)

輸出:

[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]]

5. 未初始化的陣列

可以使用 np.empty() 函式來生成未初始化的陣列。例如:

import numpy as np

# 生成一個未初始化的 1x3 陣列
arr = np.empty(3)
print(arr)

輸出:

[1. 1. 1.]

注意:未初始化的陣列的值是未定的,可能會因為系統的記憶體狀態而異。

內容解密:

以上程式碼示範瞭如何使用 NumPy 來生成不同型別的隨機陣列和矩陣。這些函式可以用於各種應用,例如資料分析、科學計算和機器學習等。透過使用這些函式,可以快速地生成所需的資料結構,並進行進一步的處理和分析。

圖表翻譯:

此圖表示瞭如何根據不同的需求選擇合適的函式來生成隨機陣列。

NumPy 標準資料型別

NumPy 陣列包含單一型別的值,因此瞭解這些型別及其限制非常重要。由於 NumPy 是用 C 語言建構的,因此這些型別對於 C、Fortran 和其他相關語言的使用者來說將會很熟悉。

標準 NumPy 資料型別

下表列出了標準的 NumPy 資料型別。注意,當建立陣列時,可以使用字串指定型別:

np.zeros(10, dtype='int16')

或使用相關的 NumPy 物件:

np.zeros(10, dtype=np.int16)

更高階的型別指定是可能的,例如指定大端或小端數字;如需更多資訊,請參考 NumPy 檔案。NumPy 也支援複合資料型別,這將在第 12 章中涵蓋。

資料型別表

資料型別描述
bool_布林值(True 或 False),儲存為一個 byte
int_預設整數型別(與 C long 相同;通常為 int64 或 int32)
intc與 C int 相同(通常為 int32 或 int64)
intp用於索引的整數(與 C ssize_t 相同;通常為 int32 或 int64)
int8byte(-128 至 127)
int16整數(-32768 至 32767)
int32整數(-2147483648 至 2147483647)
int64整數(-9223372036854775808 至 9223372036854775807)

內容解密:

上述資料型別是 NumPy 中的基本單位,瞭解它們對於有效使用 NumPy 來說是非常重要的。每個資料型別都有其特定的範圍和限制,選擇適合的資料型別可以幫助您避免溢位和其他問題。

圖表翻譯:

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Jupyter筆記本與NumPy陣列應用

package "NumPy 陣列操作" {
    package "陣列建立" {
        component [ndarray] as arr
        component [zeros/ones] as init
        component [arange/linspace] as range
    }

    package "陣列操作" {
        component [索引切片] as slice
        component [形狀變換 reshape] as reshape
        component [堆疊 stack/concat] as stack
        component [廣播 broadcasting] as broadcast
    }

    package "數學運算" {
        component [元素運算] as element
        component [矩陣運算] as matrix
        component [統計函數] as stats
        component [線性代數] as linalg
    }
}

arr --> slice : 存取元素
arr --> reshape : 改變形狀
arr --> broadcast : 自動擴展
arr --> element : +, -, *, /
arr --> matrix : dot, matmul
arr --> stats : mean, std, sum
arr --> linalg : inv, eig, svd

note right of broadcast
  不同形狀陣列
  自動對齊運算
end note

@enduml

上述圖表展示了 NumPy 的標準資料型別及其關係。每個資料型別都有其特定的用途和限制,瞭解這些關係可以幫助您更好地使用 NumPy。

資料型別基礎

在進行資料分析和科學計算時,瞭解不同資料型別的特性和應用場景至關重要。以下是對於基本資料型別的介紹,包括無符號整數、浮點數和複數。

無符號整數

無符號整數是一種只能表示非負整數的資料型別。根據其能夠表示的最大值,無符號整數可以分為以下幾種:

  • uint8:能夠表示的範圍是0到255,佔用1個byte的空間。
  • uint16:能夠表示的範圍是0到65535,佔用2個byte的空間。
  • uint32:能夠表示的範圍是0到4294967295,佔用4個byte的空間。
  • uint64:能夠表示的範圍是0到18446744073709551615,佔用8個byte的空間。

浮點數

浮點數是一種能夠表示小數的資料型別,根據其精確度和佔用的空間大小,可以分為以下幾種:

  • float16(半精確度浮點數):由1個符號位、5個指數位和10個尾數位組成,能夠提供相對較低的精確度,但佔用的空間較小。
  • float32(單精確度浮點數):由1個符號位、8個指數位和23個尾數位組成,是最常用的浮點數型別,能夠提供一個良好的精確度和空間佔用之間的平衡。
  • float64(雙精確度浮點數):由1個符號位、11個指數位和52個尾數位組成,提供了更高的精確度,但佔用的空間也更大。

複數

複數是一種能夠表示具有實部和虛部的資料型別。在科學計算中,複數常被用來表示波動、振蕩等現象。

  • complex64:是一種複數型別,由兩個float32陣列成,分別表示實部和虛部。
  • complex128:也是是一種複數型別,由兩個float64陣列成,提供了更高的精確度。

從技術架構視角來看,Jupyter筆記本與IPython的結合,為資料分析和科學計算提供了一個互動性強、易於分享的平臺。其核心價值在於整合程式碼執行、結果顯示和檔案撰寫,大幅簡化了資料分析流程,並促進了知識分享。然而,Jupyter的效能瓶頸和對特定套件的依賴性仍需關注。尤其在處理大規模資料集時,效能最佳化策略,例如使用NumPy陣列替代Python列表,至關重要。NumPy的固定型別陣列特性,減少了記憶體開銷並提升了運算效率,是Jupyter環境下高效資料處理的關鍵。展望未來,隨著雲端計算和分散式運算的發展,Jupyter結合高效能運算框架,將進一步拓展其應用場景,並在資料科學領域扮演更重要的角色。玄貓認為,對於追求效率和可重複性的資料分析工作,深入理解和應用NumPy等高效能工具,將是釋放Jupyter筆記本全部潛力的關鍵所在。