Python 为什么np.compress比布尔索引更快?

Python 为什么np.compress比布尔索引更快?,python,arrays,numpy,Python,Arrays,Numpy,np.compress在内部做了什么,使得它比布尔索引更快 在本例中,compress快了约20%,但节省的时间取决于a的大小和布尔数组b中True值的数量,但在我的机器上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.compr

np.compress
在内部做了什么,使得它比布尔索引更快

在本例中,
compress
快了约20%,但节省的时间取决于
a
的大小和布尔数组
b
True
值的数量,但在我的机器上
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
警察说

返回的是数据的副本,而不是使用切片获得的视图

与此相反,美国政府表示

沿给定轴返回阵列的选定切片”

但是,使用来确定两个数组是否共享相同的数据缓冲区表明,两个方法都没有与其父
a
共享数据,我认为这意味着两个方法都没有返回实际的切片

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。

当要沿一个轴选择的索引由布尔掩码向量指定时,函数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
i=np.random.random\u样本(n)<.5
b1=a[i]
b2=np.压缩(i,a,轴=0)
%timeit a[i]
10个回路,最佳3个:59.8 ms/回路
%timeit np.compress(i,a,轴=0)
10个回路,最佳3个:每个回路24.1毫秒

跟踪
a.通过
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
模式


[]索引被转换为一个
\uuuu getitem\uuuuu
调用,并通过不同的层进行。我还没有追踪到代码的差异很大,但我认为可以肯定地说
压缩
(或者更确切地说
获取
)只需更直接地执行任务,从而获得适度的速度提升。30-50%的速度差异表明编译代码细节存在差异,而不是像查看<代码>与复制<代码>或解释与编译那样的主要差异。

您还可以使用
np。where
将布尔值转换为索引数组,以及
np.使用
选择具有该属性的行。所有操作都会生成所选值的副本。时间可能更多地与调用开销和通用性有关。”因此,这将导致性能损失“-这应该是一个与数组大小不成比例的常数惩罚,但为什么numpy不能从布尔索引语法推断轴选择,就像它可以从轴参数推断到压缩一样?”?