Data Science — NumPy 資料科學程式馬拉松
D01 Numpy 陣列的定義與屬性
- arange()不同於 linspace() 在於對產生的元素可以有更多的控制。
- 參考資料:range() , np.arange() , np.linspace()的差異
各屬性的意義:
- ndarray.ndim: 陣列有多少維度
- ndarray.shape: 每個維度的大小
- ndarray.size: 陣列當中有幾個元素
- ndarray.dtype: 陣列中的資料型態
- ndarray.itemsize: 陣列中每個元素佔用的空間
- ndarray.data: 陣列所存在的記憶體位置
列表 vs. 陣列
- Python 內建的列表(List)是由獨立的元素所組成的容器,元素的型態可以不同。而 NumPy 陣列是同質性的(homogeneous)的物件,也就是每個元素的型態都必須相同。因此在底層定義與操作略有差異。
- 陣列的元素會配置在連續的記憶體位置,而列表則沒有這樣的限制。
D02 Numpy 陣列中不同的資料型態
- NumPy 提供了一個同類型元素的多維容器型態,稱為是數組或陣列。陣列的全名是 N-dimensional Array,習慣簡寫為 NdArray 或 Array。
在陣列的屬性當中,有兩個跟型態有關:
- ndarray.dtype: 陣列中的資料型態
- ndarray.itemsize: 陣列中每個元素佔用的空間
注意的點:
- type() != dtype() 一個是陣列本身的資料型態,一個是陣列中元素的資料型態
- is 是強比較,需要型態、物件、規格都一樣才會回傳True
D03 Numpy 陣列的初始化
- NumPy 提供了一個同類型元素的多維容器型態,稱為是數組或陣列。陣列的全名是 N-dimensional Array,習慣簡寫為 NdArray 或 Array。
一般來說,在 NumPy 當中建立/初始化陣列有四種方法:
- 從內建型態作轉換
- 從固定大小的初始值開始
- 從固定大小的序列值開始
- 從固定大小的亂數值開始
numpy.array(object, dtype=None, *, copy=True, order=’K’, subok=False, ndmin=0, like=None)
- object:必填,任何 array_like 物件
- dtype:指定轉成陣列後的元素型態
- copy:預設為 True,是否產生一個新的物件
- order:指定元素在記憶體中的儲存方式
從內建型態做轉換
np.array(list({0: 123, 1: 456}.items()))
# array([[ 0 123] [ 1 456]])
從固定大小的初始值開始
np.zeros((2, 3))
# 建立由 0 組成的 2x3 陣列
np.ones((2, 3))
# 建立由 1 組成的 2x3 陣列
np.full((2, 3), 9)
# 建立由 9 組成的 2x3 陣列
np.empty((2, 3))#zero vs. empty: 完全皆為零/極小的數
從固定大小的序列值開始
np.arange( 10, 30, 5 )
# array([10, 15, 20, 25])
np.linspace( 0, 2, 3 )
# array([0. 1. 2.])
np.logspace( 0, 2, 3 )
# array([1. 10. 100.])
從固定大小的亂數值開始
from numpy.random import default_rng
rng = default_rng()normal = rng.standard_normal((3,2))
random = rng.random((3,2))
integers = rng.integers(0, 10, size=(3,2))
D04 NumPy 陣列的算術運算
- +, np.add()
- -, np.substract(a, b)
- *, np.multiply(a, b)
- /, np.divide(a, b)
- **, np.power(a, b)
NumPy 陣列運算 — 四則運算
運算的時候,陣列的形狀 (shape) 必須相同,或是遵循廣播 (broadcasting) 規則,才能正確進行 element-wise 運算。規則如下:
- 兩個陣列形狀完全相同
- 比較兩個陣列的維度,如果維度的形狀相同的話,可以進行廣播
- 比較兩個陣列的維度,其中一個維度為1的話,可以進行廣播
常數與陣列運算
常數與陣列運算會符合「廣播(Broadcast)」運算的特性,廣播會將常數補齊成多維的陣列。
import numpy as np
a = np.array( [20,30,40,50] )print(a - 2)
# [18 28 38 48]
print(a / 10)
# [2. 3. 4. 5.]
不同大小的陣列運算
import numpy as npdata = np.array([[1, 2], [3, 4], [5, 6]])
ones_row = np.array([[1, 1]])print(data + ones_row)
# array([[2, 3],
# [4, 5],
# [6, 7]])
陣列運算與容器運算的差異
陣列有「對齊」跟「廣播」兩種重要的運算特性,這個其實就是我們講的向量運算特性。換句話說,在向量的運算中,是以「整組」為單位在進行運算,這是和 Python 容器最大的差異之一。
a = np.array( [20,30,40,50] )
print(a + 1)
# array([21 31 41 51]a = [20, 30, 40, 50]
b = []
for i in a:
b.append(i+1)
print(b)
# [21, 31, 41, 51]
D05 NumPy 陣列的邏輯、比較運算
陣列中的比較運算
import numpy as npa = np.array( [20,30,40,50] )
b = np.arange( 4 )print(a > b) # [ True True True True]
print(a < b) # [False False False False]
print(a == b) # [False False False False]
print(a != b) # [ True True True True]
陣列中的邏輯運算(and, or, not)
需要特別注意的是,在 NumPy 陣列中沒有邏輯運算,可以使用位元運算或是邏輯運算的函式方法取代:
import numpy as npa = np.array( [True, True, False, False] )
b = np.array( [True, False, True, False] )print(a and b)
Traceback (most recent call last):
...
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
利用布林值作為篩選的條件:遮罩
可以用一組 True/False 做為每一個位置的篩選條件,這種方法稱為遮罩(Mask):
import numpy as npa = np.array( [10, 20, 30, 40] )print(a[ [True, True, True, True] ])
# [10 20 30 40]
print(a[ [True, False, True, False] ])
# [10 30]
print(a[ [False, False, False, False] ])
# []
D06 Numpy 中常見的陣列方法與函式
numpy.fmax(), numpy.fmin()
- 以 element-wise 比較 2 個陣列並回傳各元素的最大值或最小值。與 maximum() / minimum() 不同的是,如果比較的元素中只有一個是 nan 的話,回傳非 nan 的值,如果兩個元素都是 nan 則回傳 nan。
- 同樣在進行比較時,若有需要會利用到廣播 (bradcasting)。
numpy.nanmax(), numpy.nanmin()
- 回傳陣列中有非 nan 元素值的最大值或最小值。
- 可以指定要比較的軸(axis),以及回傳值是否要保留維度。
百分位數:percentile(), nanpercentile()
- 計算百分位數,percentile() 與 nanpercentile() 不同的地方在於後者會忽略 nan。
- 欲取得的百分位數引數,可以傳入純量或是陣列的值 (介於0–100 之間),也可以指定要比較的軸,以及回傳值是否要保留維度。
分位數:quantile(), nanquantile()
- 計算分位數,quantile() 與 nan quantile() 不同的地方在於後者會忽略 nan。如果元素中包含 nan 的話,則 quantile() 會回傳 nan。
- 欲取得的分位數引數,可以傳入純量或是陣列的值 (介於0–1 之間),也可以指定要比較的軸,以及回傳值是否要保留維度。
平均值:mean(), nanmean()
- mean() 和 nanmean() 不同的地方在於後者會忽略 nan。如果元素中包含 nan 的話,則 mean() 會回傳 nan。下面的例子使用 np.isnan() 判斷陣列中是否包含 nan,如果無 nan 的話就呼叫 mean() 計算平均值,反之則呼叫 nanmean() 進行計算。
- 可以指定要計算平均數的軸,以及回傳值是否要保留維度。dtype 引數是計算使用的型別,若輸入陣列是整數的話,則會用 float64 型別計算,若輸入的是浮點數的話,則是依輸入陣列的型別做為 dtype。
平均值:average()
- 使用 average() 計算平均值的話,可以輸入權重值做為引數。
- 須注意權重的總和不能為 0,否則會產生錯誤。
計算中位數:median(), nanmedian()
- median() 和 nanmedian() 不同的地方在於後者會忽略 nan。如果元素中包含 nan 的話,則 median() 會回傳 nan。
- 可以指定要計算中位數的軸,以及回傳值是否要保留維度。要留意的是,如果軸或是陣列總數不是單數的話,中位數的值會是中間 2 個元素值相加除以 2。
計算標準差:std(), nanstd()
- std() 和 nanstd() 不同的地方在於後者會忽略 nan。如果元素中包含 nan 的話,則 std() 會回傳 nan。
- 可以指定要計算標準差的軸,以及回傳值是否要保留維度。若是對於精度可能造成的誤差影響,可以改變 dtype 提高精度。
- 如果要計算樣本標準差的話,可將 ddof (自由度) 引數傳入 1,在計算平均方差 (mean squared deviation) 時分母就會以 N — ddof 做計算
計算變異數:var(), nanvar()
- var() 和 nanvar() 不同的地方在於後者會忽略 nan。如果元素中包含 nan 的話,則 var() 會回傳 nan。
- 可以指定要計算變異數的軸,以及回傳值是否要保留維度。若是對於精度可能造成的誤差影響,可以改變 dtype 提高精度。
- 如果要計算樣本變異數的話,可將 ddof (自由度) 引數傳入 1,在計算平均方差 (mean squared deviation) 時分母就會以 N — ddof 做計算。
相關係數:corrcoef()
- corrcoef() 計算 Pearson 積差相關係數。引數 rowvar 預設值為 True,代表將每一個 row 當做是一筆變數。
np.sort(array)
array.sort()
numpy.sort(a, axis=-1, kind=None, …)
- a:必填,任何需要被排序的 array_like 物件
- axis:預設 = -1,指的是排序的方向
- kind:有四種排序算法可以選(quicksort, mergesort, heapsort, stable),預設為 quicksort 演算法
ndarray.sort(axis=-1, kind=None, …)
- axis:預設 = -1,指的是排序的方向
- kind:有四種排序算法可以選(quicksort, mergesort, heapsort, stable),預設為 quicksort 演算法
# 搜尋與排序方法np.searchsorted([1,2,3,4,5], 3)
# 2
- 與 np.sort() 不同的是,陣列物件.sort() 的語法會進行 in-place 排序,也就是原本的陣列內容會跟著改變。
- 多維陣列在排序時可以指定要依據的軸。
- 排序支援多種不同的排序算法,包括 quicksort (預設)、heapsort、mergesort、timesort,在 kind 引數指定即可。依照官網文件指出排序速度是以 quicksort 最快,mergesort / timesort 其次,之後是 heapsort。
# 攤平
import numpy as npa = np.arange(6).reshape((3, 2))print(a.ravel() )
print(a.flatten())
print(a.flat)
- 透過 flatten() 與 ravel() 均可將多維陣列轉形為一維陣列,flatten() 與 ravel() 的使用透過下列兩種方法,得到的結果都是完全一樣的。
- 不同的是,ravel() 建立的是原來陣列的 view,所以在 ravel() 回傳物件中做的元素值變更,將會影響原陣列的元素值。
- 預設展開的順序是 C-style,展開時是以 row 為主的順序展開。
NumPy 陣列重塑 — reshape()
- 呼叫 reshape() 時指定新的形狀 (shape),可將陣列重塑為該形狀,但如果新的總數與原先 shape 總數不一致的話,則會產生錯誤。
- Reshape 時,新的形狀可以採用模糊指定為 -1,讓 NumPy 自動計算,例如:a.reshape((5, -1))。
- 若 reshape 後的陣列元素值改變,會影響原陣列對應的元素值也跟著改變。
NumPy 陣列重塑 — resize()
- 與 reshape() 不同的地方在於,如果 resize 的大小超過總元素值,則會在後面的元素值的指定為 0。
- 如果 resize 的大小小於總元素值,則會依照 C-style 的順序,取得 resize 後的陣列元素。
NumPy 陣列的合併 — concatenate()
使用 concatenate() 進行陣列的合併時,須留意除了指定的軸之外 (預設為 axis 0),其他軸的形狀必須完全相同,合併才不會發生錯誤。
NumPy 陣列的合併 — stack(), hstack(), vstack()
- stack(), hstack(), vstack() 的觀念及用法類似,不同點在於 stack() 回傳的陣列維度會是合併前的維度 +1,而 hstack() 與 vstack() 回傳的陣列維度則是依合併的陣列而定。
- 至於是否可以合併,stack() 必須要所有陣列的形狀都一樣;而 hstack() 與 vstack() 則跟上述的規則一樣,除了指定的軸之外,其他軸的形狀必須完全相同才可以合併。
補充資料:stack(),hstack(),vstack()差異說明
NumPy 陣列的分割 — split()、hsplit()、vsplit()
- 呼叫 split() 時 indices_or_sections 引數如果給定單一整數的話,那就會按照軸把陣列等分;如果給定一個 List 的整數值的話,就會按照區段去分割。
- hsplit() 與 vsplit(),分別是依照水平軸和垂直軸去做分割。
D07 NumPy 陣列的索引、切片和迭代
import numpy as npdata = np.array([1, 2, 3])print(data[0]) # 取出第 0 個
print(data[1]) # 取出第 1 個
print(data[0:2]) # 第 0 - 1 個
print(data[1:]) # 第 1 到最後一個
print(data[-2:]) # 倒數第二到最後一個
- 攤平後再迭代多維陣列
NumPy陣列的索引和切片 (Slicing)
- 透過索引存取陣列元素或進行切片 (slicing),可以使用索引值,或是 [start:stop:step] 語法取得範圍內的元素,要留意的是起始-結束範圍仍是 half-open 的,所以回傳的元素將不包含結束索引的元素。
- 索引 -1 表示取得最後一個元素。切片如果只有給定 step 值為 -1 的話,則代表是反向取出,元素值是從最後一筆開始取出。
- 若沒有給定 start 或 stop 值的話則代表是取出該索引之前或之後的所有元素。若 start 和 stop 值都沒有給定的話,就是取出所有元素值。