为什么使用mask = None或mask = 0创建一个蒙版的numpy数组这么慢

时间:2016-05-26 18:03:15

标签: python performance numpy

今天我发现了一个函数,我找到了一个(至少对我而言)奇怪的瓶颈:创建一个带有mask=Nonemask=0的蒙版数组来初始化一个全零的蒙版,但形状与data非常慢:

>>> import numpy as np
>>> data = np.ones((100, 100, 100))

>>> %timeit ma_array = np.ma.array(data, mask=None, copy=False)
1 loop, best of 3: 803 ms per loop

>>> %timeit ma_array = np.ma.array(data, mask=0, copy=False)
1 loop, best of 3: 807 ms per loop

另一方面使用mask=False或手动创建遮罩要快得多:

>>> %timeit ma_array = np.ma.array(data, mask=False, copy=False)
1000 loops, best of 3: 438 µs per loop

>>> %timeit ma_array = np.ma.array(data, mask=np.zeros(data.shape, dtype=bool), copy=False)
1000 loops, best of 3: 453 µs per loop

为什么None0Falsenp.zeros(data.shape)的{​​{1}}参数慢近2000倍?鉴于function docs仅表示:

  

必须可转换为与数据形状相同的布尔数组。 True表示屏蔽(即无效)数据。

我在Windows 10上使用python 3.5,numpy 1.11.0

2 个答案:

答案 0 :(得分:4)

mask=FalseNumPy 1.11.0 source code

中是特殊的
if mask is True and mdtype == MaskType:
    mask = np.ones(_data.shape, dtype=mdtype)
elif mask is False and mdtype == MaskType:
    mask = np.zeros(_data.shape, dtype=mdtype)

mask=0mask=None采用慢速路径,制作0维掩码数组并通过np.resize调整其大小。

答案 1 :(得分:1)

我相信@ user2357112有解释。我分析了两种情况,结果如下:

In [14]: q.run('q.np.ma.array(q.data, mask=None, copy=False)')
         49 function calls in 0.161 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        3    0.000    0.000    0.000    0.000 :0(array)
        1    0.154    0.154    0.154    0.154 :0(concatenate)
        1    0.000    0.000    0.161    0.161 :0(exec)
       11    0.000    0.000    0.000    0.000 :0(getattr)
        1    0.000    0.000    0.000    0.000 :0(hasattr)
        7    0.000    0.000    0.000    0.000 :0(isinstance)
        1    0.000    0.000    0.000    0.000 :0(len)
        1    0.000    0.000    0.000    0.000 :0(ravel)
        1    0.000    0.000    0.000    0.000 :0(reduce)
        1    0.000    0.000    0.000    0.000 :0(reshape)
        1    0.000    0.000    0.000    0.000 :0(setprofile)
        5    0.000    0.000    0.000    0.000 :0(update)
        1    0.000    0.000    0.161    0.161 <string>:1(<module>)
        1    0.000    0.000    0.161    0.161 core.py:2704(__new__)
        1    0.000    0.000    0.000    0.000 core.py:2838(_update_from)
        1    0.000    0.000    0.000    0.000 core.py:2864(__array_finalize__)
        5    0.000    0.000    0.000    0.000 core.py:3264(__setattr__)
        1    0.000    0.000    0.161    0.161 core.py:6119(array)
        1    0.007    0.007    0.161    0.161 fromnumeric.py:1097(resize)
        1    0.000    0.000    0.000    0.000 fromnumeric.py:128(reshape)
        1    0.000    0.000    0.000    0.000 fromnumeric.py:1383(ravel)
        1    0.000    0.000    0.000    0.000 numeric.py:484(asanyarray)
        0    0.000             0.000          profile:0(profiler)
        1    0.000    0.000    0.161    0.161 profile:0(q.np.ma.array(q.data, mask=None, copy=False))

In [15]: q.run('q.np.ma.array(q.data, mask=False, copy=False)')
         37 function calls in 0.000 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 :0(array)
        1    0.000    0.000    0.000    0.000 :0(exec)
       11    0.000    0.000    0.000    0.000 :0(getattr)
        1    0.000    0.000    0.000    0.000 :0(hasattr)
        5    0.000    0.000    0.000    0.000 :0(isinstance)
        1    0.000    0.000    0.000    0.000 :0(setprofile)
        5    0.000    0.000    0.000    0.000 :0(update)
        1    0.000    0.000    0.000    0.000 :0(zeros)
        1    0.000    0.000    0.000    0.000 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 core.py:2704(__new__)
        1    0.000    0.000    0.000    0.000 core.py:2838(_update_from)
        1    0.000    0.000    0.000    0.000 core.py:2864(__array_finalize__)
        5    0.000    0.000    0.000    0.000 core.py:3264(__setattr__)
        1    0.000    0.000    0.000    0.000 core.py:6119(array)
        0    0.000             0.000          profile:0(profiler)
        1    0.000    0.000    0.000    0.000 profile:0(q.np.ma.array(q.data, mask=False, copy=False))

所以似乎数组的连接步骤是瓶颈。