2013-05-29 36 views
15

Sto usando la funzione numpy.array() per creare numpy.float64 ndarrays dalle liste.Perché numpy.array() a volte è molto lento?

Ho notato che questo è molto lento quando l'elenco contiene Nessuno o viene fornito un elenco di elenchi.

Di seguito sono riportati alcuni esempi con le volte. Esistono soluzioni ovvie, ma perché è così lento?

Esempi di elenco di None:

### Very slow to call array() with list of None 
In [3]: %timeit numpy.array([None]*100000, dtype=numpy.float64) 
1 loops, best of 3: 240 ms per loop 

### Problem doesn't exist with array of zeroes 
In [4]: %timeit numpy.array([0.0]*100000, dtype=numpy.float64) 
100 loops, best of 3: 9.94 ms per loop 

### Also fast if we use dtype=object and convert to float64 
In [5]: %timeit numpy.array([None]*100000, dtype=numpy.object).astype(numpy.float64) 
100 loops, best of 3: 4.92 ms per loop 

### Also fast if we use fromiter() insead of array() 
In [6]: %timeit numpy.fromiter([None]*100000, dtype=numpy.float64) 
100 loops, best of 3: 3.29 ms per loop 

esempi di lista di liste:

### Very slow to create column matrix 
In [7]: %timeit numpy.array([[0.0]]*100000, dtype=numpy.float64) 
1 loops, best of 3: 353 ms per loop 

### No problem to create column vector and reshape 
In [8]: %timeit numpy.array([0.0]*100000, dtype=numpy.float64).reshape((-1,1)) 
100 loops, best of 3: 10 ms per loop 

### Can use itertools to flatten input lists 
In [9]: %timeit numpy.fromiter(itertools.chain.from_iterable([[0.0]]*100000),dtype=numpy.float64).reshape((-1,1)) 
100 loops, best of 3: 9.65 ms per loop 
+7

per creare un array vuoto, utilizzare 'a = numpy.empty (100000)'. Per creare una matrice con tutti gli zeri: 'a = numpy.zeros (100000)'. – jfs

+0

Il mio obiettivo non è creare un array di zero o vuoto, ma sto riscontrando problemi di prestazioni quando None è nel set di dati. Se creo l'array e prima e imposto i valori, ho praticamente lo stesso problema. In [18]: a = numpy.empty ((100000,)) In [19]:% timeit un [:] = [nessuna] * 100000 1 loop, migliore di 3: 209 ms per loop In [20]:% timeit a [:] = [0] * 100000 100 loop, meglio di 3: 9,59 ms per loop – MarkW

+0

in realtà è una domanda interessante: perché '[3]' è molto più lento di '[5 ] '(potrebbe essere un bug o solo un caso d'uso raro non ottimizzato). Per ripetere 'NaN', chiama' numpy.repeat (float ("nan"), 100000) ' – jfs

risposta

1

La mia ipotesi è che il codice per la conversione di liste chiama semplicemente float su tutto. Se l'argomento definisce __float__, lo chiamiamo, altrimenti lo trattiamo come una stringa (generando un'eccezione su None, la prendiamo e inseriamo np.nan). La gestione delle eccezioni dovrebbe essere relativamente più lenta.

Timing sembra per verificare questa ipotesi:

import numpy as np 
%timeit [None] * 100000 
> 1000 loops, best of 3: 1.04 ms per loop 

%timeit np.array([0.0] * 100000) 
> 10 loops, best of 3: 21.3 ms per loop 
%timeit [i.__float__() for i in [0.0] * 100000] 
> 10 loops, best of 3: 32 ms per loop 


def flt(d): 
    try: 
     return float(d) 
    except: 
     return np.nan 

%timeit np.array([None] * 100000, dtype=np.float64) 
> 1 loops, best of 3: 477 ms per loop  
%timeit [flt(d) for d in [None] * 100000] 
> 1 loops, best of 3: 328 ms per loop 

Aggiunta di un altro caso solo essere evidente a dove sto andando con questo. Se ci fosse un controllo esplicito per Nessuno, non sarebbe così lento sopra:

def flt2(d):        
    if d is None: 
     return np.nan 
    try: 
     return float(d) 
    except: 
     return np.nan 

%timeit [flt2(d) for d in [None] * 100000] 
> 10 loops, best of 3: 45 ms per loop 
+0

Ho trovato la tua ipotesi molto interessante, anche se le differenze temporali che stai ottenendo (circa 1.5 volte più veloce) non supportano ancora i tuoi argomenti poiché le differenze di temporizzazione dell'OP sono circa 35X .... potresti continuare a lavorare in questa direzione –

+0

@sgpc Sì, se volessi approfondire il passaggio successivo, guarderei la fonte. Anche se lo so: se stessero esplicitamente controllando "None", non sarebbe così lento. – U2EF1

+1

Ho scavato più a fondo. Il codice sta spendendo tutto il suo tempo buttando esecuzioni. Non considera le cose come stringhe quanto chiamare getattr o getbuffer per molte cose che non hanno l'attributo o non sono buffer. Entrambi creano e ignorano le eccezioni. Ho ottimizzato common.c e ctor.c per evitare questi casi e le prestazioni sono più simili a quelle che ti aspetteresti. – MarkW

3

Ho segnalato questo come un problema di numpy. I file di report e patch sono qui:

https://github.com/numpy/numpy/issues/3392

Dopo l'applicazione di patch:

# was 240 ms, best alternate version was 3.29 
In [5]: %timeit numpy.array([None]*100000) 
100 loops, best of 3: 7.49 ms per loop 

# was 353 ms, best alternate version was 9.65 
In [6]: %timeit numpy.array([[0.0]]*100000) 
10 loops, best of 3: 23.7 ms per loop