En ce qui concerne cette réponse , existe-t-il un moyen rapide de calculer les médianes sur un tableau qui a des groupes avec un nombre inégal d'éléments?
Par exemple:
data = [1.00, 1.05, 1.30, 1.20, 1.06, 1.54, 1.33, 1.87, 1.67, ... ]
index = [0, 0, 1, 1, 1, 1, 2, 3, 3, ... ]
Et puis je veux calculer la différence entre le nombre et la médiane par groupe (par exemple, la médiane du groupe 0
est 1.025
donc le premier résultat est 1.00 - 1.025 = -0.025
). Donc, pour le tableau ci-dessus, les résultats apparaissent comme:
result = [-0.025, 0.025, 0.05, -0.05, -0.19, 0.29, 0.00, 0.10, -0.10, ...]
Puisqu'il np.median.reduceat
n'existe pas (encore), existe-t-il un autre moyen rapide d'y parvenir? Mon tableau contiendra des millions de lignes, la vitesse est donc cruciale!
Les indices peuvent être supposés contigus et ordonnés (il est facile de les transformer s'ils ne le sont pas).
Exemples de données pour les comparaisons de performances:
import numpy as np
np.random.seed(0)
rows = 10000
cols = 500
ngroup = 100
# Create random data and groups (unique per column)
data = np.random.rand(rows,cols)
groups = np.random.randint(ngroup, size=(rows,cols)) + 10*np.tile(np.arange(cols),(rows,1))
# Flatten
data = data.ravel()
groups = groups.ravel()
# Sort by group
idx_sort = groups.argsort()
data = data[idx_sort]
groups = groups[idx_sort]
python
performance
numpy
median
numpy-ufunc
Jean Paul
la source
la source
scipy.ndimage.median
suggestion dans la réponse liée? Il ne me semble pas qu'il ait besoin d'un nombre égal d'éléments par étiquette. Ou ai-je raté quelque chose?Réponses:
Parfois, vous devez écrire du code numpy non idiomatique si vous voulez vraiment accélérer votre calcul, ce que vous ne pouvez pas faire avec numpy natif.
numba
compile votre code python en bas niveau C. Étant donné que beaucoup de numpy lui-même est généralement aussi rapide que C, cela finit surtout par être utile si votre problème ne se prête pas à la vectorisation native avec numpy. Ceci est un exemple (où j'ai supposé que les indices sont contigus et triés, ce qui se reflète également dans les données d'exemple):Et voici quelques synchronisations utilisant la
%timeit
magie d'IPython :En utilisant les données d'exemple mises à jour dans la question, ces nombres (c'est-à-dire le temps d'exécution de la fonction python par rapport au temps d'exécution de la fonction accélérée JIT) sont
Cela équivaut à une accélération de 65x dans le petit cas et une accélération de 26x dans le plus grand cas (par rapport au code en boucle lente, bien sûr) en utilisant le code accéléré. Un autre avantage est que (contrairement à la vectorisation typique avec numpy natif), nous n'avions pas besoin de mémoire supplémentaire pour atteindre cette vitesse, il s'agit de code de bas niveau optimisé et compilé qui finit par être exécuté.
La fonction ci-dessus suppose que les tableaux numpy int sont
int64
par défaut, ce qui n'est pas réellement le cas sous Windows. Une alternative consiste donc à supprimer la signature de l'appel ànumba.njit
, déclenchant une compilation juste à temps appropriée. Mais cela signifie que la fonction sera compilée lors de la première exécution, ce qui peut interférer avec les résultats de synchronisation (nous pouvons soit exécuter la fonction une fois manuellement, en utilisant des types de données représentatifs, soit simplement accepter que la première exécution de synchronisation soit beaucoup plus lente, ce qui devrait Etre ignoré). C'est exactement ce que j'ai essayé d'empêcher en spécifiant une signature, ce qui déclenche une compilation anticipée.Quoi qu'il en soit, dans le cas JIT correctement, le décorateur dont nous avons besoin est juste
Notez que les timings ci-dessus que j'ai montrés pour la fonction compilée jit ne s'appliquent qu'une fois la fonction compilée. Cela se produit soit lors de la définition (avec une compilation désirée, lorsqu'une signature explicite est passée à
numba.njit
), soit pendant le premier appel de fonction (avec une compilation différée, lorsqu'aucune signature n'est transmise ànumba.njit
). Si la fonction ne doit être exécutée qu'une seule fois, le temps de compilation doit également être pris en compte pour la vitesse de cette méthode. Cela ne vaut généralement la peine de compiler des fonctions que si le temps total de compilation + exécution est inférieur au temps d'exécution non compilé (ce qui est en fait vrai dans le cas ci-dessus, où la fonction python native est très lente). Cela se produit principalement lorsque vous appelez souvent votre fonction compilée.Comme le note max9111 dans un commentaire, une des caractéristiques importantes de
numba
est lecache
mot - clé tojit
. Passercache=True
ànumba.jit
stockera la fonction compilée sur le disque, de sorte que lors de la prochaine exécution du module python donné, la fonction sera chargée à partir de là plutôt que recompilée, ce qui peut encore vous épargner l'exécution à long terme.la source
index
données de roganjosh . Je vais laisser une note à ce sujet, merci :)cache=True
pour éviter la recompilation à chaque redémarrage de l'interpréteur.Une approche consisterait à utiliser
Pandas
ici uniquement pour faire usage degroupby
. J'ai un peu gonflé les tailles d'entrée pour donner une meilleure compréhension des timings (car il y a des frais généraux dans la création du DF).Donne ce qui suit
timeit
:Pour la même taille d'échantillon, j'obtiens le considère que l'approche dict d'Aryerez est:
Cependant, si nous augmentons les entrées d'un autre facteur de 10, les synchronisations deviennent:
Cependant, au détriment d'une certaine réactivité, la réponse de Divakar utilisant le numpy pur se présente comme suit:
À la lumière du nouvel ensemble de données (qui aurait vraiment dû être défini au début):
la source
Peut-être que vous l'avez déjà fait, mais sinon, voyez si c'est assez rapide:
Production:
la source
np.vectorize
c'est un wrapper très fin pour une boucle, donc je ne m'attendrais pas à ce que cette approche soit particulièrement rapide.data
etindex
aussinp.array
s que dans la question.Voici une approche basée sur NumPy pour obtenir la médiane groupée pour les valeurs de bacs / index positives -
Pour résoudre notre cas spécifique de soustraits -
la source
df.groupby('index').transform('median')
?