在numpy数组中查找非零之前的零数

时间:2014-04-23 15:01:00

标签: python performance numpy

我有一个numpy数组A。我想以有效的方式返回A中非零之前的零数,因为它处于循环中。

如果A = np.array([0,1,2])然后np.nonzero(A)[0][0]返回1.但是如果A = np.array([0,0,0])这不起作用(我想在这种情况下回答3)。而且如果A非常大并且第一个非零值接近开头,那么效率似乎很低。

6 个答案:

答案 0 :(得分:5)

这是一个迭代的Cython版本,如果这是一个严重的瓶颈,这可能是你最好的选择

# saved as file count_leading_zeros.pyx
import numpy as np
cimport numpy as np
cimport cython

DTYPE = np.int
ctypedef np.int_t DTYPE_t

@cython.boundscheck(False)
def count_leading_zeros(np.ndarray[DTYPE_t, ndim=1] a):
    cdef int elements = a.size
    cdef int i = 0
    cdef int count = 0
    while i < elements:
        if a[i] == 0:
            count += 1
        else:
            return count
        i += 1
    return count

这类似于@ mtrw的答案,但是以原生速度编制索引。我的Cython有点粗略,所以可能会有进一步的改进。

使用几种不同的方法快速测试IPython非常有利的案例

In [1]: import numpy as np

In [2]: import pyximport; pyximport.install()
Out[2]: (None, <pyximport.pyximport.PyxImporter at 0x53e9250>)

In [3]: import count_leading_zeros

In [4]: %paste
def count_leading_zeros_python(x):
    ctr = 0
    for k in x:
        if k == 0:
            ctr += 1
        else:
            return ctr
    return ctr
## -- End pasted text --
In [5]: a = np.zeros((10000000,), dtype=np.int)

In [6]: a[5] = 1

In [7]: 

In [7]: %timeit np.min(np.nonzero(np.hstack((a, 1))))
10 loops, best of 3: 91.1 ms per loop

In [8]: 

In [8]: %timeit np.where(a)[0][0] if np.shape(np.where(a)[0])[0] != 0  else np.shape(a)[0]
10 loops, best of 3: 107 ms per loop

In [9]: 

In [9]: %timeit count_leading_zeros_python(a)
100000 loops, best of 3: 3.87 µs per loop

In [10]: 

In [10]: %timeit count_leading_zeros.count_leading_zeros(a)
1000000 loops, best of 3: 489 ns per loop

但是,如果我有证据(带有分析器)这是一个瓶颈,我只会使用这样的东西。许多事情似乎效率低下,但永远不值得花时间去解决。

答案 1 :(得分:2)

通过在数组末尾添加非零数字,您仍然可以使用np.nonzero来获得所需的结果。

A = np.array([0,1,2])
B = np.array([0,0,0])

np.min(np.nonzero(np.hstack((A, 1))))   # --> 1
np.min(np.nonzero(np.hstack((B, 1))))   # --> 3

答案 2 :(得分:2)

i = np.argmax(A!=0)
if i==0 and np.all(A==0): i=len(A)

这应该是没有扩展的最高性能解决方案。也可以轻松地向量化以沿多个轴行动。

答案 3 :(得分:1)

天真的方法有什么问题:

def countLeadingZeros(x):
""" Count number of elements up to the first non-zero element, return that count """
    ctr = 0
    for k in x:
        if k == 0:
            ctr += 1
        else: #short circuit evaluation, we found a non-zero so return immediately
            return ctr
    return ctr #we get here in the case that x was all zeros

一旦找到非零元素,就会返回,因此在最坏的情况下它是O(n)。您可以通过将其移植到C来加快速度,但是值得测试一下,看看对于您正在使用的阵列是否真的有必要。

答案 4 :(得分:1)

我很惊讶为什么没有人使用过np.where

np.where(a)[0][0] if np.shape(np.where(a)[0])[0] != 0 else np.shape(a)[0]会做到这一点

>> a = np.array([0,1,2])
>> np.where(a)[0][0] if np.shape(np.where(a)[0])[0] != 0  else np.shape(a)[0]
... 1
>> a = np.array([0,0,0))
>> np.where(a)[0][0] if np.shape(np.where(a)[0])[0] != 0  else np.shape(a)[0]
... 3
>> a = np.array([1,2,3))
>> np.where(a)[0][0] if np.shape(np.where(a)[0])[0] != 0  else np.shape(a)[0]
... 0

答案 5 :(得分:-2)

如果你不关心速度,我有一个小技巧来完成这项工作:

a = np.array([0,0,1,1,1])
t = np.where(a==0,1,0)+np.append(np.where(a==0,0,1),0)[1:]
print t
[1 2 1 1 0]
np.where(t==2)
(array([1]),)