2010-07-31 14 views
17

numpy.vectorize accetta una funzione f: a-> b e la trasforma in g: a [] -> b [].Utilizzo di Numpy Vectorize su Funzioni che restituiscono vettori

Questo funziona bene quando a e b sono scalari, ma non riesco a pensare a una ragione per la quale non avrebbe funzionato con b come ndarray o un elenco, cioè f: a-> b [] e g: un [] -> b [] []

Ad esempio:

import numpy as np 
def f(x): 
    return x * np.array([1,1,1,1,1], dtype=np.float32) 
g = np.vectorize(f, otypes=[np.ndarray]) 
a = np.arange(4) 
print(g(a)) 

Questo produce:

array([[ 0. 0. 0. 0. 0.], 
     [ 1. 1. 1. 1. 1.], 
     [ 2. 2. 2. 2. 2.], 
     [ 3. 3. 3. 3. 3.]], dtype=object) 

Ok, in modo che dà i giusti valori, ma il DTYPE sbagliata. E ancora peggio:

g(a).shape 

rendimenti:

(4,) 

Quindi questo array è praticamente inutile. So che posso convertirlo fare:

np.array(map(list, a), dtype=np.float32) 

di darmi quello che voglio:

array([[ 0., 0., 0., 0., 0.], 
     [ 1., 1., 1., 1., 1.], 
     [ 2., 2., 2., 2., 2.], 
     [ 3., 3., 3., 3., 3.]], dtype=float32) 

ma che non è né efficiente né divinatorio. Qualcuno di voi può trovare un modo più pulito per farlo?

Grazie in anticipo!

risposta

24

np.vectorize è solo una comoda funzione. In realtà non è make code run any faster. Se non è conveniente usare np.vectorize, scrivi semplicemente la tua funzione che funziona come desideri.

Lo scopo di np.vectorize è di trasformare le funzioni che non sono sensibili al numpy (ad esempio, prendere i float come input e restituire float come output) in funzioni che possono operare su (e restituire) array di numpy.

La funzione f è già numpy-aware: utilizza una matrice numpy nella sua definizione e restituisce una matrice numpy. Quindi np.vectorize non è adatto per il tuo caso d'uso.

La soluzione è quindi quella di ruotare la propria funzione f che funziona nel modo desiderato.

+6

Infatti, "solo una funzione di convenienza" descrive la maggior parte dell'APP numpy. Questo è il punto. Peccato che questa funzione non si comporti come ci si aspetterebbe. –

+1

La maggior parte delle funzioni NumPy è solo un po 'più lenta della funzione equivalente scritta in C. Ciò è vero quando la funzione NumPy è semplicemente un involucro sottile attorno a una funzione C (o Fortran). Al contrario, una funzione 'np.vectorized' deve ancora chiamare una * funzione Python * una volta per ogni elemento dell'array, quindi esegue più come il codice Python che il codice C. Le ricerche di nomi dinamici di Python offrono maggiore flessibilità, ma possono essere molto più lente del codice C. – unutbu

+0

non secondo https://stackoverflow.com/questions/35215161/most-efficient-way-to-map-function-over-numpy-array –

2
import numpy as np 
def f(x): 
    return x * np.array([1,1,1,1,1], dtype=np.float32) 
g = np.vectorize(f, otypes=[np.ndarray]) 
a = np.arange(4) 
b = g(a) 
b = np.array(b.tolist()) 
print(b)#b.shape = (4,5) 
c = np.ones((2,3,4)) 
d = g(c) 
d = np.array(d.tolist()) 
print(d)#d.shape = (2,3,4,5) 

Questo dovrebbe risolvere il problema e funzionerà indipendentemente dalle dimensioni dell'input. "map" funziona solo per un input di dimensioni. Usare ".tolist()" e creare un nuovo ndarray risolve il problema in modo più completo e piacevole (credo). Spero che questo ti aiuti.

0

Il modo migliore per risolvere questo problema è utilizzare un array NumPy 2-D (in questo caso un array di colonne) come input per la funzione originale, che genererà quindi un'uscita 2D con i risultati Credo che ti aspettassi.

Ecco quello che potrebbe sembrare in codice:

import numpy as np 
def f(x): 
    return x*np.array([1, 1, 1, 1, 1], dtype=np.float32) 

a = np.arange(4).reshape((4, 1)) 
b = f(a) 
# b is a 2-D array with shape (4, 5) 
print(b) 

questo è un modo di errore molto più semplice e meno soggetto a completare l'operazione.Piuttosto che provare a trasformare la funzione con numpy.vectorize, questo metodo si basa sulla naturale capacità di NumPy di ​​trasmettere array. Il trucco è assicurarsi che almeno una dimensione abbia una lunghezza uguale tra gli array.

1

Ho scritto una funzione, sembra adatta alle vostre necessità.

def amap(func, *args): 
    '''array version of build-in map 
    amap(function, sequence[, sequence, ...]) -> array 
    Examples 
    -------- 
    >>> amap(lambda x: x**2, 1) 
    array(1) 
    >>> amap(lambda x: x**2, [1, 2]) 
    array([1, 4]) 
    >>> amap(lambda x,y: y**2 + x**2, 1, [1, 2]) 
    array([2, 5]) 
    >>> amap(lambda x: (x, x), 1) 
    array([1, 1]) 
    >>> amap(lambda x,y: [x**2, y**2], [1,2], [3,4]) 
    array([[1, 9], [4, 16]]) 
    ''' 
    args = np.broadcast(None, *args) 
    res = np.array([func(*arg[1:]) for arg in args]) 
    shape = args.shape + res.shape[1:] 
    return res.reshape(shape) 

Lasciatevi tentare

def f(x): 
     return x * np.array([1,1,1,1,1], dtype=np.float32) 
amap(f, np.arange(4)) 

uscite

array([[ 0., 0., 0., 0., 0.], 
     [ 1., 1., 1., 1., 1.], 
     [ 2., 2., 2., 2., 2.], 
     [ 3., 3., 3., 3., 3.]], dtype=float32) 

Si può anche avvolgere con lambda o parziale per comodità

g = lambda x:amap(f, x) 
g(np.arange(4)) 

Nota la docstring di vectorize dice

La funzione vectorize è fornita principalmente per convenienza, non per prestazioni. L'implementazione è essenzialmente un ciclo for.

Quindi ci aspettiamo che lo amap abbia prestazioni simili a vectorize. Non l'ho controllato, qualsiasi test di performance è il benvenuto.

Se la prestazione è davvero importante, è necessario considerare qualcos'altro, ad es. calcolo diretto dell'array con reshape e broadcast per evitare loop in python puro (sia vectorize sia amap sono il caso successivo).

1

Un nuovo parametro signature in 1.12.0 fa esattamente quello che cosa.

def f(x): 
    return x * np.array([1,1,1,1,1], dtype=np.float32) 

g = np.vectorize(f, signature='()->(n)') 

Poi g(np.arange(4)).shape darà (4L, 5L).

Qui è specificata la firma di f. Lo (n) è la forma del valore restituito e () è la forma del parametro che è scalare. E i parametri possono essere anche matrici. Per firme più complesse, vedere Generalized Universal Function API.

Problemi correlati