Python:numpy.ma模块
翻译总结自: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来获取更多的细节。
简单来说,numpy.ma
可以看做是numpy
的补充:
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