2015-10-02 9 views
5

Stavo rivisitare alcuni dei miei codice per migliorare le prestazioni e stumpled sopra qualcosa di strano:Wrapping np.arrays __pow__ metodo

a = np.linspace(10,1000,1000000).reshape(1000,1000) 

%timeit np.square(a) 
100 loops, best of 3: 8.07 ms per loop 

%timeit a*a 
100 loops, best of 3: 8.18 ms per loop 

%timeit a**2 
100 loops, best of 3: 8.32 ms per loop 

Ok sembra avere un certo overhead quando si utilizza il potere-operator (**) ma per il resto sembrano identici (immagino NumPy sta facendo), ma poi ha ottenuto strano:

In [46]: %timeit np.power(a, 2) 
10 loops, best of 3: 121 ms per loop 

quindi non c'è nessun problema, ma sembra un po 'incoerente per avere un ripiego per il pow magia, ma non per l'UFUNC . Ma poi mi sono interessato da quando sto usando terze potenze molto:

%timeit a*a*a 
100 loops, best of 3: 18.1 ms per loop 

%timeit a**3 
10 loops, best of 3: 121 ms per loop 

%timeit np.power(a, 3) 
10 loops, best of 3: 121 ms per loop 

Non sembra esserci alcuna "scorciatoia" nella terza potenza e UFUNC e 'magic-pow' lavorare lo stesso (almeno per quanto riguarda prestazione).

Ma questo non è il massimo dal momento che voglio un metodo coerente di utilizzare i poteri nel mio codice e non sono abbastanza sicuro di come avvolgere il numero __pow__ di numpy.

Quindi, per arrivare al punto, la mia domanda è:

C'è un modo per avvolgere i numpys __pow__ metodo? Perché voglio un modo coerente di scrivere poteri nel mio script non scrivendo a**2 e in un altro posto power(a, 3). Semplicemente scrivendo a**3 e reindirizzando questo alla mia funzione di alimentazione, sarebbe preferibile (ma per quello avrei bisogno di avvolgere in qualche modo il ndarrays __pow__ o?). Attualmente sto usando una scorciatoia, ma questo non è che bello (ho nemmeno bisogno di dichiarare il == 2 caso esponente poiché np.power esegue non ottimale lì):

def power(array, exponent): 
    if exponent == 2: #catch this, or it calls the slow np.power(array, exponent) 
     return np.square(array) 
    if exponent == 3: 
     return array * array * array 
    #As soon as np.cbrt is avaiable catch the exponent 4/3 here too 
    return np.power(array, exponent) 

%timeit power(a, 3) 
100 loops, best of 3: 17.8 ms per loop 
%timeit a**3 
10 loops, best of 3: 121 ms per loop 

Sto usando NumPy v1.9.3 e non lo faccio desidera sottoclasse np.ndarray solo per il wrapping del metodo __pow__. :-)

EDIT: ho riscritto la parte in cui arrivo alla mia domanda. Per chiarirlo: non sto chiedendo perché NumPy lo fa nel modo in cui lo fa - questo è solo per spiegare perché io pongo la domanda.

+0

Vuoi veramente ottimizzare l'operatore di potenza per il case quadrato perché è un po 'più veloce? Mi sembra davvero troppo ottimizzato; soprattutto se significa introdurre un'altra funzione. Basta usare '** n' tutto il tempo, e non preoccuparti di quella piccola differenza di fuso orario – poke

+2

np.all (a ** 3 - a \ * a \ * a < 1e-6) -> Vero, quindi penso che sia lo stesso, per intorpidirti devo dichiararlo una matrice per innescare la moltiplicazione della matrice e sì la domanda è come avvolgere \ _ \ _ pow__ e la differenza di tempo è grande nel mio caso da quando mi occupo di molti di quegli array veramente grandi (1000 immagini 1000x2000). per le modifiche, sono abbastanza nuovo e non ottengo la formattazione del testo corretta – MSeifert

+2

Non è possibile modificare il metodo '__pow__' di un narray senza usare sottoclassi. Potresti (come hai suggerito) scrivere un'altra funzione. Se sei preoccupato per le prestazioni, potresti usare una libreria come [numexpr] (https://github.com/pydata/numexpr) che ha una funzione di potenza ottimizzata. Ad esempio, 'numexpr.evaluate (" a ** 3 ")' è circa 20 volte più veloce di 'a * a * a'. –

