2015-10-25 14 views
5

Sono interessato a calcolare un grande array NumPy. Ho un grande array A che contiene un sacco di numeri. Voglio calcolare la somma di diverse combinazioni di questi numeri. La struttura dei dati è la seguente:Vectorize grande moltiplicazione NumPy

A = np.random.uniform(0,1, (3743, 1388, 3)) 
Combinations = np.random.randint(0,3, (306,3)) 
Final_Product = np.array([ np.sum(A*cb, axis=2) for cb in Combinations]) 

mia domanda è se c'è un modo efficiente più elegante e di memoria per calcolare questo? Trovo frustrante lavorare con np.dot() quando è coinvolto un array 3D.

Se è utile, la forma di Final_Product dovrebbe essere idealmente (3743, 306, 1388). Attualmente lo Final_Product ha la forma (306, 3743, 1388), quindi posso solo rimodellare per arrivarci.

risposta

5

np.dot() non darà l'output desiderato, a meno che non implichino passaggi aggiuntivi che probabilmente includeranno reshaping. Ecco un vectorized approccio usando np.einsum farlo un solo colpo, senza alcun sovraccarico di memoria in più -

Final_Product = np.einsum('ijk,lk->lij',A,Combinations) 

Per completezza, ecco con np.dot e reshaping come discusso in precedenza -

M,N,R = A.shape 
Final_Product = A.reshape(-1,R).dot(Combinations.T).T.reshape(-1,M,N) 

test Runtime e verificare di uscita -

In [138]: # Inputs (smaller version of those listed in question) 
    ...: A = np.random.uniform(0,1, (374, 138, 3)) 
    ...: Combinations = np.random.randint(0,3, (30,3)) 
    ...: 

In [139]: %timeit np.array([ np.sum(A*cb, axis=2) for cb in Combinations]) 
1 loops, best of 3: 324 ms per loop 

In [140]: %timeit np.einsum('ijk,lk->lij',A,Combinations) 
10 loops, best of 3: 32 ms per loop 

In [141]: M,N,R = A.shape 

In [142]: %timeit A.reshape(-1,R).dot(Combinations.T).T.reshape(-1,M,N) 
100 loops, best of 3: 15.6 ms per loop 

In [143]: Final_Product =np.array([np.sum(A*cb, axis=2) for cb in Combinations]) 
    ...: Final_Product2 = np.einsum('ijk,lk->lij',A,Combinations) 
    ...: M,N,R = A.shape 
    ...: Final_Product3 = A.reshape(-1,R).dot(Combinations.T).T.reshape(-1,M,N) 
    ...: 

In [144]: print np.allclose(Final_Product,Final_Product2) 
True 

In [145]: print np.allclose(Final_Product,Final_Product3) 
True 
+0

Grazie! Ho anche trovato la risposta di @ajcr come molto utile. Usando il tensore ho dimezzato il tempo utilizzato in 'np.einsum' – Julien

+0

@ Julien mi piace anche la soluzione di ajcr! Penso che sia una versione succinta di ciò che 'punto' fa qui. – Divakar

5

Inst Per il dot è possibile utilizzare tensordot. Vostro attuale metodo è equivalente a:

np.tensordot(A, Combinations, [2, 1]).transpose(2, 0, 1) 

Nota la transpose al fine di mettere gli assi nell'ordine corretto.

Come dot, la funzione tensordot può chiamare alle veloci librerie BLAS/LAPACK (se sono state installate) e quindi dovrebbe funzionare bene per array di grandi dimensioni.

+0

Breve e semplice, mi piace! – Divakar

+0

@Divakar: grazie! Comunque preferisco ancora 'einsum' :-) –

+0

Anch'io !! Ho avuto questa sensazione quando 'einsum' mette fuori un array 3D, non è efficiente come ridurre in un array' 2D' o meglio uno 'scalare'. – Divakar