这个 numpy.ma 模块

理论基础

屏蔽数组是可能缺少条目或条目无效的数组。这个 numpy.ma 模块为支持带掩码的数据数组的numpy提供了一个几乎相同的替换。

什么是屏蔽数组?

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

屏蔽数组是标准的组合 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

这个 numpy.ma 模块

的主要特征 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 = ma.masked_values([1.0, 1.e20, 3.0, 4.0], 1.e20)

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

使用NUPYP.MA

构造屏蔽数组

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

  • 第一种可能是直接调用 MaskedArray 班级。

  • 第二种可能性是使用两个屏蔽数组构造函数, arraymasked_array .

    array [数据] [, dtype, copy, order, mask, ...] )

    具有可能被屏蔽的值的数组类。

    masked_array 

    的别名 numpy.ma.core.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, 1.e+20),
                dtype=[('a', '<i8'), ('b', '<f8')])
    
  • 另一种可能是使用以下任何功能:

    asarray (a) [, dtype, order] )

    将输入转换为给定数据类型的屏蔽数组。

    asanyarray (a) [, dtype] )

    将输入转换为屏蔽数组,保存子类。

    fix_invalid (a) [, mask, copy, fill_value] )

    返回包含无效数据的输入,该数据被屏蔽并替换为填充值。

    masked_equal (x,值) [, copy] )

    屏蔽一个等于给定值的数组。

    masked_greater (x,值) [, copy] )

    屏蔽大于给定值的数组。

    masked_greater_equal (x,值) [, copy] )

    屏蔽大于或等于给定值的数组。

    masked_inside \(x,v1,v2[, copy] )

    屏蔽给定间隔内的数组。

    masked_invalid (a) [, copy] )

    屏蔽出现无效值的数组(NAN或INF)。

    masked_less (x,值) [, copy] )

    屏蔽小于给定值的数组。

    masked_less_equal (x,值) [, copy] )

    屏蔽小于或等于给定值的数组。

    masked_not_equal (x,值) [, copy] )

    屏蔽一个数组,其中 not 等于给定值。

    masked_object (x,值) [, copy, shrink] )

    屏蔽数组 x 数据完全等于值。

    masked_outside \(x,v1,v2[, copy] )

    屏蔽给定间隔之外的数组。

    masked_values (x,值) [, rtol, atol, copy, ...] )

    使用浮点等同性屏蔽。

    masked_where \(条件,a[, copy] )

    屏蔽满足条件的数组。

访问数据

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

  • 通过 data 属性。输出是数组的视图, numpy.ndarray 或者它的一个子类,这取决于创建屏蔽数组时基础数据的类型。

  • 通过 __array__ 方法。然后输出为 numpy.ndarray .

  • 直接将屏蔽数组的视图作为 numpy.ndarray 或者它的一个子类(实际上是使用 data 属性为)。

  • 通过使用 getdata 功能。

如果某些条目被标记为无效,则这些方法都不完全令人满意。作为一般规则,如果需要不带任何屏蔽项的数组表示,建议使用 filled 方法。

访问掩码

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

另一种可能是使用 getmaskgetmaskarray 功能。 getmask(x) 输出的掩码 x 如果 x 是一个屏蔽数组,特殊值 nomask 否则。 getmaskarray(x) 输出的掩码 x 如果 x 是屏蔽数组。如果 x 没有无效项或不是屏蔽数组,该函数输出的布尔数组为 False 元素的数量 x .

仅访问有效条目

为了只检索有效的条目,我们可以使用掩码的倒数作为索引。可以使用 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。

修改遮罩

屏蔽一个条目

建议将屏蔽数组的一个或多个特定项标记为无效的方法是指定特殊值 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,
            dtype=int64)

最后,通过为掩码分配一系列布尔值,可以屏蔽和/或取消屏蔽特定条目:

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

取消对条目的屏蔽

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

>>> 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()
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)
>>> x.harden_mask()
masked_array(data=[1, 2, 5],
             mask=[False, False, False],
       fill_value=999999)

要取消屏蔽数组的所有屏蔽项的屏蔽(前提是屏蔽不是硬屏蔽),最简单的解决方案是分配常量 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)

索引和切片

作为一个 MaskedArray 是的子类 numpy.ndarray 它继承了其索引和切片机制。

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

>>> x = ma.array([1, 2, 3], mask=[0, 0, 1])
>>> x[0]
1
>>> x[-1]
masked
>>> x[-1] is ma.masked
True

如果屏蔽数组具有命名字段,则访问单个条目将返回 numpy.void 如果没有字段被屏蔽,则为对象;如果至少有一个字段被屏蔽,则为与初始数组具有相同数据类型的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]
(3, --)

当访问一个切片时,输出是一个屏蔽数组,其 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, False, False],
       fill_value=999999)
>>> x.mask
array([False, False, False, False,  True])
>>> x.data
array([ 1, -1,  3,  4,  5])

访问具有结构化数据类型的屏蔽数组的字段时,返回 MaskedArray .

屏蔽数组上的操作

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

警告

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

这个 numpy.ma 模块附带了大多数UFunc的特定实现。具有有效域的一元和二元函数(例如 logdivide 返回 masked 每当输入被屏蔽或不在有效域内时,常量:

>>> ma.log([-1, 0, 1, 2])
masked_array(data=[--, --, 0.0, 0.6931471805599453],
             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.6931471805599453, --],
             mask=[ True, False,  True, False,  True],
       fill_value=1e+20)

实例

具有表示缺少数据的给定值的数据

让我们考虑一个元素列表, 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]

填写缺失数据

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

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

数值运算

数字运算可以很容易地执行,而不必担心丢失的值、除以零、负数的平方根等:

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

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

忽略极值

让我们考虑一个数组 d 介于0和1之间的浮点数。我们希望计算 d 忽略范围之外的任何数据时 [0.2, 0.9] ::

>>> d = np.linspace(0, 1, 20)
>>> print(d.mean() - ma.masked_outside(d, 0.2, 0.9).mean())
-0.05263157894736836