160 votes

Comment calculer une moyenne mobile sans conserver le compte et le total des données ?

J'essaie de trouver un moyen de calculer une moyenne cumulative mobile sans stocker le compte et le total des données reçues jusqu'à présent.

J'ai trouvé deux algorithmes mais tous deux ont besoin de stocker le compte :

  • nouvelle moyenne = ((ancien compte * anciennes données) + prochaines données) / prochain compte
  • nouvelle moyenne = ancienne moyenne + (prochaine donnée - ancienne moyenne) / prochain compte

Le problème avec ces méthodes est que le compte devient de plus en plus grand, ce qui entraîne une perte de précision dans la moyenne obtenue.

La première méthode utilise l'ancien compte et le compte suivant qui sont évidemment séparés de 1. Cela m'a fait penser qu'il y avait peut-être un moyen de supprimer le compte, mais malheureusement je ne l'ai pas encore trouvé. Cela m'a tout de même permis d'aller un peu plus loin et d'obtenir la deuxième méthode, mais le compte est toujours présent.

Est-ce possible, ou est-ce que je cherche juste l'impossible ?

104voto

Muis Points 1827

Vous pouvez simplement le faire :

double approxRollingAverage (double avg, double new_sample) {

    avg -= avg / N;
    avg += new_sample / N;

    return avg;
}

N est le nombre d'échantillons sur lesquels vous voulez faire la moyenne. Notez que cette approximation est équivalente à une moyenne mobile exponentielle. Voir : Calculer la moyenne mobile en C++

94voto

Abdullah Al-Ageel Points 1033
New average = old average * (n-1)/n + new value /n

Ceci en supposant que le compte n'a changé que d'une seule valeur. Dans le cas où il est modifié par M valeurs alors :

new average = old average * (n-len(M))/n + (sum of values in M)/n).

Voici la formule mathématique (je crois que c'est la plus efficace), je pense que vous pouvez faire le reste du code par vous-mêmes.

38voto

Flip Points 498

En un blog sur l'exécution des calculs de la variance de l'échantillon, où la moyenne est également calculée à l'aide de La méthode de Welford :

enter image description here

Dommage que nous ne puissions pas télécharger des images SVG.

33voto

antak Points 2202

Voici encore une autre réponse qui commente comment Muis , Abdullah Al-Ageel y Flip La réponse de la Commission est c'est mathématiquement la même chose sauf qu'il est écrit différemment.

Bien sûr, nous avons José Manuel Ramos L'analyse de l'auteur explique comment les erreurs d'arrondi affectent chacune d'entre elles de manière légèrement différente, mais cela dépend de la mise en œuvre et changerait en fonction de la manière dont chaque réponse est appliquée au code.

Il y a cependant une différence assez importante

C'est dans Muis 's N , Flip 's k y Abdullah Al-Ageel 's n . Abdullah Al-Ageel n'explique pas vraiment ce que n devrait être, mais N y k diffèrent en ce que N est " le nombre d'échantillons où vous voulez faire la moyenne sur " tandis que k est le nombre de valeurs échantillonnées. (Bien que je doute que l'appel à N le nombre d'échantillons est exacte).

Et nous en arrivons à la réponse ci-dessous. C'est essentiellement la même vieille moyenne mobile pondérée exponentielle que les autres, donc si vous cherchiez une alternative, arrêtez-vous ici.

Moyenne mobile pondérée exponentielle

Initialement :

average = 0
counter = 0

Pour chaque valeur :

counter += 1
average = average + (value - average) / min(counter, FACTOR)

La différence est que min(counter, FACTOR) partie. C'est la même chose que de dire min(Flip's k, Muis's N) .

FACTOR est une constante qui affecte la vitesse à laquelle la moyenne "rattrape" la dernière tendance. Plus le nombre est petit, plus la vitesse est grande. (A 1 ce n'est plus une moyenne et devient simplement la dernière valeur).

Cette réponse nécessite le compteur de fonctionnement counter . Si cela pose problème, le min(counter, FACTOR) peut être remplacé par un simple FACTOR en le transformant en Muis Réponse de la Commission. Le problème, c'est que la moyenne mobile est affectée par ce que vous voulez. average est initialisé à . S'il a été initialisé à 0 ce zéro peut prendre beaucoup de temps pour sortir de la moyenne.

Comment cela se présente-t-il ?

Exponential moving average

13voto

La réponse de Flip est computationnellement plus cohérente que celle de Muis.

En utilisant le format des nombres doubles, on peut voir le problème d'arrondi dans l'approche de Muis :

The Muis approach

Lorsque vous divisez et soustrayez, un arrondi apparaît dans la valeur enregistrée précédente, la modifiant.

Cependant, l'approche Flip préserve la valeur stockée et réduit le nombre de divisions, ce qui réduit l'arrondi et minimise l'erreur propagée à la valeur stockée. L'ajout n'entraîne des arrondis que s'il y a quelque chose à ajouter (lorsque N est grand, il n'y a rien à ajouter).

The Flip approach

Ces changements sont remarquables lorsque vous faites tendre la moyenne de grandes valeurs vers zéro.

Je vous montre les résultats à l'aide d'un tableur :

Tout d'abord, les résultats obtenus : Results

Les colonnes A et B sont les valeurs n et X_n, respectivement.

La colonne C correspond à l'approche Flip, et la colonne D à l'approche Muis, le résultat étant stocké dans la moyenne. La colonne E correspond à la valeur moyenne utilisée dans le calcul.

Un graphique montrant la moyenne des valeurs paires est le suivant :

Graph

Comme vous pouvez le constater, il existe de grandes différences entre les deux approches.

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