The numpy.ma module

Rationale

屏蔽数组是可能具有缺失或无效条目的数组。numpy.ma模块为numpy提供了几乎与工作相似的替换,支持带有掩码的数据数组。

What is a masked array?

在许多情况下,数据集可能由于无效数据的存在而不完整或受到污染。例如,传感器可能无法记录数据,或记录无效值。numpy.ma模块提供了一种方便的方法来解决这个问题,通过引入masked数组。

掩码数组是标准numpy.ndarray和掩码的组合。掩码是nomask,表示相关数组的值无效,或者为相关数组的每个元素确定该值是否有效的布尔数组。当掩码的元素为False时,关联数组的相应元素有效,称为未掩蔽。当掩码的元素True时,相关数组的相应元素称为被掩蔽(无效)。

该包确保在计算中不使用屏蔽条目。

作为例子,让我们考虑下面的数据集:

>>> import numpy as np
>>> import numpy.ma as ma
>>> x = np.array([1, 2, 3, -1, 5])

我们希望将第四个条目标记为无效。最简单的是创建一个蒙版数组:

>>> mx = ma.masked_array(x, mask=[0, 0, 0, 1, 0])

我们现在可以计算数据集的平均值,而不考虑无效数据:

>>> mx.mean()
2.75

The numpy.ma module

numpy.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的值都是无效的,我们会这样做:

>>> z = masked_values([1.0, 1.e20, 3.0, 4.0], 1.e20)

有关屏蔽数组的创建方法的完整讨论,请参见Constructing masked arrays一节。

Using numpy.ma

Constructing masked arrays

有几种方法来构造屏蔽数组。

  • 第一种可能性是直接调用MaskedArray类。

  • 第二种可能性是使用两个掩蔽的数组构造函数arraymasked_array

    array(data [,dtype,copy,order,mask,...]) 可能带有掩码值的数组类。
    masked_array MaskedArray的别名
  • 第三个选项是获取现有数组的视图。在这种情况下,如果数组没有命名字段,则视图的掩码设置为nomask,否则设置与数组相同结构的布尔数组。

    >>> x = np.array([1, 2, 3])
    >>> x.view(ma.MaskedArray)
    masked_array(data = [1 2 3],
                 mask = False,
           fill_value = 999999)
    >>> x = np.array([(1, 1.), (2, 2.)], dtype=[('a',int), ('b', float)])
    >>> x.view(ma.MaskedArray)
    masked_array(data = [(1, 1.0) (2, 2.0)],
                 mask = [(False, False) (False, False)],
           fill_value = (999999, 1e+20),
                dtype = [('a', '<i4'), ('b', '<f8')])
    
  • 另一种可能性是使用以下任何功能:

    asarray(a [,dtype,order]) 将输入转换为给定数据类型的屏蔽数组。
    asanyarray(a [,dtype]) 将输入转换为屏蔽的数组,保留子类。
    fix_invalid(a [,mask,copy,fill_value]) 返回带有无效数据的输入,并用填充值替换。
    masked_equal(x,value [,copy]) 屏蔽等于给定值的数组。
    masked_greater(x,value [,copy]) 屏蔽大于给定值的数组。
    masked_greater_equal(x,value [,copy]) 屏蔽大于或等于给定值的数组。
    masked_inside(x,v1,v2 [,copy]) 屏蔽给定间隔内的数组。
    masked_invalid(a [,copy]) 屏蔽发生无效值的数组(NaN或inf)。
    masked_less(x,value [,copy]) 屏蔽小于给定值的数组。
    masked_less_equal(x,value [,copy]) 屏蔽小于或等于给定值的数组。
    masked_not_equal(x,value [,copy]) 屏蔽数组,其中等于给定值。
    masked_object(x,value [,copy,shrink]) 屏蔽数组x,其中数据与值完全相等。
    masked_outside(x,v1,v2 [,copy]) 在给定间隔之外屏蔽数组。
    masked_values(x,value [,rtol,atol,copy,...]) 使用浮点平等的掩码。
    masked_where(condition,a [,copy]) 屏蔽满足条件的数组。

Accessing the data

