我正在尝试找到找到二维排序数组的每一行的第一个非零值的最快方法。从技术上讲,数组中唯一的值是0和1,它是“已排序”。
例如,数组可能如下所示:
v =
0 0 0 1 1 1 1
0 0 0 1 1 1 1
0 0 0 0 1 1 1
0 0 0 0 0 0 1
0 0 0 0 0 0 1
0 0 0 0 0 0 1
0 0 0 0 0 0 0
我可以使用argmax函数
argmax(v, axis=1))
找到它从零变为1的时间,但我相信这会在每行进行详尽的搜索。我的阵列大小合适(~2000x2000)。 argmax是否仍然优于仅针对for循环中的每一行执行搜索排序方法,还是有更好的替代方案?
此外,数组总是这样,一行的第一个位置总是> = =它上面一行中第一个的第一个位置(但不保证会有一个位置)最后几行)。我可以利用for循环和每个行的“起始索引值”来利用它,它等于前一行中前一个1的位置,但我认为numpy argmax函数仍然胜过用python编写的循环
我只是对备选方案进行基准测试,但阵列的边长可能会发生很大变化(从250到10,000)。
答案 0 :(得分:4)
argmax()使用C级循环,它比Python循环要快得多,所以我想即使你在Python中编写一个智能算法,也很难击败argmax(),你可以使用Cython来加速:
@cython.boundscheck(False)
@cython.wraparound(False)
def find(int[:,:] a):
cdef int h = a.shape[0]
cdef int w = a.shape[1]
cdef int i, j
cdef int idx = 0
cdef list r = []
for i in range(h):
for j in range(idx, w):
if a[i, j] == 1:
idx = j
r.append(idx)
break
else:
r.append(-1)
return r
在我的PC上用于2000x2000矩阵,它是100us vs 3ms。
答案 1 :(得分:4)
使用np.where的速度相当快:
>>> a
array([[0, 0, 0, 1, 1, 1, 1],
[0, 0, 0, 1, 1, 1, 1],
[0, 0, 0, 0, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0]])
>>> np.where(a>0)
(array([0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 4, 5]), array([3, 4, 5, 6, 3, 4, 5, 6, 4, 5, 6, 6, 6, 6]))
使用大于0的坐标传递元组。
您还可以使用np.where测试每个子数组:
def first_true1(a):
""" return a dict of row: index with value in row > 0 """
di={}
for i in range(len(a)):
idx=np.where(a[i]>0)
try:
di[i]=idx[0][0]
except IndexError:
di[i]=None
return di
打印:
{0: 3, 1: 3, 2: 4, 3: 6, 4: 6, 5: 6, 6: None}
即,第0行:索引3> 0;第4行:索引4> 0;第6行:没有大于0的索引
正如您所怀疑的那样,argmax可能会更快:
def first_true2():
di={}
for i in range(len(a)):
idx=np.argmax(a[i])
if idx>0:
di[i]=idx
else:
di[i]=None
return di
# same dict is returned...
如果你可以处理所有naughts行没有None
的逻辑,那就更快了:
def first_true3():
di={}
for i, j in zip(*np.where(a>0)):
if i in di:
continue
else:
di[i]=j
return di
这是一个在argmax中使用轴的版本(如评论中所示):
def first_true4():
di={}
for i, ele in enumerate(np.argmax(a,axis=1)):
if ele==0 and a[i][0]==0:
di[i]=None
else:
di[i]=ele
return di
对于速度比较(在你的示例数组上),我得到了这个:
rate/sec usec/pass first_true1 first_true2 first_true3 first_true4
first_true1 23,818 41.986 -- -34.5% -63.1% -70.0%
first_true2 36,377 27.490 52.7% -- -43.6% -54.1%
first_true3 64,528 15.497 170.9% 77.4% -- -18.6%
first_true4 79,287 12.612 232.9% 118.0% 22.9% --
如果我将它扩展到2000 X 2000 np数组,我得到的是:
rate/sec usec/pass first_true3 first_true1 first_true2 first_true4
first_true3 3 354380.107 -- -0.3% -74.7% -87.8%
first_true1 3 353327.084 0.3% -- -74.6% -87.7%
first_true2 11 89754.200 294.8% 293.7% -- -51.7%
first_true4 23 43306.494 718.3% 715.9% 107.3% --