Для особого случая f = sum
:
In [32]: np.bincount(ind,A)
Out[32]: array([ 10., 7., 14.])
Предполагается:
f
- это ufunc - У вас достаточнопамять для создания двумерного массива формы
len(A) x len(A)
Вы можете создать двумерный массив 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.]]
Затем вы можете применить f
вдоль оси = 0, чтобы получить желаемый результат.Здесь используется третье предположение:
- Дополнительные нули в
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
Включение прекрасной идеи использования разреженногоматрица:
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 │
·───────────────────────·────────────────────·