2015-06-08 9 views
5

Ho scritto un programma e lo ho profilato. Le strozzature sono i seguenti (se uso una matrice sparsa):Il modo più veloce per accedere e inserire valori nella matrice

26534 0.775 0.000 66.657 0.003 compressed.py:638(__setitem__) 
    26534 2.240 0.000 59.438 0.002 compressed.py:688(_set_many) 
    13318 2.993 0.000 50.024 0.004 compressed.py:742(_insert_many) 
3034231 23.087 0.000 38.101 0.000 defmatrix.py:312(__getitem__) 

Se uso una matrice densa, allora queste operazioni sono lenti (deve init matrice di zeri)

3034072 24.902 0.000 41.135 0.000 defmatrix.py:312(__getitem__) 
    11780 19.586 0.002 19.586 0.002 {numpy.core.multiarray.zeros} 

La scarsa la versione matrix è più veloce (193s contro 178s). Ma recuperare e mettere in fila è chiaramente un collo di bottiglia per me. Ho provato a utilizzare la funzione take, dove uso range() per creare un array contenente gli indici della riga. Tuttavia questo è molto peggio (di un fattore di 10000) rispetto a quello che sto facendo attualmente, che è per la matrice X, X[idx,:] per la messa e X.getrow(idx).todense() da prendere.

C'è un modo migliore per accedere e sostituire queste righe? Le mie matrici sono piuttosto grandi (~ 100000 righe 20-500 colonne).

Modifica: Sto usando csr_matrix (ma aperto a qualsiasi tipo di matrice sparsa - questo sembrava adatto per afferrare le righe). Di seguito una serie di test solo per dare un MWE. Le velocità sono circa 3E-4s, 7E-3s, .1s. Questo è sorprendente per me e mi chiedo se c'è un modo più veloce per farlo rispetto all'approccio principale. Se rimuovo la chiamata todense() il tempo sparse viene tagliato a metà - ma questo sembra ancora piuttosto lento.

import numpy as np 
from time import time 
from scipy.sparse import csr_matrix 

def testFancy(mat,idxs): 
    for i in idxs: 
     x = mat[i,:] 

def testTake(mat,idxs): 
    for i in idxs: 
     x = mat.take(range(i*50,i*50+50)) 

def testSparse(mat,idxs): 
    for i in idxs: 
     x = mat.getrow(i).todense() 

mat = np.random.rand(50000,50) 
idxs = np.random.randint(50000-1, size=1000) 

#fancy 
t0 = time() 
testFancy(mat,idxs) 
t1 = time() 
print str(t1-t0) 

#take 
t0 = time() 
testTake(mat,idxs) 
t1 = time() 
print str(t1-t0) 

#sparse 
mat = csr_matrix((50000,50)) 
t0 = time() 
testSparse(mat,idxs) 
t1 = time() 
print str(t1-t0) 
+2

Un [mcve] (http://stackoverflow.com/help/mcve) sarebbe d'aiuto. – wwii

+1

Potresti fornire un esempio di lavoro minimo che potremmo copiare e incollare? È difficile dire qualcosa altrimenti. – rth

+1

Si noti inoltre che esistono diverse implementazioni di una matrice sparsa. Dal termine 'sparse matrix' solo possiamo solo intuire la struttura dati sottostante. – cel

risposta

1

Basta usare indicizzazione fantasia, sia per ottenere e impostare righe in un array,

import numpy as np 
from scipy.sparse import csr_matrix 

mat_ds = np.random.rand(50000,50) 
mat_csr = csr_matrix((50000,50)) 
mat_lil = mat_csr.tolil() 

idxs = np.random.randint(50000-1, size=1000) 

print(mat_sp[idxs, :].todense()) 
print(mat_csr[idxs, :]) 

mat_sp[idxs, :] = 2.0 # or any other expression that makes sens here 
mat_csr[idxs, :] = 2.0 

non dovrebbe importa se le matrici è scarsa o meno. Questo sarà più veloce di qualsiasi soluzione personalizzata con un loop (~ 250 volte più veloce di testSparse nel mio caso).

Ovviamente l'assegnazione a una matrice sparsa deve essere eseguita in modo da preservare la scarsità, altrimenti verrà riallocata, il che è costoso per csr_matrix. Ad esempio l'esempio sopra, produce un avvertimento a causa di esso.

Modifica: in risposta ai commenti. Consideriamo l'interrogazione di una sola riga,

In [1]: %timeit -n 1 mat_csr[0, :].todense() 
1 loops, best of 3: 101 µs per loop 

In [2]: %timeit -n 1 mat_lil[0, :].todense() 
1 loops, best of 3: 157 µs per loop 

In [3]: %timeit -n 1 mat_ds[0, :] 
The slowest run took 8.25 times longer than the fastest. This could mean that an intermediate result is being cached 
1 loops, best of 3: 954 ns per loop 

quindi sì, l'interrogazione di una fitta, è da 10 a 100 volte più veloce poi matrice sparsa con il risultato cast come densa (se si utilizza array di CSR o lil), perché non c'è meno spese generali. Non si può fare nulla al riguardo, devi solo scegliere se hai bisogno di array sparsi o no.

+0

Il ciclo era solo per normalizzare il tempo del calcolo. Gli accessi saranno fatti una riga alla volta. – user671931

+0

@ user671931 Ho aggiornato la risposta per tenere conto dei commenti. – rth

0

Ho provato a utilizzare un dok_mat: rix. Il risultato sulla mia prova è 0.03s e l'applicazione generale è 66s con:

3034124 11.833 0.000 20.293 0.000 defmatrix.py:312(__getitem__) 

Questo sembra essere un buon compromesso - ma mi chiedo se potrebbe essere migliore.