假设我有两个数组
A = [ 6, 4, 5, 7, 9 ]
ind = [ 0, 0, 2, 1, 2 ]
和函数f。
我想构建一个新的数组B,其大小是ind中不同元素的数量,B [i]是f的结果,参数是由i索引的A的子数组。
对于这个例子,如果我取f = sum,那么
B = [10, 7, 14]
或f = max
B = [6, 7, 9]
在numpy中有比for循环更有效的方法吗?
由于
答案 0 :(得分:3)
针对f = sum
的特殊情况:
In [32]: np.bincount(ind,A)
Out[32]: array([ 10., 7., 14.])
假设:
f
是一个ufunc len(A) x len(A)
您可以制作2D数组B
:
B=np.zeros((len(A),max(ind)+1))
并使用B
中的值填充A
中的各个位置,以便B
的第一列仅在A
时获得ind == 0
的值, B
的第二列仅在A
等时获取ind == 1
的值:
B[zip(*enumerate(ind))]=A
你最终会得到像
这样的数组[[ 6. 0. 0.]
[ 4. 0. 0.]
[ 0. 0. 5.]
[ 0. 7. 0.]
[ 0. 0. 9.]]
然后,您可以沿轴= 0应用f
以获得所需的结果。
这里使用了第三个假设:
B
中的额外零点不会影响
期望的结果。如果你能忍受这些假设:
import numpy as np
A = np.array([ 6, 4, 5, 7, 9 ])
ind = np.array([ 0, 0, 2, 1, 2 ])
N=100
M=10
A2 = np.array([np.random.randint(M) for i in range(N)])
ind2 = np.array([np.random.randint(M) for i in range(N)])
def use_extra_axis(A,ind,f):
B=np.zeros((len(A),max(ind)+1))
B[zip(*enumerate(ind))]=A
return f(B)
def use_loop(A,ind,f):
n=max(ind)+1
B=np.empty(n)
for i in range(n):
B[i]=f(A[ind==i])
return B
def fmax(arr):
return np.max(arr,axis=0)
if __name__=='__main__':
print(use_extra_axis(A,ind,fmax))
print(use_loop(A,ind,fmax))
对于M
和N
的某些值(例如M = 10,N = 100),使用额外轴可能比使用循环更快:
% python -mtimeit -s'import test,numpy' 'test.use_extra_axis(test.A2,test.ind2,test.fmax)'
10000 loops, best of 3: 162 usec per loop
% python -mtimeit -s'import test,numpy' 'test.use_loop(test.A2,test.ind2,test.fmax)'
1000 loops, best of 3: 222 usec per loop
然而,随着N变大(比如M = 10,N = 10000),使用循环可能会更快:
% python -mtimeit -s'import test,numpy' 'test.use_extra_axis(test.A2,test.ind2,test.fmax)'
100 loops, best of 3: 13.9 msec per loop
% python -mtimeit -s'import test,numpy' 'test.use_loop(test.A2,test.ind2,test.fmax)'
100 loops, best of 3: 4.4 msec per loop
使用稀疏矩阵合并thouis's excellent idea:
def use_sparse_extra_axis(A,ind,f):
B=scipy.sparse.coo_matrix((A, (range(len(A)), ind))).toarray()
return f(B)
def use_sparse(A,ind,f):
return [f(v) for v in scipy.sparse.coo_matrix((A, (ind, range(len(A))))).tolil().data]
哪种实施最佳取决于参数N
和M
:
N=1000, M=100
·───────────────────────·────────────────────·
│ use_sparse_extra_axis │ 1.15 msec per loop │
│ use_extra_axis │ 2.79 msec per loop │
│ use_loop │ 3.47 msec per loop │
│ use_sparse │ 5.25 msec per loop │
·───────────────────────·────────────────────·
N=100000, M=10
·───────────────────────·────────────────────·
│ use_sparse_extra_axis │ 35.6 msec per loop │
│ use_loop │ 43.3 msec per loop │
│ use_sparse │ 91.5 msec per loop │
│ use_extra_axis │ 150 msec per loop │
·───────────────────────·────────────────────·
N=100000, M=50
·───────────────────────·────────────────────·
│ use_sparse │ 94.1 msec per loop │
│ use_loop │ 107 msec per loop │
│ use_sparse_extra_axis │ 170 msec per loop │
│ use_extra_axis │ 272 msec per loop │
·───────────────────────·────────────────────·
N=10000, M=50
·───────────────────────·────────────────────·
│ use_loop │ 10.9 msec per loop │
│ use_sparse │ 11.7 msec per loop │
│ use_sparse_extra_axis │ 15.1 msec per loop │
│ use_extra_axis │ 25.4 msec per loop │
·───────────────────────·────────────────────·
答案 1 :(得分:2)
我认为你不能摆脱循环,但也许使用scipy的稀疏矩阵会更有效率。
[f(v) for v in scipy.sparse.coo_matrix((A, (ind, range(len(A))))).tolil().data]
答案 2 :(得分:0)
另一种可能性,
from operator import itemgetter
from itertools import groupby
A = [ 6, 4, 5, 7, 9 ]
ind = [ 0, 0, 2, 1, 2 ]
z = zip(ind,A)
z.sort()
fst,snd = itemgetter(0), itemgetter(1)
g = groupby(z,fst)
f = sum
# or
# f = max
for i in g:
print i[0],f(snd(j) for j in i[1])
答案 3 :(得分:0)
至少在添加时,这是有效的
import numpy as np
def op_at(f, ind, vals):
base = np.zeros(np.max(ind)+1)
f.at(base, ind, vals)
return base
print op_at(np.add, [ 0, 0, 2, 1, 2], [ 6, 4, 5, 7, 9])
> [ 10. 7. 14.]
不幸的是,它似乎不适用于最大。