numpy.amax () trouvera la valeur max dans un tableau et numpy.amin () fait de même pour la valeur min. Si je veux trouver à la fois max et min, je dois appeler les deux fonctions, ce qui nécessite de passer deux fois sur le (très grand) tableau, ce qui semble lent.
Y a-t-il une fonction dans l'API numpy qui trouve à la fois max et min en un seul passage dans les données?
amax
etamin
minmax
à la bibliothèque en question ( github.com/numpy/numpy/issues/9836 ).Réponses:
Non. Au moment d'écrire ces lignes, une telle fonction n'existe pas. (Et oui, s'il y avait une telle fonction, ses performances seraient nettement meilleures que celles d'appeler
numpy.amin()
etnumpy.amax()
successivement sur un grand tableau.)la source
Je ne pense pas que passer deux fois sur le tableau soit un problème.Considérez le pseudo-code suivant:Bien qu'il n'y ait qu'une seule boucle ici, il y a encore 2 vérifications. (Au lieu d'avoir 2 boucles avec 1 chèque chacune). En réalité, la seule chose que vous économisez est la surcharge d'une boucle. Si les tableaux sont vraiment gros comme vous le dites, cette surcharge est faible par rapport à la charge de travail réelle de la boucle. (Notez que tout cela est implémenté en C, donc les boucles sont de toute façon plus ou moins libres).
EDIT Désolé pour les 4 d'entre vous qui ont voté pour et ont eu confiance en moi. Vous pouvez certainement optimiser cela.
Voici un code fortran qui peut être compilé dans un module python via
f2py
(peut-être qu'unCython
gourou peut venir le comparer avec une version C optimisée ...):Compilez-le via:
Et maintenant, nous sommes dans un endroit où nous pouvons le tester:
Les résultats sont un peu stupéfiants pour moi:
Je dois dire que je ne comprends pas complètement. Comparer juste
np.min
contreminmax1
etminmax2
est toujours une bataille perdue, donc ce n'est pas seulement un problème de mémoire ...notes - L'augmentation de la taille d'un facteur de
10**a
et la diminution de la répétition d'un facteur de10**a
(en gardant la taille du problème constante) modifie les performances, mais pas d'une manière apparemment cohérente, ce qui montre qu'il existe une interaction entre les performances de la mémoire et la surcharge des appels de fonction dans python. Même en comparant unemin
implémentation simple dans fortran bat numpy par un facteur d'environ 2 ...la source
i < minval
est vrai, alorsi > maxval
est toujours faux, vous n'avez donc besoin de faire que 1,5 vérifications par itération en moyenne lorsque la secondeif
est remplacée par unelif
.f2py
qu'il enveloppe simplement Fortran codé à la main pour qu'il puisse être appelé par Python. Un test "plus juste" consiste probablement à coder manuellement C puis à utiliserf2py
(!) Pour l'envelopper pour Python. Si vous autorisez C ++, Shed Skin peut être le point idéal pour équilibrer la facilité de codage avec les performances.Il existe une fonction de recherche (max-min) appelée numpy.ptp si cela vous est utile:
mais je ne pense pas qu'il y ait un moyen de trouver à la fois min et max avec un seul parcours.
EDIT: ptp appelle juste min et max sous le capot
la source
Vous pouvez utiliser Numba , qui est un compilateur Python dynamique prenant en charge NumPy utilisant LLVM. L'implémentation qui en résulte est assez simple et claire:
Il devrait également être plus rapide que l'
min() & max()
implémentation d'un Numpy . Et tout cela sans avoir à écrire une seule ligne de code C / Fortran.Faites vos propres tests de performances, car cela dépend toujours de votre architecture, de vos données, de vos versions de packages ...
la source
numba
fonction une fois avant le test de référence pour vous assurer qu'il est compilé JIT ?. De plus, si vous utilisezipython
, pour plus de simplicité, je vous suggère de l'utiliser%timeit whatever_code()
pour mesurer l'exécution du temps.elif
permet à votre minimum d'être plus grand que votre maximum. Par exemple, avec un tableau de longueur 1, le max sera quelle que soit cette valeur, tandis que min est + infini. Ce n'est pas un gros problème pour un code unique, mais pas un bon code à jeter profondément dans le ventre d'une bête de production.En général, vous pouvez réduire le nombre de comparaisons pour un algorithme minmax en traitant deux éléments à la fois et en comparant uniquement le plus petit au minimum temporaire et le plus grand au maximum temporaire. En moyenne, il suffit de 3/4 des comparaisons qu'une approche naïve.
Cela pourrait être implémenté en c ou fortran (ou tout autre langage de bas niveau) et devrait être presque imbattable en termes de performances. j'utilisenumba pour illustrer le principe et obtenir une implémentation très rapide, indépendante de dtype:
C'est définitivement plus rapide que l'approche naïve présentée par Peque :
Comme prévu, la nouvelle implémentation minmax ne prend qu'environ 3/4 du temps de mise en œuvre naïve (
2.1 / 2.75 = 0.7636363636363637
)la source
Juste pour avoir quelques idées sur les chiffres auxquels on peut s'attendre, étant donné les approches suivantes:
(les
extrema_loop_*()
approches sont similaires à ce qui est proposé ici , tandis que lesextrema_while_*()
approches sont basées sur le code d' ici )Les horaires suivants:
indiquent que les
extrema_while_*()
sont les plus rapides, les plusextrema_while_nb()
rapides Dans tous les cas, les solutionsextrema_loop_nb()
etextrema_loop_cy()
surpassent également l'approche NumPy uniquement (en utilisantnp.max()
etnp.min()
séparément).Enfin, notez qu'aucun de ceux-ci n'est aussi flexible que
np.min()
/np.max()
(en termes de support n-dim,axis
paramètre, etc.).(le code complet est disponible ici )
la source
extrema_while_nb
Personne n'a mentionné numpy.percentile , alors j'ai pensé que je le ferais. Si vous demandez des
[0, 100]
centiles, cela vous donnera un tableau de deux éléments, le min (0e centile) et le max (100e centile).Cependant, cela ne répond pas à l'objectif de l'OP: ce n'est pas plus rapide que min et max séparément. Cela est probablement dû à certaines machines qui permettraient des percentiles non extrêmes (un problème plus difficile, qui devrait prendre plus de temps).
Une future version de Numpy pourrait mettre dans un cas spécial pour ignorer le calcul normal du percentile si seulement
[0, 100]
sont demandés. Sans rien ajouter à l'interface, il existe un moyen de demander à Numpy le minimum et le maximum en un seul appel (contrairement à ce qui a été dit dans la réponse acceptée), mais l'implémentation standard de la bibliothèque ne profite pas de ce cas pour le faire digne d'intérêt.la source
C'est un vieux fil de discussion, mais de toute façon, si quelqu'un regarde à nouveau cela ...
Lors de la recherche simultanée du min et du max, il est possible de réduire le nombre de comparaisons. Si vous comparez des flotteurs (ce que je suppose), cela pourrait vous faire gagner du temps, mais pas en complexité de calcul.
Au lieu de (code Python):
vous pouvez d'abord comparer deux valeurs adjacentes dans le tableau, puis comparer uniquement la plus petite au minimum actuel et la plus grande au maximum actuel:
Le code ici est écrit en Python, clairement pour la vitesse, vous utiliseriez C ou Fortran ou Cython, mais de cette façon, vous faites 3 comparaisons par itération, avec len (ar) / 2 itérations, ce qui donne des comparaisons 3/2 * len (ar). Par opposition à cela, faire la comparaison "de manière évidente" vous faites deux comparaisons par itération, conduisant à des comparaisons 2 * len (ar). Économise 25% du temps de comparaison.
Peut-être que quelqu'un trouvera un jour cela utile.
la source
np.bincount
, voir ici . Il n'utilise pas le truc que vous indiquez, car il s'est avéré être jusqu'à 2 fois plus lent que l'approche naïve. Il existe un lien entre le PR et certains points de référence complets des deux méthodes.À première vue, semble faire l'affaire:
numpy.histogram
... mais si vous regardez la source de cette fonction, elle appelle simplement
a.min()
eta.max()
indépendamment, et ne parvient donc pas à éviter les problèmes de performances abordés dans cette question. :-(De même, cela
scipy.ndimage.measurements.extrema
ressemble à une possibilité, mais il appelle aussi simplementa.min()
eta.max()
indépendamment.la source
np.histogram
ne fonctionne pas toujours pour cela car les(amin, amax)
valeurs renvoyées sont pour les valeurs minimum et maximum du bac. Si j'ai, par exemplea = np.zeros(10)
, desnp.histogram(a, bins=1)
retours(array([10]), array([-0.5, 0.5]))
. Dans ce cas, l'utilisateur recherche(amin, amax)
= (0, 0).Cela valait la peine pour moi de toute façon, donc je proposerai ici la solution la plus difficile et la moins élégante pour quiconque pourrait être intéressé. Ma solution consiste à implémenter un algorithme min-max multithread en un seul passage en C ++ et à l'utiliser pour créer un module d'extension Python. Cet effort nécessite un peu de temps système pour apprendre à utiliser les API Python et NumPy C / C ++, et ici je vais montrer le code et donner quelques petites explications et références pour quiconque souhaite emprunter cette voie.
Multi-threadé Min / Max
Il n'y a rien de trop intéressant ici. Le tableau est divisé en morceaux de taille
length / workers
. Le min / max est calculé pour chaque morceau dans afuture
, qui sont ensuite analysés pour le min / max global.Le module d'extension Python
C'est là que les choses commencent à devenir moche ... Une façon d'utiliser le code C ++ en Python est d'implémenter un module d'extension. Ce module peut être construit et installé à l'aide du
distutils.core
module standard. Une description complète de ce que cela implique est couverte dans la documentation Python: https://docs.python.org/3/extending/extending.html . REMARQUE: il existe certainement d'autres moyens d'obtenir des résultats similaires, pour citer https://docs.python.org/3/extending/index.html#extending-index :Essentiellement, cette voie est probablement plus académique que pratique. Cela étant dit, ce que j'ai fait ensuite, c'était, en m'en tenant assez près du didacticiel, de créer un fichier de module. C'est essentiellement un passe-partout pour que les distutils sachent quoi faire avec votre code et en créer un module Python. Avant de faire quoi que ce soit, il est probablement sage de créer un environnement virtuel Python afin de ne pas polluer vos packages système (voir https://docs.python.org/3/library/venv.html#module-venv ).
Voici le fichier du module:
Dans ce fichier, il y a une utilisation significative de Python ainsi que de l'API NumPy, pour plus d'informations, consultez: https://docs.python.org/3/c-api/arg.html#c.PyArg_ParseTuple , et pour NumPy : https://docs.scipy.org/doc/numpy/reference/c-api.array.html .
Installation du module
La prochaine chose à faire est d'utiliser distutils pour installer le module. Cela nécessite un fichier d'installation:
Pour enfin installer le module, exécutez à
python3 setup.py install
partir de votre environnement virtuel.Test du module
Enfin, nous pouvons tester pour voir si l'implémentation C ++ surpasse réellement l'utilisation naïve de NumPy. Pour ce faire, voici un script de test simple:
Voici les résultats que j'ai obtenus en faisant tout cela:
Celles-ci sont beaucoup moins encourageantes que les résultats indiquent plus tôt dans le fil, qui indiquaient une accélération d'environ 3,5x, et n'intégraient pas le multi-threading. Les résultats que j'ai obtenus sont quelque peu raisonnables, je m'attendrais à ce que la surcharge de threading et domine le temps jusqu'à ce que les tableaux deviennent très grands, auquel point l'augmentation des performances commencerait à se rapprocher de
std::thread::hardware_concurrency
x augmenter.Conclusion
Il y a certainement de la place pour des optimisations spécifiques aux applications pour certains codes NumPy, semble-t-il, en particulier en ce qui concerne le multi-threading. Je ne sais pas si cela en vaut la peine ou non, mais cela semble certainement être un bon exercice (ou quelque chose du genre). Je pense que peut-être apprendre certains de ces "outils tiers" comme Cython peut être une meilleure utilisation du temps, mais qui sait.
la source
v = min_max_it->get();
. Laget
méthode se bloque jusqu'à ce que le résultat soit prêt et le renvoie. Étant donné que la boucle traverse chaque avenir, elle ne se terminera pas tant qu'ils ne seront pas tous terminés. future.get ()Le moyen le plus court que j'ai trouvé est le suivant:
Mais comme il trie le tableau, ce n'est pas le plus efficace.
Un autre chemin court serait:
Cela devrait être plus efficace, mais le résultat est calculé et un flottant est renvoyé.
la source