web-dev-qa-db-fra.com

Utilisation de Numpy Vectorize sur des fonctions renvoyant des vecteurs

numpy.vectorize prend une fonction f: a-> b et la transforme en g: a [] -> b [].

Cela fonctionne bien lorsque a et b sont des scalaires, mais je ne vois pas pourquoi cela ne fonctionnerait pas avec b en tant que ndarray ou liste, c'est-à-dire f: a-> b [] et g: a [] -> b [] []

Par exemple:

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

Cela donne:

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

Ok, donc ça donne les bonnes valeurs, mais le mauvais type. Et pire encore:

g(a).shape

rendements:

(4,)

Donc, ce tableau est à peu près inutile. Je sais que je peux le convertir en faisant:

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

pour me donner ce que je veux:

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

mais ce n'est ni efficace ni Pythonic. Est-ce que l'un de vous peut trouver un moyen plus propre de faire cela? 

Merci d'avance!

24
prodigenius

np.vectorize est juste une fonction de commodité. En fait, il ne fait pas accélère le code . S'il n'est pas pratique d'utiliser np.vectorize, écrivez simplement votre propre fonction qui fonctionne comme vous le souhaitez.

np.vectorize sert à transformer des fonctions qui ne sont pas compatibles avec numpy (par exemple, prendre des flottants en entrée et renvoyer des flottants en sortie) en fonctions pouvant fonctionner sur (et renvoyer) des tableaux numpy.

Votre fonction f est déjà consciente de numpy - elle utilise un tableau numpy dans sa définition et retourne un tableau numpy. Donc, np.vectorize ne convient pas à votre cas d'utilisation.

La solution consiste donc simplement à lancer votre propre fonction f qui fonctionne comme vous le souhaitez.

32
unutbu

Un nouveau paramètre signature dans 1.12.0 fait exactement ce que vous faites.

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

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

Alors g(np.arange(4)).shape donnera (4L, 5L).

Ici, la signature de f est spécifiée. Le (n) est la forme de la valeur de retour et le () est la forme du paramètre qui est scalaire. Et les paramètres peuvent aussi être des tableaux. Pour des signatures plus complexes, voir API de fonction universelle généralisée .

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)

Cela devrait résoudre le problème et cela fonctionnera quelle que soit la taille de votre entrée. "map" ne fonctionne que pour une entrée donnée. Utiliser ".tolist ()" et créer un nouveau ndarray résout le problème plus complètement et plus agréablement (je crois). J'espère que cela t'aides.

3
Aniq Ahsan

J'ai écrit une fonction, elle semble correspondre à vos besoins.

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)

Laisser essayer

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

Les sorties

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

Vous pouvez également l’envelopper de lambda ou partiel pour plus de commodité

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

Notez la docstring de vectorize dit

La fonction vectorize est fournie principalement pour des raisons de commodité et non pour Performances. L'implémentation est essentiellement une boucle for.

Ainsi, nous nous attendrions à ce que la amap présente ici des performances similaires à celles de vectorize. Je n'ai pas vérifié, tous les tests de performance sont les bienvenus.

Si la performance est vraiment importante, vous devriez envisager autre chose, par exemple calcul direct de tableau avec reshape et broadcast pour éviter les boucles en python pur (vectorize et amap sont les deux derniers cas).

1
Syrtis Major

La meilleure façon de résoudre ce problème consiste à utiliser un tableau 2-D NumPy (dans ce cas, un tableau de colonnes) comme entrée de la fonction original , qui générera ensuite une sortie 2-D avec les résultats, je crois. vous vous attendiez.

Voici à quoi cela pourrait ressembler dans le code:

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)

Il s'agit d'un moyen beaucoup plus simple et moins sujet aux erreurs pour terminer l'opération. Plutôt que d'essayer de transformer la fonction avec numpy.vectorize, cette méthode repose sur la capacité naturelle de NumPy à diffuser des tableaux. L'astuce consiste à s'assurer qu'au moins une dimension a une longueur égale entre les tableaux.

0
bburks832