128 votes

Évaluation efficace d'une fonction à chaque cellule d'un tableau NumPy

Étant donné un NumPy réseau A Quel est le moyen le plus rapide et le plus efficace d'appliquer la même fonction, f , à tous cellule ?

  1. Supposons que nous attribuions à A(i,j) les f(A(i,j)) .

  2. La fonction, f n'a pas de sortie binaire, les opérations de masquage ne sont donc d'aucune utilité.

L'itération "évidente" en double boucle (à travers chaque cellule) est-elle la solution optimale ?

164voto

blubberdiblub Points 441

Vous pourriez simplement vectoriser la fonction et l'appliquer directement à un tableau Numpy à chaque fois que vous en avez besoin :

import numpy as np

def f(x):
    return x * x + 3 * x - 2 if x > 0 else x * 5 + 8

f = np.vectorize(f)  # or use a different name if you want to keep the original f

result_array = f(A)  # if A is your Numpy array

Il est probablement préférable de spécifier directement un type de sortie explicite lors de la vectorisation :

f = np.vectorize(f, otypes=[np.float])

6voto

cyborg Points 5463

Une question similaire se pose : Mappage d'un tableau NumPy en place . Si vous pouvez trouver un ufunc pour votre f(), alors vous devez utiliser le paramètre out.

1voto

Si vous travaillez avec des chiffres et des f(A(i,j)) = f(A(j,i)) vous pouvez utiliser scipy.spatial.distance.cdist définissant f comme une distance entre A(i) y A(j) .

0voto

Rushikesh Points 81

Toutes les réponses ci-dessus sont comparables, mais si vous avez besoin d'utiliser une fonction personnalisée pour le mappage, et que vous disposez de numpy.ndarray et vous devez conserver la forme du tableau.

Je n'en ai comparé que deux, mais il conservera la forme de l'objet. ndarray . J'ai utilisé le tableau avec 1 million d'entrées à des fins de comparaison. J'utilise ici la fonction square. Je présente le cas général pour un tableau à n dimensions. Pour un tableau à deux dimensions, il suffit de faire iter pour le 2D.

import numpy, time

def A(e):
    return e * e

def timeit():
    y = numpy.arange(1000000)
    now = time.time()
    numpy.array([A(x) for x in y.reshape(-1)]).reshape(y.shape)        
    print(time.time() - now)
    now = time.time()
    numpy.fromiter((A(x) for x in y.reshape(-1)), y.dtype).reshape(y.shape)
    print(time.time() - now)
    now = time.time()
    numpy.square(y)  
    print(time.time() - now)

Sortie

>>> timeit()
1.162431240081787    # list comprehension and then building numpy array
1.0775556564331055   # from numpy.fromiter
0.002948284149169922 # using inbuilt function

Ici, vous pouvez voir clairement numpy.fromiter fonction de carré d'utilisateur, utilisez celle de votre choix. Si votre fonction dépend de i, j c'est-à-dire les indices du tableau, itérer sur la taille du tableau comme for ind in range(arr.size) , utiliser numpy.unravel_index pour obtenir i, j, .. en fonction de votre indice 1D et de la forme du tableau numpy.unravel_index

Cette réponse est inspirée de ma réponse à une autre question. aquí

0voto

Wunderbar Points 367

Je pense avoir trouvé une meilleure solution. L'idée de changer la fonction en fonction universelle python (voir la documentation ), qui peut exercer un calcul parallèle sous le capot.

Il est possible d'écrire son propre ufunc en C, ce qui est certainement plus efficace, ou en invoquant np.frompyfunc qui est une méthode d'usine intégrée. Après avoir été testée, cette méthode est plus efficace que la méthode np.vectorize :

f = lambda x, y: x * y
f_arr = np.frompyfunc(f, 2, 1)
vf = np.vectorize(f)
arr = np.linspace(0, 1, 10000)

%timeit f_arr(arr, arr) # 307ms
%timeit f_arr(arr, arr) # 450ms

J'ai également testé des échantillons plus importants et l'amélioration est proportionnelle. Pour une comparaison des performances d'autres méthodes, voir ce poste

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X