翻譯總結自:The numpy.ma module — NumPy v1.21 Manual
前言
ma是Mask的縮寫,關於Mask的解釋,如果有PS的基礎,可以理解為蒙版,如果有計算機網絡的基礎,可以理解為掩碼。Mask array是專門用於提取數組中特定元素構成的新數組的中間數組。
類比的話,如果說原數組是一塊棋盤,每個位置都寫了特定數字,那么Mask array就是和原棋盤大小相同的一塊布,只是上邊有幾個洞。那么,把這塊布蓋在棋盤上,就只能顯示出這幾個洞處的數字了,其他位置上的數字都被蒙上了,顯示的結果就是對原數組進行Mask的結果,這塊布就是Mask。
原理
Masked array(就是前言中所寫的棋盤,即原數組)中可能存在缺省值或非法值。numpy.ma
模塊提供了一個類似於numpy工作模式的Masked array。
注意區分Masked array與Mask array的差別,Masked array是原數組,Mask array是參與Mask的中間數組。
什么是Masked array?
在大多數情況下,數據集中可能包含有無效數據(比如空值、非法值)。numpy.ma
模塊通過引入Masked array來解決這個問題。
Masked array並不是單個數組,而是①標准ndarray和②Mask array的組合。一個Mask array也可以是一個nomask
,在Mask array中,要么用空值代表了非法值,要么用布爾數組中的True和False指明哪些值有效和無效。
如果Masked中某個元素是無效的,那么它對應的Mask中的這個位置的元素就是True,說明這個元素需要被“蒙”起來;反之,有效元素的位置上,Mask array中就是False,這個元素就不會被“蒙”。
這就確保了,那些Masked元素不會被用於計算。
舉個例子,假設我們有以下一個數據集:
import numpy as np import numpy.ma as ma x=np.array([1,2,3,-1,5])
如果第四個數據非法且需要Mask的,最簡單的方法就是構造一個Masked array:
mx=ma.masked_array(x,mask=[0,0,0,1,0])
然后,我們就可以排除這個無效值再對正確的數據集進行統計分析了:
mx.mean() #均值 #2.75
numpy.ma
ma模塊最主要的功能是MaskedArray
類,它是numpy.ndarray
的子類。關於這個類的屬性和方法,可以通過看MaskedArray class來獲取更多的細節。
import numpy as np import numpy.ma as ma
如果要構造一個第二個元素是非法元素的數組,我們可以這樣寫:
y=ma.array([1,2,3],mask=[0,1,0])
如果要構造一個,所有與1.e20相差不多的值視為無效值的Masked array,可以這樣寫:
z=ma.masked_values([1.0,1.e20,3.0,4.0],1.e20)
此外,還有許多構造Masked array的方法,可以看Constructing masked arrays。
使用numpy.ma
1)構造Masked array
Masked array由兩部分組成:①標准ndarray;②Mask array;兩者尺寸相同。
有多種構造Masked array的方法:
- 直接引入
MaskedArray
類; - 兩種Masked array構造器,
array
與masked_array
:array(data[,dtype,copy,order,mask,...]) 最常用,直接用一個array和與之對應的mask進行構造 masked_array 等同於numpy.ma.core.MaskedArray - 其它方法(常用的被我用紅色標注):
|
將一個array正常轉化為masked array |
|
與asarray相同 |
|
無效值(NaN和inf)會被Mask,並且無效值會被填充為fill_value |
|
等於value的值會被Mask |
|
大於value的值會被Mask |
|
大於等於value的值會被Mask |
|
給定區間內的值會被Mask |
|
無效值(NaN和inf)會被Mask |
|
小於value的值會被Mask |
|
小於等於value的值會被Mask |
|
不等於value的值會被Mask |
|
完全等同於value(常用於String)的值會被Mask |
|
給定區間外的值會被Mask |
|
浮點值相等的值會被Mask |
|
符合條件的下標/索引會被Mask |
2)訪問數據
Masked array中的data array可以通過多種方式訪問:
- 通過data屬性;輸出結果是一個
numpy.ndarray
(或子類)的視圖:m.data - 通過__array__方法;輸出結果是一個
numpy.ndarray
。 - 通過getdata函數。
通常情況下,需要先把其中無效數據通過filled
方法填充后,才能通過以上方法進行訪問。
3)訪問Mask
訪問Mask array可以用 mask
屬性:m.mask。一定要注意,Mask中的True代表着Masked array中的無效數據。
另外兩種訪問Mask的方法是通過getmask
和 getmaskarray
方法:
- getmask(x)要么返回Mask array,要么返回
nomask
。 - getmaskarray(x)要么返回Mask array,要么返回全是False的布爾數組。
4)訪問合法元素
①~mask
如果我們要提取合法元素,我們可以用~mask作為索引:
x=ma.array([[1, 2], [3, 4]], mask=[[0, 1], [1, 0]])
x[~x.mask]
masked_array(data=[1, 4], mask=[False, False], fill_value=999999)
提取結果是MaskedArray,只是data全是合法元素,mask全是False。
②compress
另一種提取合法元素的方法是用compressed
,它只返回由合法元素組成的ndarray:
x.compressed()
array([1, 4])
需要注意compressed
的輸出總是一維數組。
5)調整Mask
①Mask特定元素
a、直接標記特定元素為masked(推薦)
如果想把Masked array中的某個或某些元素手動標記為無效元素,推薦方法是直接把這個元素修改為 masked
。
#①單個元素 x = ma.array([1, 2, 3]) x[0] = ma.masked x masked_array(data=[--, 2, 3], mask=[ True, False, False], fill_value=999999) #②根據坐標篩選多個元素 y = ma.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) #同時給出橫縱坐標,兩者組合得到一個元素位置/索引 y[(0, 1, 2), (1, 2, 0)] = ma.masked y masked_array( data=[[1, --, 3], [4, 5, --], [--, 8, 9]], mask=[[False, True, False], [False, False, True], [ True, False, False]], fill_value=999999) #③用切片得到多個元素 z = ma.array([1, 2, 3, 4]) z[:-2] = ma.masked z masked_array(data=[--, --, 3, 4], mask=[ True, True, False, False], fill_value=999999)
b、修改Mask array
x = ma.array([1, 2, 3]) x.mask = [0, 1, 0]#修改mask
x masked_array(data=[1, --, 3], mask=[False, True, False], fill_value=999999)
②取消Mask特定元素
a、直接給特定元素賦值(合法值)
x = ma.array([1, 2, 3], mask=[0, 0, 1]) x[-1] = 5 x masked_array(data=[1, 2, 5], mask=[False, False, False], fill_value=999999)
這種方法對於hard mask會失效,一個array是否是hard mask,可以通過屬性hardmask查看。
可以通過soften_mask
來把hard mask變為soft mask,也可以通過harden_mask
把它變成hard mask。
b、取消全部元素的Mask
在mask不是hard mask的前提下,可以通過把mask設置為nomask
來取消全部元素的mask。
x = ma.array([1, 2, 3], mask=[0, 0, 1]) x.mask = ma.nomask x masked_array(data=[1, 2, 3], mask=[False, False, False], fill_value=999999)
6)索引和切片
由於MaskedArray是ndarray的子類,所以它也繼承了ndarray的索引和切片機制。
①索引
通過索引訪問MaskedArray單個元素時,如果MaskedArray是一維的,那么輸出結果要么是正常元素(Mask中該元素是False時),要么是一個特殊的值——masked
(Mask中該元素是True時):
x = ma.array([1, 2, 3], mask=[0, 0, 1]) x[0] #正常元素 Mask為False 1 x[-1] #特殊元素 Mask為True masked x[-1] is ma.masked True
如果MaskedArray是多維的,那么通過一個索引訪問時,會列出該維度下的所有數據(除了Mask為False的元素):
y = ma.masked_array([(1,2), (3, 4)], mask=[(0, 0), (0, 1)], dtype=[('a', int), ('b', int)]) y[0] #第一行 (1, 2) y[-1] #最后一行 (3, --)
②切片
通過切片訪問MaskedArray時,會返回一個新的MaskedArray,它的data和mask是由原MaskedArray的data和mask進行切片獲取到的:
x = ma.array([1, 2, 3, 4, 5], mask=[0, 1, 0, 0, 1]) mx = x[:3] mx masked_array(data=[1, --, 3], mask=[False, True, False], fill_value=999999) mx[1] = -1 mx masked_array(data=[1, -1, 3], mask=[False, False, False], fill_value=999999) x.mask array([False, False, False, False, True]) x.data array([ 1, -1, 3, 4, 5])
7)Masked Array相關操作與運算
Masked Array支持一系列的算術和比較運算。無效數據通常不會參與運算,這意味着運算前后data的樣式應該是一樣的(有效的還是有效,無效的還是無效)。
numpy.ma
模塊中實現了數組運算的大部分函數,有些一元或者二元函數(比如log
和divide
)在對某些值進行操作時,可能導致結果出現無效值(比如log(0)、log(-1)等):
ma.log([-1, 0, 1, 2]) masked_array(data=[--, --, 0.0, 0.6931471805599453], mask=[ True, True, False, False], fill_value=1e+20)
Masked Array也支持標准numpy函數,輸出也是Masked Array。
x = ma.array([-1, 1, 0, 2, 3], mask=[0, 0, 0, 0, 1]) np.log(x) masked_array(data=[--, 0.0, --, 0.6931471805599453, --], mask=[ True, False, True, False, True], fill_value=1e+20)
例子
1)用某個特定值代替無效值的Array
考慮一個List x,其中存放一系列元素,用-9999代替無效值。
我們希望①計算這些元素的平均值;②計算這些元素與平均值間的差值(以上計算都不包含無效值)。
import numpy.ma as ma x = [0.,1.,-9999.,3.,4.] mx=ma.masked_values(x,-9999.)#構造Masked Array,標記-9999為無效值 #計算平均值 print(mx.mean()) 2.0 #計算差值 print(mx - mx.mean()) [-2.0 -1.0 -- 1.0 2.0] print(mx.anom()) [-2.0 -1.0 -- 1.0 2.0]
2)填充無效值
還以1)為背景,假設我們想把無效值填充為有效值的平均值:
print(mx.filled(mx.mean())) [ 0. 1. 2. 3. 4.]
3)數學運算
數學運算時,會簡單地不考慮無效值、除以0、對負值開根號等情況:
import numpy.ma as ma x = ma.array([1., -1., 3., 4., 5., 6.], mask=[0,0,0,0,1,0]) y = ma.array([1., 2., 0., 4., 5., 6.], mask=[0,0,0,0,0,1]) print(ma.sqrt(x/y)) [1.0 -- -- 1.0 -- --]
如果出現以上所說情況,會把這些情況的結果視為無效值。
4)忽略極端值
假設我們有一個array d,其中的元素都是位於(0,1)間的浮點值,我們想要計算d中位於[0.2,0.9]范圍外值的平均值:
d = np.linspace(0, 1, 20) d_mean=d.mean() d_out=ma.masked_outside(d,0.2,0.9)#提取在[0.2,0.9]范圍內的值 d_out_mean=d_out.mean() d_in_mean=d_mean-d_out_mean -0.05263157894736836