En utilisant num Py Vectorize sur les fonctions qui renvoient des vecteurs

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

Cela fonctionne bien lorsque a et b sont scalaires, mais je ne peux pas penser à une raison pour laquelle il ne fonctionnerait pas avec b comme un ndarray ou de la liste, c'est à dire f:a->b[] et g: []->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 dtype. Et même pire:

g(a).shape

donne:

(4,)

ce tableau est donc à peu près inutile. Je sais que je peux le convertir en train de faire:

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 pythonique. Qui de vous les gars trouver une façon plus propre de faire cela?

Merci d'avance!

22
demandé sur Alex 2010-07-31 22:20:10

5 réponses

np.vectorize est juste une fonction de commodité. Il ne fait pas les le code est exécuté plus rapidement. Si elle n'est pas pratique à utiliser np.vectorize, il suffit d'écrire votre propre fonction qui fonctionne comme vous le souhaitez.

Le but de np.vectorize est de transformer des fonctions qui ne sont pas sensibles à la numpy (par exemple prendre des flotteurs comme entrée et retourner des flotteurs comme sortie) en fonctions qui peuvent fonctionner sur (et retourner) des tableaux numpy.

votre fonction f est déjà sensible à la numpy -- il 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 est donc juste de lancer votre propre fonction f cela fonctionne comme vous le souhaitez.

29
répondu unutbu 2010-07-31 19:25:37

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

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

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

g(np.arange(4)).shape donner (4L, 5L).

Voici la signature de f est spécifié. (n) est la forme de la valeur de retour, et le () est la forme du paramètre scalaire. Et les paramètres peuvent être des tableaux. Pour les signatures plus complexes, voir Généralisée de la Fonction Universelle de l'API.

5
répondu Cosyn 2017-10-21 05:11:57
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 corriger le problème et cela fonctionnera quelle que soit la taille de votre entrée. "carte" ne fonctionne que pour une dimensions des entrées. Utiliser." tolist () " et la création d'un nouveau ndarray résout le problème plus complètement et plus gentiment(je crois). Espérons que cette aide.

3
répondu Aniq Ahsan 2014-07-24 05:07:47

j'ai écrit une fonction, elle semble correspondre à votre besoin.

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)

Laissez vous tenter

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

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 de partial for convenience

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

Notez le document de vectorize dit

vectorize la fonction est fournie principalement pour la commodité, pas pour performance. La mise en œuvre est essentiellement un pour boucle.

ainsi, on s'attendrait à ce que le amap ici ont des performances similaires, comme vectorize. Je n'ai pas vérifié, les tests sont les bienvenus.

si la performance est vraiment importante, vous devriez considérer autre chose, par exemple le calcul de tableau direct avec reshape et broadcast pour éviter la boucle en python pur (tous les deux vectorize et amap sont le dernier cas).

1
répondu Syrtis Major 2016-08-02 01:46:06

La meilleure façon de résoudre ceci serait d'utiliser un tableau 2-D de NumPy (dans ce cas un tableau de colonne) comme entrée dans le origine fonction, qui générera alors une sortie 2-D avec les résultats que je crois que vous attendiez.

Voici à quoi il 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)

C'est beaucoup plus simple et moins sujette aux erreurs façon pour terminer l'opération. Plutôt que d'essayer de transformer la fonction avec numpy.vectorize, cette méthode repose sur les capacité naturelle de diffuser des tableaux. L'astuce est de faire en sorte qu'au moins une dimension a une longueur égale entre les tableaux.

0
répondu bburks832 2016-08-02 01:23:59