可以通过几种方式访问​​屏蔽数组的基础数据:

  • 通过data属性。输出是数组作为numpy.ndarray或其子类之一的视图,这取决于在掩蔽数组创建时底层数据的类型。
  • 通过__array__方法。然后输出为numpy.ndarray
  • 通过直接将掩码的数组视为numpy.ndarray或其子类之一(实际上是使用data属性)。
  • 通过使用getdata函数。

如果一些条目被标记为无效,则这些方法都不能完全令人满意。作为一般规则,在需要数组的表示而没有任何隐藏条目的情况下,建议用filled方法填充数组。

Accessing the mask

可通过其mask属性访问掩码数组的掩码。我们必须记住,掩码中的True条目表示无效数据。

另一种可能性是使用getmaskgetmaskarray函数。getmask(x)如果x是掩码数组,则输出x的掩码,否则输出特殊值nomask如果x是屏蔽数组,则getmaskarray(x)输出x的掩码。如果x没有无效条目或不是掩码数组,则该函数输出具有与x一样多的元素的False的布尔数组。

Accessing only the valid entries

要仅检索有效条目,我们可以使用掩码的逆作为索引。可以使用numpy.logical_not函数或简单使用~运算符计算掩码的逆:

>>> 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)

另一种检索有效数据的方法是使用compressed方法,该方法返回一维ndarray(或其子类之一,取决于baseclass属性):

>>> x.compressed()
array([1, 4])

请注意,compressed的输出始终为1D。

Modifying the mask

Masking an entry

将掩码数组的一个或多个特定条目标记为无效的建议方法是为其分配特殊值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)

第二种可能性是直接修改mask,但不建议使用。

注意

当使用简单的非结构化数据类型创建新的蒙版数组时,掩码最初设置为特殊值nomask,大致对应于布尔值False尝试设置nomask的元素将失败,并显示TypeError异常,因为布尔值不支持项分配。

通过将True赋给掩码,可以立即屏蔽数组的所有条目:

>>> x = ma.array([1, 2, 3], mask=[0, 0, 1])
>>> x.mask = True
>>> x
masked_array(data = [-- -- --],
             mask = [ True  True  True],
       fill_value = 999999)

最后,可以通过向掩码分配一系列布尔值来对特定条目进行掩码和/或取消掩码:

>>> x = ma.array([1, 2, 3])
>>> x.mask = [0, 1, 0]
>>> x
masked_array(data = [1 -- 3],
             mask = [False  True False],
       fill_value = 999999)

Unmasking an entry

要取消屏蔽一个或多个特定条目,我们只需为它们分配一个或多个新的有效值:

>>> x = ma.array([1, 2, 3], mask=[0, 0, 1])
>>> x
masked_array(data = [1 2 --],
             mask = [False False  True],
       fill_value = 999999)
>>> x[-1] = 5
>>> x
masked_array(data = [1 2 5],
             mask = [False False False],
       fill_value = 999999)

注意

如果掩码的数组具有hard掩码,如通过hardmask属性所示,通过直接赋值来取消掩盖条目将静默失败。引入此功能以防止覆盖掩码。要强制取消屏蔽数组具有硬掩码的条目,必须先使用soften_mask方法软化该掩码,然后再进行分配。可以使用harden_mask重新硬化:

>>> x = ma.array([1, 2, 3], mask=[0, 0, 1], hard_mask=True)
>>> x
masked_array(data = [1 2 --],
             mask = [False False  True],
       fill_value = 999999)
>>> x[-1] = 5
>>> x
masked_array(data = [1 2 --],
             mask = [False False  True],
       fill_value = 999999)
>>> x.soften_mask()
>>> x[-1] = 5
>>> x
masked_array(data = [1 2 5],
             mask = [False False  False],
       fill_value = 999999)
>>> x.harden_mask()

要取消屏蔽掩码数组的所有掩码条目(假设掩码不是硬掩码),最简单的解决方案是将常量nomask分配给掩码:

>>> x = ma.array([1, 2, 3], mask=[0, 0, 1])
>>> x
masked_array(data = [1 2 --],
             mask = [False False  True],
       fill_value = 999999)
>>> x.mask = ma.nomask
>>> x
masked_array(data = [1 2 3],
             mask = [False False False],
       fill_value = 999999)

