我有一个numpy数组:
import numpy as np
arr = np.random.rand(100)
如果我想找到它的最大值,我会在我的机器上运行np.amax
,它每秒运行155357次。
然而,由于某些原因,我不得不掩盖它的一些价值观。例如,让我们只屏蔽一个单元格:
import numpy.ma as ma
arr = ma.masked_array(arr, mask=[0]*99 + [1])
现在,找到最大值要慢得多,每秒运行26574次。
这只是无掩码数组上此操作速度的17%。
例如,其他操作是subtract
、add
和multiply
。尽管在屏蔽阵列上,它们对所有值进行操作,但与无屏蔽阵列(15343/497663(相比,其速度仅为4%
我正在寻找一种更快的方法来操作像这样的掩码数组,无论它是否使用numpy。
(我需要在真实数据上运行这个,这是具有多维和数百万个单元的阵列(
MaskedArray
是基numpyndarray
的一个子类。它没有自己编译的代码。查看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
文件说,这个包最早是在数字时代之前开发的,并在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