NumPy - 遮罩数组上更快的操作?

3

我有一个numpy数组:

import numpy as np
arr = np.random.rand(100)

如果我想找到它的最大值,我运行np.amax,在我的计算机上每秒运行155,357次。
然而,由于某些原因,我必须屏蔽一些数值。例如,让我们只屏蔽一个单元格:
import numpy.ma as ma
arr = ma.masked_array(arr, mask=[0]*99 + [1])

现在,寻找最大值的速度要慢得多,每秒运行 26,574 次。
这仅相当于未屏蔽数组上该操作速度的 17%
其他操作,例如 subtractaddmultiply。虽然在屏蔽数组上它们作用于所有值,但它们只有未屏蔽数组的 4% 的速度(15,343/497,663)。
我正在寻找一种更快的方式来处理像这样的屏蔽数组,无论是使用 numpy 还是其他库。
(我需要在实际数据上运行,这些数据具有多个维度和数百万个单元格)
1个回答

6

MaskedArray 是基础numpy ndarray 的子类。它本身没有编译的代码。请查看numpy/ma/目录或主文件以获取详细信息:

/usr/local/lib/python3.6/dist-packages/numpy/ma/core.py

一个掩码数组有两个关键属性,data 和 mask。其中一个是你用来创建它的数据数组,另一个是同样大小的布尔数组。
因此,所有操作都必须考虑这两个数组。它不仅要计算新的 data,还要计算新的 mask。
它可以采取多种方法(取决于操作):
- 直接使用 data。 - 使用压缩过的 data,即删除了掩盖值的新数组。 - 使用填充过的 data,其中掩盖值被替换为 fillvalue 或一些无害的值(例如,在加法时为 0,在乘法时为 1)。
掩码值的数量(0 或全部)在速度上没有或者很少有影响。
因此,你看到的速度差异并不奇怪。有很多额外的计算在进行。ma.core.py 文件中说,这个包最初是在 numpy 出现之前开发的,并在 2005 年左右纳入了 numpy。虽然一直有更新以保持更新,但我认为它没有得到重大改进。
以下是 np.ma.max 方法的代码:
def max(self, axis=None, out=None, fill_value=None, keepdims=np._NoValue):

    kwargs = {} if keepdims is np._NoValue else {'keepdims': keepdims}

    _mask = self._mask
    newmask = _check_mask_axis(_mask, axis, **kwargs)
    if fill_value is None:
        fill_value = maximum_fill_value(self)
    # No explicit output
    if out is None:
        result = self.filled(fill_value).max(
            axis=axis, out=out, **kwargs).view(type(self))
        if result.ndim:
            # Set the mask
            result.__setmask__(newmask)
            # Get rid of Infs
            if newmask.ndim:
                np.copyto(result, result.fill_value, where=newmask)
        elif newmask:
            result = masked
        return result
    # Explicit output
    ....

关键步骤如下:
fill_value = maximum_fill_value(self)  # depends on dtype
self.filled(fill_value).max(
            axis=axis, out=out, **kwargs).view(type(self))

您可以尝试使用“filled”来查看数组中发生了什么。
In [40]: arr = np.arange(10.)                                                                                        
In [41]: arr                                                                                                         
Out[41]: array([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
In [42]: Marr = np.ma.masked_array(arr, mask=[0]*9 + [1])                                                            
In [43]: Marr                                                                                                        
Out[43]: 
masked_array(data=[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, --],
             mask=[False, False, False, False, False, False, False, False,
                   False,  True],
       fill_value=1e+20)
In [44]: np.ma.maximum_fill_value(Marr)                                                                              
Out[44]: -inf
In [45]: Marr.filled()                                                                                               
Out[45]: 
array([0.e+00, 1.e+00, 2.e+00, 3.e+00, 4.e+00, 5.e+00, 6.e+00, 7.e+00,
       8.e+00, 1.e+20])
In [46]: Marr.filled(_44)                                                                                            
Out[46]: array([  0.,   1.,   2.,   3.,   4.,   5.,   6.,   7.,   8., -inf])
In [47]: arr.max()                                                                                                   
Out[47]: 9.0
In [48]: Marr.max()                                                                                                  
Out[48]: 8.0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接