掩码数组

理由

掩码数组是包含了丢失或无效条目的数组。 numpy.ma 模块为numpy提供了几乎类似工作的替代方案, 支持带掩码的数据矩阵。

什么是掩码数组?

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

掩码数组是标准 numpy.ndarrayopen in new window 和掩码的组合。 掩码或者是 nomaskopen in new window, 指示关联数组的任何值都是无效的,或者是布尔数组的数组,用于确定关联数组的每个元素的值是否有效。 当掩码的元素为 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 模块的主要特性是MaskedArrayopen in new window 类,它是的子类numpy.ndarrayopen in new window。 在MaskedArray类open in new window部分中更详细地描述了类、其属性和方法。

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)

有关掩码数组创建方法的完整讨论,请参阅构造掩码数组一节。

使用 numpy.ma 模块

创建掩码数组

有几种方法可以创建一个掩码数组。

访问数据

可以通过多种方式访问​​掩码数组的基础数据:

如果某些条目被标记为无效,则这些方法都不是完全令人满意的。作为一般规则,在需要不带任何掩码条目的数组表示的情况下,建议使用该filledopen in new window方法填充数组。

访问掩码

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

另一种可能性是使用getmaskopen in new windowgetmaskarrayopen in new window 函数。getmask(x)输出xif 的掩码x是掩码数组,nomaskopen in new window否则输出特殊值。getmaskarray(x) 输出xif 的掩码x是掩码数组。如果x没有无效条目或不是掩码数组,则该函数输出一个False具有尽可能多的元素的布尔数组 x

仅访问有效条目

要仅检索有效条目,我们可以使用掩码的反转作为索引。掩码的反转可以使用numpy.logical_notopen in new window函数计算,也可以 使用~运算符计算:

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

检索有效数据的另一种方法是使用该compressedopen in new window 方法,该方法返回一维ndarrayopen in new window(或其子类之一,具体取决于baseclassopen in new window 属性的值):

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

请注意,输出compressedopen in new window始终为1D。

修改掩码

掩码条目

将掩码数组的一个或多个特定条目标记为无效的推荐方法是maskedopen in new window为它们分配特殊值:

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

第二种可能性是maskopen in new window直接修改,但不鼓励这种用法。

注意

使用简单的非结构化数据类型创建新的掩码数组时,掩码最初设置为特殊valuenomaskopen in new window,该值大致对应于布尔值False。尝试设置元素 nomaskopen in new window将失败并出现TypeErroropen in new window异常,因为布尔值不支持项目分配。

通过分配掩码,可以立即掩码数组的所有条目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)

取消掩码条目

要取消掩码一个或多个特定条目,我们可以为它们分配一个或多个新的有效值:

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

注意

如果掩码数组具有 掩码,则通过直接分配取消掩码条目将无声地失败,如hardmask属性所示。引入此功能是为了防止覆盖掩码。要强制取消掩码数组具有硬掩码的条目,必须首先使用soften_maskopen in new window分配前的方法软化掩码。可以通过以下方式重新强化harden_maskopen in new window

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

要取消掩码掩码数组的所有掩码条目(假设掩码不是硬掩码),最简单的解决方案是将常量赋valuenomaskopen in new window给掩码:

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

索引和切片

作为a MaskedArrayopen in new window的子类numpy.ndarrayopen in new window,它继承了索引和切片的机制。

当访问没有命名字段的掩码数组的单个条目时,输出是标量(如果掩码的相应条目是 False)或特殊valuemaskedopen in new window(如果掩码的相应条目是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')])

访问切片时,输出是一个掩码数组,其 dataopen in new window属性是原始数据的视图,其掩码是nomaskopen in new window(如果原始数组中没有无效条目)或原始掩码的相应切片视图。视图是确保将掩模的任何修改传播到原始视图所必需的。

>>> 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,  True, False, False,  True])
>>> x.data
array([ 1, -1,  3,  4,  5])

访问具有结构化数据类型的掩码数组的字段将返回一个MaskedArrayopen in new window

掩码数组的操作

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

警告

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

numpy.ma模块附带了大多数ufunc的特定实现。 只要输入被掩码或超出有效域,具有有效域(例如logopen in new windowdivideopen in new window)的一元和二元函数 maskedopen in new window就会返回常量:

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

示例

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

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

输出的四个值是无效的:第一个值来自取负数的平方根,第二个来自除以零,以及最后两个输入被掩码的位置。

忽略极值

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

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