Indexing and slicing

由于MaskedArraynumpy.ndarray的子类,它会继承其用于索引和切片的机制。

当访问没有命名字段的被掩蔽数组的单个条目时,输出是标量(如果掩码的相应条目是False)或特殊值masked (如果掩码的相应条目为True):

>>> x = ma.array([1, 2, 3], mask=[0, 0, 1])
>>> x[0]
1
>>> x[-1]
masked_array(data = --,
             mask = True,
       fill_value = 1e+20)
>>> x[-1] is ma.masked
True

如果掩蔽的数组具有命名字段,访问单个条目将返回numpy.void对象(如果没有掩码),或者如果至少一个字段具有与初始数组相同的dtype的0d掩码数组的字段被屏蔽。

>>> y = ma.masked_array([(1,2), (3, 4)],
...                mask=[(0, 0), (0, 1)],
...               dtype=[('a', int), ('b', int)])
>>> y[0]
(1, 2)
>>> y[-1]
masked_array(data = (3, --),
             mask = (False, True),
       fill_value = (999999, 999999),
            dtype = [('a', '<i4'), ('b', '<i4')])

当访问切片时,输出是掩蔽的数组,其data属性是原始数据的视图,并且其掩码是nomask(如果没有无效条目原始数组)或原始掩码的相应切片的副本。需要复制以避免将掩模的任何修改传播到原始版本。

>>> 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  True False],
       fill_value = 999999)
>>> x.mask
array([False,  True, False, False,  True], dtype=bool)
>>> x.data
array([ 1, -1,  3,  4,  5])

访问具有结构化数据类型的掩蔽数组的字段会返回MaskedArray

Operations on masked arrays

掩码数组支持算术和比较操作。尽可能不处理被屏蔽数组的无效条目,这意味着在操作之前和之后相应的data条目应该是相同的。

警告

我们需要强调,这种行为可能不是系统的,在某些情况下,掩码数据可能会受到操作的影响,因此用户不应该依赖此数据保持不变。

numpy.ma模块自带了大多数ufuncs的特定实现。具有有效性域(例如logdivide)的一元和二进制函数在输入被屏蔽或落在外部时返回masked有效性域:

>>> ma.log([-1, 0, 1, 2])
masked_array(data = [-- -- 0.0 0.69314718056],
             mask = [ True  True False False],
       fill_value = 1e+20)

蒙版数组还支持标准numpy ufunc。输出然后是掩蔽的数组。在输入被屏蔽的地方,一元ufunc的结果被屏蔽。二进制ufunc的结果在任何输入被屏蔽的地方被屏蔽。如果ufunc也返回可选的上下文输出(包含ufunc的名称,其参数及其域的3元素元组),则处理上下文,并且在相应输入落在有效性之外的情况下掩蔽输出掩码数组的条目域:

>>> x = ma.array([-1, 1, 0, 2, 3], mask=[0, 0, 0, 0, 1])
>>> np.log(x)
masked_array(data = [-- -- 0.0 0.69314718056 --],
             mask = [ True  True False False  True],
       fill_value = 1e+20)

Examples

Data with a given value representing missing data

让我们考虑元素列表,x,其中值为-9999. 表示丢失数据。我们希望计算数据的平均值和异常的向量(与平均值的偏差):

>>> import numpy.ma as ma
>>> x = [0.,1.,-9999.,3.,4.]
>>> mx = ma.masked_values (x, -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]

Filling in the missing data

假设现在我们希望打印相同的数据,但是将缺失值替换为平均值。

>>> print mx.filled(mx.mean())
[ 0.  1.  2.  3.  4.]

Numerical operations

可以容易地执行数值运算,而不必担心缺失值,除以零,负数的平方根等。

>>> import numpy as np, 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 np.sqrt(x/y)
[1.0 -- -- 1.0 -- --]

输出的四个值无效:第一个值来自负数的平方根,第二个值除以零,最后两个输入被屏蔽。

Ignoring extreme values

让我们考虑一个数组d的0和1之间的随机浮点数。我们希望计算d的值的平均值,而忽略范围[0.1, 0.9] t2 >:

>>> print ma.masked_outside(d, 0.1, 0.9).mean()