Existe-t-il une fonction SciPy ou une fonction NumPy ou un module pour Python qui calcule la moyenne de fonctionnement d'un tableau 1D dans une fenêtre spécifique?
python
numpy
scipy
moving-average
Shejo284
la source
la source
UPD: des solutions plus efficaces ont été proposées par Alleo et jasaarim .
Vous pouvez utiliser
np.convolve
pour cela:Explication
La moyenne courante est un cas de l'opération mathématique de convolution . Pour la moyenne courante, vous faites glisser une fenêtre le long de l'entrée et calculez la moyenne du contenu de la fenêtre. Pour les signaux 1D discrets, la convolution est la même chose, sauf qu'au lieu de la moyenne, vous calculez une combinaison linéaire arbitraire, c'est-à-dire multipliez chaque élément par un coefficient correspondant et additionnez les résultats. Ces coefficients, un pour chaque position dans la fenêtre, sont parfois appelés le noyau de convolution . Maintenant, la moyenne arithmétique de N valeurs est
(x_1 + x_2 + ... + x_N) / N
, donc le noyau correspondant est(1/N, 1/N, ..., 1/N)
, et c'est exactement ce que nous obtenons en utilisantnp.ones((N,))/N
.Bords
L'
mode
argument denp.convolve
spécifie comment gérer les arêtes. J'ai choisi levalid
mode ici parce que je pense que c'est ainsi que la plupart des gens s'attendent à ce que la course à pied fonctionne, mais vous pouvez avoir d'autres priorités. Voici un graphique qui illustre la différence entre les modes:la source
numpy.cumsum
est plus complexe.Solution efficace
La convolution est bien meilleure qu'une approche simple, mais (je suppose) qu'elle utilise FFT et donc assez lente. Cependant, spécialement pour le calcul du fonctionnement, l'approche suivante fonctionne bien
Le code à vérifier
Notez que
numpy.allclose(result1, result2)
estTrue
, deux méthodes sont équivalentes. Plus N est grand, plus la différence de temps est grande.avertissement: bien que cumsum soit plus rapide, il y aura une augmentation des erreurs en virgule flottante qui peuvent rendre vos résultats invalides / incorrects / inacceptables
les commentaires ont souligné ce problème d'erreur en virgule flottante ici, mais je le rends plus évident ici dans la réponse. .
np.longdouble
mais votre erreur en virgule flottante deviendra toujours significative pour un nombre relativement important de points (environ> 1e5 mais dépend de vos données)la source
numpy.convolve
O (mn); sa documentation mentionne quiscipy.signal.fftconvolve
utilise FFT.running_mean([1,2,3], 2)
donnearray([1, 2])
. Remplacerx
par[float(value) for value in x]
fait l'affaire.x
contient des flottants. Exemple:running_mean(np.arange(int(1e7))[::-1] + 0.2, 1)[-1] - 0.2
retourne0.003125
pendant que l'on attend0.0
. Plus d'informations: en.wikipedia.org/wiki/Loss_of_significanceMise à jour: l'exemple ci-dessous montre l'ancienne
pandas.rolling_mean
fonction qui a été supprimée dans les versions récentes de pandas. Un équivalent moderne de l'appel de fonction ci-dessous seraitpandas est plus approprié pour cela que NumPy ou SciPy. Sa fonction rolling_mean fait le travail de manière pratique. Il renvoie également un tableau NumPy lorsque l'entrée est un tableau.
Il est difficile de battre les
rolling_mean
performances avec une implémentation Python pure personnalisée. Voici un exemple de performance par rapport à deux des solutions proposées:Il existe également de belles options pour gérer les valeurs de bord.
la source
df.rolling(windowsize).mean()
fonctionne maintenant à la place (très rapidement je pourrais ajouter). pour 6000 lignes, la série a%timeit test1.rolling(20).mean()
renvoyé 1000 boucles, le meilleur de 3: 1,16 ms par boucledf.rolling()
fonctionne assez bien, le problème est que même ce formulaire ne supportera plus les ndarrays à l'avenir. Pour l'utiliser, nous devrons d'abord charger nos données dans un Pandas Dataframe. J'aimerais voir cette fonction ajoutée à l'unnumpy
ou l' autrescipy.signal
.%timeit bottleneck.move_mean(x, N)
est 3 à 15 fois plus rapide que les méthodes Cumsum et Pandas sur mon PC. Jetez un œil à leur indice de référence dans le README du repo .Vous pouvez calculer une moyenne courante avec:
Mais c'est lent.
Heureusement, numpy inclut une fonction de convolution que nous pouvons utiliser pour accélérer les choses. La moyenne courante équivaut à une convolution
x
avec un vecteurN
long, avec tous les membres égaux à1/N
. L'implémentation numpy de convolve inclut le transitoire de départ, vous devez donc supprimer les N-1 premiers points:Sur ma machine, la version rapide est 20 à 30 fois plus rapide, en fonction de la longueur du vecteur d'entrée et de la taille de la fenêtre de calcul de la moyenne.
Notez que convolve inclut un
'same'
mode qui semble devoir résoudre le problème transitoire de départ, mais il le sépare entre le début et la fin.la source
mode='valid'
dans ceconvolve
qui ne nécessite pas de post-traitement.mode='valid'
supprime le transitoire des deux extrémités, non? Silen(x)=10
etN=4
, pour une moyenne courante, je voudrais 10 résultats maisvalid
renvoie 7.modes = ('full', 'same', 'valid'); [plot(convolve(ones((200,)), ones((50,))/50, mode=m)) for m in modes]; axis([-10, 251, -.1, 1.1]); legend(modes, loc='lower center')
(avec pyplot et numpy importés).runningMean
Ai-je un effet secondaire de la moyenne avec des zéros, lorsque vous sortez du tableau avecx[ctr:(ctr+N)]
pour le côté droit du tableau.runningMeanFast
ont également ce problème d'effet de frontière.dans mes tests sur Tradewave.net TA-lib gagne toujours:
résultats:
la source
NameError: name 'info' is not defined
. J'obtiens cette erreur, monsieur.Pour une solution prête à l'emploi, voir https://scipy-cookbook.readthedocs.io/items/SignalSmooth.html . Il fournit une moyenne mobile avec le
flat
type de fenêtre. Notez que c'est un peu plus sophistiqué que la simple méthode de convolution à faire soi-même, car elle tente de gérer les problèmes au début et à la fin des données en les reflétant (ce qui peut ou non fonctionner dans votre cas. ..).Pour commencer, vous pouvez essayer:
la source
numpy.convolve
la différence uniquement en modifiant la séquence.w
la taille de la fenêtre ets
les données?Vous pouvez utiliser scipy.ndimage.filters.uniform_filter1d :
uniform_filter1d
:'reflect'
est la valeur par défaut, mais dans mon cas, je voulais plutôt'nearest'
C'est aussi assez rapide (près de 50 fois plus rapide
np.convolve
et 2 à 5 fois plus rapide que l'approche cumsum donnée ci-dessus ):voici 3 fonctions qui vous permettent de comparer l'erreur / la vitesse de différentes implémentations:
la source
uniform_filter1d
,np.convolve
avec un rectangle, etnp.cumsum
suivi denp.subtract
. mes résultats: (1.) convolve est le plus lent. (2.) cumsum / soustraire est environ 20-30x plus rapide. (3.) uniform_filter1d est environ 2-3 fois plus rapide que cumsum / soustract. le gagnant est définitivement uniform_filter1d.uniform_filter1d
est plus rapide que lacumsum
solution (d'environ 2-5x). etuniform_filter1d
n'obtient pas d'erreur de virgule flottante massive comme lecumsum
fait la solution.Je sais que c'est une vieille question, mais voici une solution qui n'utilise aucune structure de données ou bibliothèque supplémentaire. Il est linéaire dans le nombre d'éléments de la liste d'entrée et je ne peux penser à aucun autre moyen de le rendre plus efficace (en fait, si quelqu'un connaît une meilleure façon d'allouer le résultat, faites-le moi savoir).
REMARQUE: ce serait beaucoup plus rapide en utilisant un tableau numpy au lieu d'une liste, mais je voulais éliminer toutes les dépendances. Il serait également possible d'améliorer les performances par une exécution multi-thread
La fonction suppose que la liste d'entrée est unidimensionnelle, soyez donc prudent.
Exemple
Supposons que nous ayons une liste
data = [ 1, 2, 3, 4, 5, 6 ]
sur laquelle nous voulons calculer une moyenne glissante avec une période de 3, et que vous souhaitiez également une liste de sortie de la même taille que celle d'entrée (c'est le plus souvent le cas).Le premier élément a l'index 0, donc la moyenne mobile doit être calculée sur les éléments d'index -2, -1 et 0. Évidemment, nous n'avons pas de données [-2] et de données [-1] (sauf si vous souhaitez utiliser conditions aux limites), donc nous supposons que ces éléments sont 0. Cela équivaut à un remplissage à zéro de la liste, sauf que nous ne la remplissons pas réellement, gardons simplement une trace des indices qui nécessitent un remplissage (de 0 à N-1).
Donc, pour les N premiers éléments, nous continuons simplement à additionner les éléments dans un accumulateur.
A partir des éléments N + 1 vers l'avant, l'accumulation simple ne fonctionne pas. nous attendons
result[3] = (2 + 3 + 4)/3 = 3
mais c'est différent de(sum + 4)/3 = 3.333
.La façon de calculer la valeur correcte est de soustraire
data[0] = 1
desum+4
, donnant ainsisum + 4 - 1 = 9
.Cela se produit parce qu'actuellement
sum = data[0] + data[1] + data[2]
, mais c'est aussi vrai pour touti >= N
parce que, avant la soustraction,sum
c'estdata[i-N] + ... + data[i-2] + data[i-1]
.la source
Je pense que cela peut être résolu avec élégance en utilisant un goulot d'étranglement
Voir l'exemple de base ci-dessous:
"mm" est la moyenne mobile de "a".
"window" est le nombre maximum d'entrées à considérer pour la moyenne mobile.
"min_count" est le nombre minimal d'entrées à considérer pour la moyenne mobile (par exemple pour les premiers éléments ou si le tableau a des valeurs nan).
La bonne partie est que Bottleneck aide à gérer les valeurs nanométriques et il est également très efficace.
la source
Je n'ai pas encore vérifié à quelle vitesse c'est, mais vous pouvez essayer:
la source
Cette réponse contient des solutions utilisant la bibliothèque standard Python pour trois scénarios différents.
Moyenne mobile avec
itertools.accumulate
Il s'agit d'une solution Python 3.2+ efficace en termes de mémoire calculant la moyenne mobile sur un itérable de valeurs en tirant parti
itertools.accumulate
.Notez qu'il
values
peut être tout itérable, y compris les générateurs ou tout autre objet qui produit des valeurs à la volée.Tout d'abord, construisez paresseusement la somme cumulée des valeurs.
Ensuite,
enumerate
la somme cumulée (commençant à 1) et construisez un générateur qui donne la fraction des valeurs accumulées et l'indice d'énumération actuel.Vous pouvez émettre
means = list(rolling_avg)
si vous avez besoin de toutes les valeurs en mémoire à la fois ou appeler de manièrenext
incrémentielle.(Bien sûr, vous pouvez également effectuer une itération
rolling_avg
avec unefor
boucle, qui appelleranext
implicitement.)Cette solution peut être écrite comme une fonction comme suit.
Une coroutine à laquelle vous pouvez envoyer des valeurs à tout moment
Cette coroutine consomme les valeurs que vous lui envoyez et conserve une moyenne courante des valeurs vues jusqu'à présent.
Il est utile lorsque vous n'avez pas d'itérables de valeurs, mais que vous acquérez les valeurs à faire la moyenne une par une à différents moments de la vie de votre programme.
La coroutine fonctionne comme ceci:
Calcul de la moyenne sur une fenêtre glissante de taille
N
Cette fonction de générateur prend une taille de fenêtre itérable et de fenêtre
N
et donne la moyenne sur les valeurs actuelles à l'intérieur de la fenêtre. Il utilise adeque
, qui est une structure de données similaire à une liste, mais optimisée pour des modifications rapides (pop
,append
) aux deux points de terminaison .Voici la fonction en action:
la source
Un peu tard à la fête, mais j'ai créé ma propre petite fonction qui ne s'enroule PAS autour des extrémités ou des pads avec des zéros qui sont ensuite utilisés pour trouver la moyenne également. Un autre traitement est qu'il rééchantillonne également le signal en des points linéairement espacés. Personnalisez le code à volonté pour obtenir d'autres fonctionnalités.
La méthode est une simple multiplication matricielle avec un noyau gaussien normalisé.
Une utilisation simple sur un signal sinusoïdal avec un bruit distribué normal ajouté:
la source
sum
, utilisant à lanp.sum
place 2 L'@
opérateur (aucune idée de ce que c'est) renvoie une erreur. Je vais peut-être l'examiner plus tard, mais je manque de temps pour le moment@
est l'opérateur de multiplication matricielle qui implémente np.matmul . Vérifiez si votrey_in
tableau est un tableau numpy, cela pourrait être le problème.Au lieu de numpy ou scipy, je recommanderais aux pandas de le faire plus rapidement:
Cela prend la moyenne mobile (MA) de 3 périodes de la colonne «données». Vous pouvez également calculer les versions décalées, par exemple celle qui exclut la cellule actuelle (décalée une vers l'arrière) peut être calculée facilement comme:
la source
pandas.rolling_mean
alors que la mine utilisepandas.DataFrame.rolling
. Vous pouvez également calculer facilement le déplacement,min(), max(), sum()
etc. ainsimean()
qu'avec cette méthode.pandas.rolling_min, pandas.rolling_max
etc. Ils sont similaires mais différents.Il y a un commentaire de mab enfoui dans l'une des réponses ci-dessus qui a cette méthode.
bottleneck
amove_mean
qui est une moyenne mobile simple:min_count
est un paramètre pratique qui prendra essentiellement la moyenne mobile jusqu'à ce point dans votre tableau. Si vous ne définissez pasmin_count
, il sera égalwindow
, et tout jusqu'àwindow
points le seranan
.la source
Une autre approche pour trouver la moyenne mobile sans utiliser numpy, panda
imprimera [2.0, 4.0, 6.0, 6.5, 7.4, 7.833333333333333]
la source
Cette question est maintenant encore plus ancienne que lorsque NeXuS en a parlé le mois dernier, MAIS j'aime la façon dont son code traite les cas extrêmes. Cependant, comme il s'agit d'une «moyenne mobile simple», ses résultats sont en retard par rapport aux données auxquelles ils s'appliquent. Je pensais que le traitement des cas de pointe de manière plus satisfaisante que les modes de numpy
valid
,same
etfull
pourrait être obtenue en appliquant une approche similaire à uneconvolution()
méthode basée.Ma contribution utilise une moyenne mobile centrale pour aligner ses résultats avec leurs données. Lorsqu'il y a trop peu de points disponibles pour que la fenêtre pleine taille puisse être utilisée, les moyennes courantes sont calculées à partir de fenêtres successivement plus petites aux bords du tableau. [En fait, à partir de fenêtres successivement plus grandes, mais c'est un détail d'implémentation.]
C'est relativement lent car il utilise
convolve()
et pourrait probablement être beaucoup amélioré par un vrai Pythonista, cependant, je pense que l'idée tient.la source
Il existe de nombreuses réponses ci-dessus sur le calcul d'une moyenne courante. Ma réponse ajoute deux fonctionnalités supplémentaires:
Cette deuxième caractéristique est particulièrement utile pour déterminer les valeurs qui diffèrent d'un certain montant de la tendance générale.
J'utilise numpy.cumsum car c'est la méthode la plus rapide ( voir la réponse d'Alleo ci-dessus ).
Ce code fonctionne uniquement pour les N pairs. Il peut être ajusté pour les nombres impairs en changeant le np.insert de padded_x et n_nan.
Exemple de sortie (raw en noir, movavg en bleu):
Ce code peut être facilement adapté pour supprimer toutes les valeurs de moyenne mobile calculées à partir de moins de cutoff = 3 valeurs non nan.
la source
Utiliser uniquement la bibliothèque standard Python (mémoire efficace)
Donnez simplement une autre version de l'utilisation de la bibliothèque standard
deque
uniquement. Je suis assez surpris que la plupart des réponses utilisentpandas
ounumpy
.En fait, j'ai trouvé une autre implémentation dans les documents python
Cependant, la mise en œuvre me semble un peu plus complexe qu'elle ne devrait l'être. Mais il doit être dans la documentation python standard pour une raison, quelqu'un pourrait-il commenter l'implémentation de la mienne et la documentation standard?
la source
O(n*d)
calculs (d
étant la taille de la fenêtre, lan
taille de l'itérable) et ils le fontO(n)
Avec les variables de @ Aikude, j'ai écrit une ligne.
la source
Bien qu'il existe des solutions à cette question ici, veuillez jeter un coup d'œil à ma solution. C'est très simple et fonctionne bien.
la source
En lisant les autres réponses, je ne pense pas que ce soit ce que la question demandait, mais je suis arrivé ici avec le besoin de garder une moyenne mobile d'une liste de valeurs qui augmentait en taille.
Donc, si vous souhaitez conserver une liste de valeurs que vous acquérez quelque part (un site, un appareil de mesure, etc.) et la moyenne des dernières
n
valeurs mises à jour, vous pouvez utiliser le code ci-dessous, qui minimise l'effort d'ajout de nouvelles éléments:Et vous pouvez le tester avec, par exemple:
Qui donne:
la source
Une autre solution utilisant simplement une bibliothèque standard et deque:
la source
À des fins éducatives, permettez-moi d'ajouter deux autres solutions Numpy (qui sont plus lentes que la solution cumsum):
Fonctions utilisées: as_strided , add.reduceat
la source
Toutes les solutions susmentionnées sont médiocres car elles manquent
numpy.cumsum
, ouO(len(x) * w)
implémentations sous forme de convolutions.Donné
Notez que
x_[:w].sum()
égalex[:w-1].sum()
. Donc, pour la première moyenne, l'numpy.cumsum(...)
additionx[w] / w
(viax_[w+1] / w
) et la soustraction0
(dex_[0] / w
). Cela se traduit parx[0:w].mean()
Via cumsum, vous mettrez à jour la deuxième moyenne en ajoutant
x[w+1] / w
et en soustrayant en plusx[0] / w
, ce qui donnex[1:w+1].mean()
.Cela continue jusqu'à ce qu'il
x[-w:].mean()
soit atteint.Cette solution est vectorisée
O(m)
, lisible et numériquement stable.la source
Que diriez-vous d' un filtre de moyenne mobile ? C'est aussi un one-liner et a l'avantage, que vous pouvez facilement manipuler le type de fenêtre si vous avez besoin d'autre chose que le rectangle, ie. une moyenne mobile simple de N-long d'un tableau a:
Et avec la fenêtre triangulaire appliquée:
Remarque: Je jette généralement les N premiers échantillons comme faux donc
[N:]
à la fin, mais ce n'est pas nécessaire et la question d'un choix personnel uniquement.la source
Si vous choisissez de lancer le vôtre, plutôt que d'utiliser une bibliothèque existante, soyez conscient de l'erreur de virgule flottante et essayez de minimiser ses effets:
Si toutes vos valeurs sont à peu près du même ordre de grandeur, cela aidera à préserver la précision en ajoutant toujours des valeurs de magnitudes à peu près similaires.
la source