问题描述
|
假设我有两个数组
A = [ 6,4,5,7,9 ]
ind = [ 0,2,1,2 ]
和一个功能f。
我想建立一个新数组B,其大小为ind中与B [i]中in的不同元素的数量,f的结果为参数i由i索引的A子数组。
对于此示例,如果我取f =和,则
B = [10,14]
或f =最大值
B = [6,9]
有没有比numpy中的for循环更有效的方法?
谢谢
解决方法
对于
f = sum
的特殊情况:
In [32]: np.bincount(ind,A)
Out[32]: array([ 10.,7.,14.])
假设:
f
是ufunc
您有足够的内存来制作2D
形状为len(A) x len(A)
的数组
您可以制作2D数组B
:
B=np.zeros((len(A),max(ind)+1))
并在B
的各个位置填充A
的值,以使B
的第一列仅在ind == 0
时获得ѭ10gets的值,而B
的第二列仅在ind == 1
时获得A
的值,等等:
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,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,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,fmax))
print(use_loop(A,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.fmax)\'
1000 loops,best of 3: 222 usec per loop
但是,随着N变大(例如M = 10,N = 10000),使用循环可能会更快:
% python -mtimeit -s\'import test,test.fmax)\'
100 loops,best of 3: 13.9 msec per loop
% python -mtimeit -s\'import test,best of 3: 4.4 msec per loop
结合thouis使用稀疏矩阵的出色思想:
def use_sparse_extra_axis(A,f):
B=scipy.sparse.coo_matrix((A,(range(len(A)),ind))).toarray()
return f(B)
def use_sparse(A,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 │
·───────────────────────·────────────────────·
,我认为您无法摆脱循环,但是也许使用scipy的稀疏矩阵会更有效。
[f(v) for v in scipy.sparse.coo_matrix((A,range(len(A))))).tolil().data]
,另一种可能性
from operator import itemgetter
from itertools import groupby
A = [ 6,9 ]
ind = [ 0,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])
,至少对于添加而言,这有效
import numpy as np
def op_at(f,vals):
base = np.zeros(np.max(ind)+1)
f.at(base,vals)
return base
print op_at(np.add,[ 0,2],[ 6,9])
> [ 10. 7. 14.]
不幸的是,它似乎无法正常工作。