6.12. 選取與重新排列¶
歸約運算會把陣列收合成純量或更低階的結果。選取則涵蓋了那些決定 哪些 元素得以保留以及它們 落在何處 的運算:條件式選擇、裁剪、排序、查找索引,以及沿著某個軸重新排序。
6.12.1. 條件式選擇¶
where() 會傳回一個陣列:在條件為真的位置取 x 的元素,否則取 y 的元素。這三個運算元會一起進行廣播:
a = np.array([1, 2, 3, 4, 5], dtype=np.float)
np.where(a < 3, a, 0.0)
# array([1.0, 2.0, 0.0, 0.0, 0.0])
這是實作「逐元素 if/else」而不需撰寫 Python 迴圈的合適工具。
clip() 是 maximum(lo, minimum(a, hi)) 的簡寫——將數值飽和限制在某個範圍內:
np.clip(a, 2.0, 4.0)
# array([2.0, 2.0, 3.0, 4.0, 4.0])
maximum() 與 minimum() 接受兩個運算元,並逐元素傳回較大/較小者:
np.maximum(a, 3.0)
np.minimum(a, np.array([5, 4, 3, 2, 1]))
6.12.2. 查找索引¶
nonzero() 會傳回每個非零元素的座標,並依維度各自拆分為一個索引陣列。對於二維輸入,其結果是一個包含兩個陣列的 tuple:第一個保存列索引,第二個保存欄索引。將它們逐欄配對,即可得到每個非零位置的 (row, col):
m = np.array([[0, 2, 0],
[3, 0, 0]], dtype=np.float)
np.nonzero(m)
# (array([0, 1], dtype=uint16), array([1, 0], dtype=uint16))
m 中的非零項目為 m[0, 1] = 2 和 m[1, 0] = 3。傳回的第一個陣列 [0, 1] 給出它們的列索引;第二個陣列 [1, 0] 給出它們的欄索引。並排讀取這兩個陣列即可還原出位置 (0, 1) 與 (1, 0)。
另有兩個歸約運算也會產生索引:
argsort()-- 一個整數陣列,能將輸入沿著給定的軸(預設為最後一個軸)排序:a = np.array([40, 10, 30, 20], dtype=np.uint8) idx = np.argsort(a) # array([1, 3, 2, 0], dtype=uint16) a[idx] # array([10, 20, 30, 40])
argsort一律傳回uint16;因此被排序的陣列在其排序軸上的元素個數不得超過 65,535 個。
bincount() 會計算一維 uint8 / uint16 輸入中每個非負整數出現的次數:
histogram = np.bincount(np.array([0, 1, 1, 2, 2, 2], dtype=np.uint8))
# array([1, 2, 3], dtype=uint16)
在不撰寫 Python 迴圈的情況下,可用於建立小整數像素值的直方圖。
6.12.3. 排序與重新排序¶
sort() 會沿著給定的軸(預設為最後一個軸)傳回陣列已排序的副本。若要原地排序,請直接對陣列使用 sort():
np.sort(np.array([3, 1, 2], dtype=np.float))
# array([1.0, 2.0, 3.0])
flip() 會沿著給定的軸反轉順序(未傳入 axis 時則反轉所有軸):
np.flip(np.array([1, 2, 3, 4]))
# array([4, 3, 2, 1])
roll() 會依給定的計數循環地位移元素。可用於實作環形緩衝區式的移位暫存器:
np.roll(np.array([1, 2, 3, 4]), 1)
# array([4, 1, 2, 3])
take() 是花式索引(fancy indexing)的明確形式——可挑選任意索引位置的元素:
a = np.array([10, 20, 30, 40, 50], dtype=np.uint8)
np.take(a, [0, 2, 4])
# array([10, 30, 50], dtype=uint8)
6.12.4. 過濾與結構性編輯¶
compress() 是布林索引的明確形式——傳回 a 中由布林條件選出的切片:
a = np.array([10, 20, 30, 40], dtype=np.uint8)
np.compress(a > 15, a)
# array([20, 30, 40], dtype=uint8)
delete() 會傳回一個移除了給定索引處項目的副本:
a = np.array([10, 20, 30, 40, 50], dtype=np.uint8)
np.delete(a, [1, 3])
# array([10, 30, 50], dtype=uint8)
diff() 會沿著某個軸傳回陣列的第 n 階離散前向差分。用於計算相鄰樣本之間的一階變化量:
samples = np.array([1, 3, 6, 10, 15], dtype=np.float)
np.diff(samples)
# array([2.0, 3.0, 4.0, 5.0])
6.12.5. 各項運算的成本¶
本頁中幾乎每個函式都會傳回一個全新配置的陣列。有兩個例外:
在每秒執行多次的迴圈中,請優先使用原地的 sort(),並在其他所有地方重複使用預先配置的緩衝區。布林遮罩本身在每次比較執行時都會被配置——請一次性建立遮罩並在各項運算間重複使用,而不要在每次迭代內重新建立它。