risposta

2

Questa è una buona presa. Anch'io mi chiedo perché questo comportamento. Ma per essere breve e concisa di rispondere alla domanda, vorrei solo fare:

def mypower(array, exponent): 
    return reduce(lambda x,y: x*y, [array for _ in range(exponent)]) 


%timeit mypower(a,2) 
100 loops, best of 3: 3.68 ms per loop 

%timeit mypower(a,3) 
100 loops, best of 3: 8.09 ms per loop 

%timeit mypower(a,4) 
100 loops, best of 3: 12.6 ms per loop 

Obsviouly l'overhead aumenta con l'esponente, ma per i più bassi è meglio di 10 volte il tempo.

Nota che questo è diverso dalla implementazione originale NumPy che non è specifico per un esponente numerico e supporta una serie di esponenti come secondo argomento (check it out here).

sovraccarico l'operatore

Il modo per fare quello che vuoi è quello di creare una sottoclasse ndarray e utilizzare le visualizzazioni. Vedere l'esempio seguente:

import numexpr 
import numpy as np 
​ 
class MyArray(np.ndarray): 
    def __pow__(self, other): 
     return reduce(lambda x,y: x*y, [self for _ in range(other)]) 
​ 
class NumExprArray(np.ndarray): 
    def __pow__(self, other): 
     return numexpr.evaluate("self**%f" % other) 
     #This implies extra overhead, is as much as 4x slower: 
     #return numexpr.evaluate("self**other") 

a = np.linspace(10,1000,1000000).reshape(1000,1000).view(MyArray) 
na = np.linspace(10,1000,1000000).reshape(1000,1000).view(NumExprArray) 
​ 
%timeit a**2 
1000 loops, best of 3: 1.2 ms per loop 

%timeit na**2 
1000 loops, best of 3: 1.14 ms per loop 

%timeit a**3 
100 loops, best of 3: 4.69 ms per loop 

%timeit na**3 
100 loops, best of 3: 2.36 ms per loop 

%timeit a**4 
100 loops, best of 3: 6.59 ms per loop 

%timeit na**4 
100 loops, best of 3: 2.4 ms per loop 

Per ulteriori informazioni su questo metodo si prega di seguire questo link.Un altro modo sarebbe quello di utilizzare un custom infix operator ma a fini di leggibilità non è così buono. Come si può vedere, numexpr dovrebbe essere la strada da percorrere.

+0

scusa, la tua risposta è buona ma volevo avvolgere in qualche modo la funzione np.ndarray \ _ \ _ pow__ (se possibile), quindi posso semplicemente scrivere un \ * \ * x che penso sia il modo migliore per leggere il codice. Sto sfruttando alcune supposizioni sui miei dati (numeri reali come dati e solo 2, 3 e 4/3 come esponenti) potrebbe significare che potrebbe rompersi da qualche altra parte, ma poiché questo è solo per uso personale, questo non importa molto (si spera) – MSeifert

+0

Ho aggiornato la risposta a quello che penso sia l'unico modo per fare ciò che vuoi ... L'elevazione del numero di – rll

+0

sarebbe più veloce per potenze maggiori, numexpr sembra farlo come puoi vedere nei tempi di ** 3 e ** 4 – jtaylor

2

Se leggo correttamente il source, quando numpy esegue l'alimentazione, controlla se il valore numerico dell'esponente è uno dei casi speciali (-0.5, 0, 0.5, 1 e 2). In tal caso, l'operazione viene eseguita utilizzando le routine speciali. Tutti gli altri valori numerici dell'esponente sono considerati "generali" e saranno alimentati nella funzione di potenza generica, che potrebbe essere lenta (specialmente se l'esponente è promosso in virgola mobile, ma non sono sicuro se questo è il caso con a ** 3).

+0

Scusa, forse ho fatto qualcosa di sbagliato nel pubblicare la mia domanda, ma non sto chiedendo perché Numpy lo fa in questo modo. Nella mia esperienza numpy fa la maggior parte delle cose nel miglior modo possibile. Sto chiedendo un modo per reindirizzare i pitoni chiamare numpy.ndarray. \ _ \ _ Pow__, quando si chiama un ** x, alla mia funzione di potenza, che cattura i miei casi promossi e li ottimizza e se non può li lancia ritorno a Numpy. – MSeifert