内部np.compress
做什么使得它比布尔索引更快?
在此示例中,compress
的速度提高约20%,但节省的时间因a
的大小和布尔数组True
中的b
值的数量而异},但在我的机器上compress
总是更快。
import numpy as np
a = np.random.rand(1000000,4)
b = (a[:,0]>0.5)
%timeit a[b]
#>>> 10 loops, best of 3: 24.7 ms per loop
%timeit a.compress(b, axis=0)
#>>> 10 loops, best of 3: 20 ms per loop
documentation for boolean indexing说
返回的是数据的副本,而不是用切片获取的视图
相反,compress docs说
沿给定的轴返回选定的阵列切片"。
但是,使用method provided here来确定两个数组是否共享同一个数据缓冲区,这表明两个方法都不与其父a
共享数据,我认为这两个方法都不返回实际的切片。 / p>
def get_data_base(arr):
base = arr
while isinstance(base.base, np.ndarray):
base = base.base
return base
def arrays_share_data(x, y):
return get_data_base(x) is get_data_base(y)
arrays_share_data(a, a.compress(b, axis=0))
#>>> False
arrays_share_data(a, a[b])
#>>> False
我只是好奇,因为我经常在工作中执行这些操作。我运行通过Anaconda安装的python 3.5.2,numpy v 1.11.1。
答案 0 :(得分:1)
当沿一个轴选择的索引由布尔掩码的向量指定时,函数compress是花式索引的替代方法,
明显的速度增益是由于轴选择是预先指定的,而花式索引可用于对阵列进行任意选择,因此会导致性能损失。
这也是您所经历的变速增益的原因。
i = np.random.random_sample(n) < .5
b1 = a[i]
b2 = np.compress(i, a, axis=0)
%timeit a[i]
10 loops, best of 3: 59.8 ms per loop
%timeit np.compress(i, a, axis=0)
10 loops, best of 3: 24.1 ms per loop
答案 1 :(得分:1)
通过a.compress
numpy
上的多层函数调用跟踪github
我到达
/numpy/core/src/multiarray/item_selection.c
PyArray_Compress(PyArrayObject *self, PyObject *condition, int axis,
PyArrayObject *out)
# various checks
res = PyArray_Nonzero(cond);
ret = PyArray_TakeFrom(self, PyTuple_GET_ITEM(res, 0), axis,
out, NPY_RAISE);
使用示例数组,compress
与执行where
获取索引数组相同,然后take
:
In [135]: a.shape
Out[135]: (1000000, 4)
In [136]: b.shape
Out[136]: (1000000,)
In [137]: a.compress(b, axis=0).shape
Out[137]: (499780, 4)
In [138]: a.take(np.nonzero(b)[0], axis=0).shape
Out[138]: (499780, 4)
In [139]: timeit a.compress(b, axis=0).shape
100 loops, best of 3: 14.3 ms per loop
In [140]: timeit a.take(np.nonzero(b)[0], axis=0).shape
100 loops, best of 3: 14.3 ms per loop
事实上,如果我在[]索引中使用这个索引数组,我会得到相似的时间:
In [141]: idx=np.where(b)[0]
In [142]: idx.shape
Out[142]: (499780,)
In [143]: timeit a[idx,:].shape
100 loops, best of 3: 14.6 ms per loop
In [144]: timeit np.take(a,idx, axis=0).shape
100 loops, best of 3: 9.9 ms per loop
np.take
代码涉及更多,因为它包含clip
和wrap
模式。
[]索引转换为__getitem__
调用,并通过各种层。我没有追溯到代码变化很大,但我认为compress
(或更确切地说take
)只是采取更直接的方式来完成任务是安全的,因此获得适度的速度提升。速度差异为30-50%表明编译代码详细信息存在差异,而不是像views
vs copies
这样的主要内容,或者是针对编译的解释。