Étant donné un tableau NumPy A , quel est le moyen le plus rapide / le plus efficace d'appliquer la même fonction, f , à chaque cellule?
Supposons que l'on attribue à A (i, j) le f (A (i, j)) .
La fonction, f , n'a pas de sortie binaire, donc les opérations de masquage n'aideront pas.
L'itération double boucle "évidente" (à travers chaque cellule) est-elle la solution optimale?
Réponses:
Vous pouvez simplement vectoriser la fonction, puis l'appliquer directement à un tableau Numpy à chaque fois que vous en avez besoin:
Il est probablement préférable de spécifier un type de sortie explicite directement lors de la vectorisation:
la source
vectorize
description de la fonction: La fonction de vectorisation est fournie principalement pour des raisons de commodité, pas pour les performances. L'implémentation est essentiellement une boucle for. Cela n'accélérera donc probablement pas du tout le processus.vectorize
détermine le type de retour. Cela a produit des bugs.frompyfunc
est un peu plus rapide, mais renvoie un tableau d'objets dtype. Les deux alimentent des scalaires, pas des lignes ou des colonnes.np.vectorize
ma fonction (qui utilise RK45) me donne une vitesse d'un facteur de ~ 20.Une question similaire est: mapper un tableau NumPy en place . Si vous pouvez trouver un ufunc pour votre f (), vous devez utiliser le paramètre out.
la source
Si vous travaillez avec des nombres et
f(A(i,j)) = f(A(j,i))
, vous pouvez utiliser scipy.spatial.distance.cdist définissant f comme une distance entreA(i)
etA(j)
.la source
Je crois avoir trouvé une meilleure solution. L'idée de changer la fonction en fonction universelle python (voir documentation ), qui permet d'exercer un calcul parallèle sous le capot.
On peut écrire son propre personnalisé
ufunc
en C, ce qui est sûrement plus efficace, ou en invoquantnp.frompyfunc
, qui est une méthode d'usine intégrée. Après les tests, c'est plus efficace quenp.vectorize
:J'ai également testé des échantillons plus grands et l'amélioration est proportionnelle. Pour une comparaison des performances d'autres méthodes, voir cet article
la source
Lorsque le tableau 2d (ou nd-tableau) est contigu en C ou F, alors cette tâche de mappage d'une fonction sur un tableau 2d est pratiquement la même que la tâche de mappage d'une fonction sur un tableau 1d - nous venons de doivent le voir de cette façon, par exemple via
np.ravel(A,'K')
.Une solution possible pour 1d-array a été discutée par exemple ici .
Cependant, lorsque la mémoire du 2d-array n'est pas contiguë, alors la situation est un peu plus compliquée, car on voudrait éviter d'éventuels échecs de cache si les axes sont traités dans le mauvais ordre.
Numpy dispose déjà d'une machine pour traiter les axes dans le meilleur ordre possible. Une possibilité d'utiliser cette machine est
np.vectorize
. Cependant, la documentation de numpynp.vectorize
indique qu'elle est "fournie principalement pour la commodité, pas pour les performances" - une fonction python lente reste une fonction python lente avec toute la surcharge associée! Un autre problème est son énorme consommation de mémoire - voir par exemple ce message SO .Quand on veut avoir une performance d'une fonction C mais utiliser la machinerie de numpy, une bonne solution est d'utiliser numba pour la création d'ufuncs, par exemple:
Il bat facilement
np.vectorize
mais aussi quand la même fonction serait exécutée que la multiplication / addition de numpy-array, ieVoir l'annexe de cette réponse pour le code de mesure du temps:
La version de Numba (verte) est environ 100 fois plus rapide que la fonction python (ie
np.vectorize
), ce qui n'est pas surprenant. Mais c'est également environ 10 fois plus rapide que la fonctionnalité numpy, car la version numbas n'a pas besoin de tableaux intermédiaires et utilise donc le cache plus efficacement.Bien que l'approche ufunc de numba soit un bon compromis entre convivialité et performances, ce n'est toujours pas le mieux que nous puissions faire. Pourtant, il n’existe pas de solution miracle ou d’approche idéale pour une tâche quelconque - il faut comprendre quelles sont les limites et comment elles peuvent être atténuées.
Par exemple, pour les fonctions transcendantes (par exemple
exp
,sin
,cos
) numba ne fournit pas d'avantages par rapport de numpynp.exp
(il n'y a pas de tableaux temporaires créés - la principale source de la vitesse-up). Cependant, mon installation Anaconda utilise le VML d'Intel pour les vecteurs supérieurs à 8192 - il ne peut tout simplement pas le faire si la mémoire n'est pas contiguë. Il serait donc préférable de copier les éléments dans une mémoire contiguë afin de pouvoir utiliser le VML d'Intel:Pour l'équité de la comparaison, j'ai désactivé la parallélisation de VML (voir code en annexe):
Comme on peut le voir, une fois que VML entre en jeu, la surcharge de copie est plus que compensée. Pourtant, une fois que les données deviennent trop volumineuses pour le cache L3, l'avantage est minime car la tâche redevient liée à la bande passante mémoire.
D'un autre côté, numba pourrait également utiliser le SVML d'Intel, comme expliqué dans cet article :
et l'utilisation de VML avec des rendements de parallélisation:
La version de numba a moins de frais généraux, mais pour certaines tailles, VML bat SVML même malgré la surcharge de copie supplémentaire - ce qui n'est pas un peu surprenant car les ufuncs de numba ne sont pas parallélisés.
Annonces:
A. comparaison de la fonction polynomiale:
B. comparaison de
exp
:la source
Toutes les réponses ci-dessus se comparent bien, mais si vous devez utiliser une fonction personnalisée pour le mappage, et que vous l'avez fait
numpy.ndarray
, vous devez conserver la forme du tableau.Je n'ai comparer que deux, mais il conservera la forme de
ndarray
. J'ai utilisé le tableau avec 1 million d'entrées à des fins de comparaison. Ici, j'utilise la fonction carrée. Je présente le cas général du tableau à n dimensions. Pour deux dimensions, faites simplementiter
pour 2D.Production
ici, vous pouvez voir clairement la
numpy.fromiter
fonction carrée de l'utilisateur, utilisez celle de votre choix. Si votre fonction dépend desi, j
indices du tableau, itérez sur la taille du tableau commefor ind in range(arr.size)
, utiliseznumpy.unravel_index
pour obtenir eni, j, ..
fonction de votre index 1D et de la forme du tableau numpy.unravel_indexCette réponse est inspirée de ma réponse à une autre question ici
la source