it-swarm.com.de

Verwenden von Numpy Vectorize für Funktionen, die Vektoren zurückgeben

numpy.vectorize nimmt eine Funktion f: a-> b und wandelt sie in g: a [] -> b [] um.

Dies funktioniert gut, wenn a und b Skalare sind, aber ich kann mir keinen Grund vorstellen, warum es mit b nicht als ndarray oder Liste funktioniert, dh f: a-> b [] und g: a [] -> b [] []

Zum Beispiel:

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))

Dies ergibt:

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

Ok, das ergibt die richtigen Werte, aber den falschen dtype. Und noch schlimmer:

g(a).shape

ergibt:

(4,)

Also ist dieses Array so ziemlich nutzlos. Ich weiß, dass ich es konvertieren kann, indem ich:

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

um mir zu geben was ich will:

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

aber das ist weder effizient noch pythonisch. Kann einer von euch einen saubereren Weg finden, dies zu tun?

Danke im Voraus!

24
prodigenius

np.vectorize ist nur eine Komfortfunktion. Es macht eigentlich keinen Code schneller laufen . Wenn es nicht bequem ist, np.vectorize zu verwenden, schreiben Sie einfach Ihre eigene Funktion, die nach Ihren Wünschen funktioniert.

Der Zweck von np.vectorize besteht darin, nicht numpy-fähige Funktionen (z. B. floats als Eingabe und return floats als Ausgabe) in Funktionen umzuwandeln, die numpy-Arrays verarbeiten (und zurückgeben) können.

Ihre Funktion f ist bereits numpy-fähig - sie verwendet ein numpy-Array in ihrer Definition und gibt ein numpy-Array zurück. np.vectorize passt also nicht zu Ihrem Anwendungsfall.

Die Lösung besteht daher darin, einfach Ihre eigene Funktion f zu rollen, die so funktioniert, wie Sie es wünschen.

32
unutbu

Ein neuer Parameter signature in 1.12.0 macht genau was du was.

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

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

Dann gibt g(np.arange(4)).shape(4L, 5L) aus.

Hier wird die Signatur von f angegeben. Der (n) ist die Form des Rückgabewerts und der () ist die Form des Parameters, der skalar ist. Die Parameter können auch Arrays sein. Für komplexere Signaturen siehe Generalized Universal Function API .

5
Cosyn
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)

Dies sollte das Problem beheben und funktioniert unabhängig von der Größe Ihrer Eingabe. "map" funktioniert nur für eine dimensionale Eingabe. Die Verwendung von ".tolist ()" und das Erstellen eines neuen ndarray löst das Problem vollständiger und besser (glaube ich). Hoffe das hilft.

3
Aniq Ahsan

Ich habe eine Funktion geschrieben, die anscheinend zu Ihrem Bedarf passt.

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)

Lassen Sie versuchen

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

Ausgänge

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

Sie können es auch aus Bequemlichkeitsgründen mit Lambda oder teilweise einwickeln

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

Beachten Sie die DocString von vectorize sagt

Die Funktion vectorize dient in erster Linie dem Komfort und nicht der Leistung. Die Implementierung ist im Wesentlichen eine for-Schleife.

Daher würden wir erwarten, dass die amap hier eine ähnliche Leistung hat wie die vectorize. Ich habe es nicht überprüft. Jeder Leistungstest ist willkommen.

Wenn die Leistung wirklich wichtig ist, sollten Sie etwas anderes in Betracht ziehen, z. direkte Arrayberechnung mit reshape und broadcast, um eine Schleife in reinem Python zu vermeiden (sowohl vectorize als auch amap sind der spätere Fall).

1
Syrtis Major

Der beste Weg, dies zu lösen, wäre die Verwendung eines 2-D-NumPy-Arrays (in diesem Fall eines Spalten-Arrays) als Eingabe für die Funktion original , die dann eine 2-D-Ausgabe mit den erwarteten Ergebnissen generiert du hast erwartet.

So könnte es im Code aussehen:

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)

Dies ist eine viel einfachere und weniger fehleranfällige Methode, um den Vorgang abzuschließen. Anstatt zu versuchen, die Funktion mit numpy.vectorize zu transformieren, stützt sich diese Methode auf NumPys natürliche Fähigkeit, Arrays zu übertragen. Der Trick besteht darin, sicherzustellen, dass mindestens eine Dimension die gleiche Länge zwischen den Arrays aufweist.

0
bburks832