2013-12-17 2 views
10

Diciamo che voglio fare una somma elemento-saggio di una lista di array NumPy:sommando rapidamente array NumPy elemento-saggio

tosum = [rand(100,100) for n in range(10)] 

sto cercando il modo migliore per farlo. Sembra che numpy.sum è terribile:

timeit.timeit('sum(array(tosum), axis=0)', 
       setup='from numpy import sum; from __main__ import tosum, array', 
       number=10000) 
75.02289700508118 
timeit.timeit('sum(tosum, axis=0)', 
       setup='from numpy import sum; from __main__ import tosum', 
       number=10000) 
78.99106407165527 

Ridurre è molto più veloce (per la somma di quasi due ordini di grandezza):

timeit.timeit('reduce(add,tosum)', 
       setup='from numpy import add; from __main__ import tosum', 
       number=10000) 
1.131795883178711 

Esso si presenta come ridurre ha anche un vantaggio significativo sul somma non NumPy (notare che questi sono per 1E6 corre piuttosto che 1E4 per i tempi di cui sopra):

timeit.timeit('reduce(add,tosum)', 
       setup='from numpy import add; from __main__ import tosum', 
       number=1000000) 
109.98814797401428 

timeit.timeit('sum(tosum)', 
       setup='from __main__ import tosum', 
       number=1000000) 
125.52461504936218 

ci sono altri metodi che dovrei provare? Qualcuno può spiegare le classifiche?


Modifica

numpy.sum è sicuramente più veloce se la lista è trasformato in una matrice di NumPy prima:

tosum2 = array(tosum) 
timeit.timeit('sum(tosum2, axis=0)', 
       setup='from numpy import sum; from __main__ import tosum2', 
       number=10000) 
1.1545608043670654 

Tuttavia, sono interessati solo a fare una somma una volta , quindi trasformare la matrice in una matrice numpy comporterebbe comunque una vera penalità delle prestazioni.

+2

Sto indovinando che 'np.sum' prima crea e matrice e poi lo riassume il che spiegherebbe è scarso rendimento ... sto cercando di indovinare che sarebbe stato il più veloce se si fosse passato un' np. ndarray' per cominciare. – mgilson

+1

E mi aspetto che riduca per battere 'sum' di circa 1/11 dato che salta il' 0 + tosum [0] 'che è implicito in' sum'. – mgilson

+0

Questo ha senso. Comincio con un gruppo di array separati, quindi trasformarli in una serie numpy in primo luogo comporterebbe la stessa penalizzazione delle prestazioni di come la somma lo fa per me (dal momento che sto facendo la somma una sola volta). – lnmaurer

risposta

4

Quanto segue è competitivo con reduce ed è più veloce se l'elenco tosum è sufficientemente lungo. Tuttavia, non è molto più veloce ed è più codice. (reduce(add, tosum) è sicuro abbastanza.)

def loop_inplace_sum(arrlist): 
    # assumes len(arrlist) > 0 
    sum = arrlist[0].copy() 
    for a in arrlist[1:]: 
     sum += a 
    return sum 

Timing per l'originale tosum. reduce(add, tosum) è più veloce:

In [128]: tosum = [rand(100,100) for n in range(10)] 

In [129]: %timeit reduce(add, tosum) 
10000 loops, best of 3: 73.5 µs per loop 

In [130]: %timeit loop_inplace_sum(tosum) 
10000 loops, best of 3: 78 µs per loop 

Timing per un elenco di array molto più a lungo. Ora loop_inplace_sum è più veloce.

In [131]: tosum = [rand(100,100) for n in range(500)] 

In [132]: %timeit reduce(add, tosum) 
100 loops, best of 3: 5.09 ms per loop 

In [133]: %timeit loop_inplace_sum(tosum) 
100 loops, best of 3: 4.4 ms per loop 
+0

Interessante. Avete pensieri su dove arriva la velocità? Forse ha meno spese generali di ridurre? (Sto solo aggiungendo ~ 10 array di grandi dimensioni insieme, quindi probabilmente mi limiterò a ridurre, ma questo metodo è utile per il futuro.) – lnmaurer

+0

Sì, meno spese generali: l'aggiunta sul posto elimina la creazione di alcuni oggetti. Se si sostituisce 'sum + = a' con' sum = sum + a', diventa un po 'più lento di 'reduce'. –