为什么 Numpy 掩码数组有用?

Why are Numpy masked arrays useful?

我一直在阅读掩码数组文档,但我很困惑 - MaskedArray 与仅维护一个值数组和一个布尔掩码有什么不同?有人能给我一个 MaskedArrays 更方便或性能更高的例子吗?

更新 6/5

为了更具体地说明我的问题,这里是如何使用 MaskedArray 的经典示例:

>>>data = np.arange(12).reshape(3, 4)
>>>mask = np.array([[0., 0., 1., 0.],
                    [0., 0., 0., 1.],
                    [0., 1., 0., 0.]])

>>>masked = np.ma.array(data, mask=mask)
>>>masked

masked_array(
  data=[[0, 1, --, 3],
        [4, 5, 6, --],
        [8, --, 10, 11]],
  mask=[[False, False,  True, False],
        [False, False, False,  True],
        [False,  True, False, False]],
  fill_value=999999)

>>>masked.sum(axis=0)

masked_array(data=[12, 6, 16, 14], mask=[False, False, False, False], fill_value=999999)

我也可以这样轻松地做同样的事情:

>>>data = np.arange(12).reshape(3, 4).astype(float)
>>>mask = np.array([[0., 0., 1., 0.],
                    [0., 0., 0., 1.],
                    [0., 1., 0., 0.]]).astype(bool)

>>>masked = data.copy()  # this keeps the original data reuseable, as would
                         # the MaskedArray. If we only need to perform one 
                         # operation then we could avoid the copy
>>>masked[mask] = np.nan
>>>np.nansum(masked, axis=0)

array([12.,  6., 16., 14.])

我想 MaskedArray 版本看起来更好一些,如果您需要可重复使用的数组,则可以避免复制。从标准 ndarray 转换为 MaskedArray 时,它不会使用同样多的内存吗?在将掩码应用于数据时,它是否避免了引擎盖下的副本?还有其他优势吗?

官方回答被举报here:

In theory, IEEE nan was specifically designed to address the problem of missing values, but the reality is that different platforms behave differently, making life more difficult. On some platforms, the presence of nan slows calculations 10-100 times. For integer data, no nan value exists.

事实上,与类似的 nans 数组相比,掩码数组可能非常慢:

import numpy as np
g = np.random.random((5000,5000))
indx = np.random.randint(0,4999,(500,2))
g_nan = g.copy()
g_nan[indx] = np.nan
mask =  np.full((5000,5000),False,dtype=bool)
mask[indx] = True
g_mask = np.ma.array(g,mask=mask)

%timeit (g_mask + g_mask)**2
# 1.27 s ± 35.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit (g_nan + g_nan)**2
# 76.5 ms ± 715 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

它们什么时候有用?

在多年的编程中,我发现它们在以下场合很有用:

  • 当您想保留屏蔽的值以供以后处理而不复制数组时。
  • 您不想被 nan 操作的奇怪行为所欺骗(you might be tricked by the behaviour of masked array 顺便说一下)。
  • 如果掩码是数组的一部分,当您必须使用它们的掩码处理许多数组时,您可以避免代码和混淆。
  • 与 nan 值相比,您可以为掩码值分配不同的含义。例如,我使用 np.nan 作为缺失值,但我也屏蔽了 SNR 较差的值,因此我可以识别两者。

一般来说,您可以将掩码数组视为更紧凑的表示形式。最好的方法是逐个测试更易于理解和有效的解决方案。