Algorithme de moyenne mobile efficace<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />Bonjour,
Merci à ceux qui ont posté ici avant, j'ai trouvé des réponses utiles!
J'ai plusieurs grands ensembles de données (20 millions de points chacun !) provenant d'un oscilloscope. Je veux calculer une moyenne mobile. Cependant, l'algorithme que j'ai conçu est très lent. Voici le code de ma formule :
"
Si(Row() < 100, Empty(), Summation(i = Row() - 99, Row(), :Current[i,Empty()]) / 100)
"

Au fur et à mesure que la largeur de la fenêtre de calcul de la moyenne (dans ce cas 100 échantillons) augmente (disons 500, 1 000 ou même 50 000 ou plus), le temps de calcul devient déraisonnable. Quelqu'un a-t-il un moyen plus rapide de faire une moyenne mobile?
Merci!
Danois
Re : Algorithme de moyenne mobile efficaceJe trouve généralement que si je peux éviter la table tous ensemble, c'est plus rapide. Cela tourne plus vite sur ma machine. Vous remarquerez que Mes chiffres sont également différents de Jeffs. C'est simplement parce que je reflète les chiffres à la fin, puis que je vais x points à partir du centre (plutôt que les derniers x points). Cela a également une pondération des valeurs manquantes, donc si vous avez des valeurs manquantes, ce n'est pas un problème. Au moins je le pense. Si vous n'avez pas besoin d'une colonne à la fin, c'est encore plus rapide. Et comme il s'agit essentiellement d'une matrice d'index concaténée, c'est assez rapide.
Names Default to here(1);
dt = open("$SAMPLE_DATA\Big Class.jmp");
st1 = HPtime();
mat = :height << Get Values;
//number of rows you want to average
x= 5;
//+- gap
z = (x-1)/2;
//get the rows
rows = 1::nrows(mat);
//mirror the rows on the front and back
mirrored = ((z+1)::2)||rows||((ncol(rows)-1)::(ncol(rows)-z));
big_mat = [];
for(i=0, i<=x-1, i++,
//this is cutting up the longer mirrored vector into x vectors of the same length as the original
big_mat ||= mat[mirrored[rows+i]];
);
//now we can just take a VSum of the transpose /x to get our average
Squasher = !ismissing(big_mat); //everywhere there's a non-missing, make 1
Squasher = VSum(Squasher`); //count real numbers, this is to not weight non-missings
A = Vsum(big_mat`)/x;
New Column("Moving Average", Set Values(A)); //If you don't need this column
//and you're just doing matrix operations this is actually about half the total time
en1 = HPTime()-st1;
// Data Table method
st2 = HPTime();
dt<< New Column("moving sum(x)", formula(If(Row()<=:Moving Avg Window,
If( Row() == 1,
:height,
Lag( :Name( "moving sum(x)" ), 1 ) + :height
),
(Lag( :Name( "moving sum(x)" ), 1 ) + :height) -
Lag( :height, :Moving Avg Window )
)));
dt<< New Column("Moving Avg", Formula(
If(Row()>=:Moving Avg Window,
:Name( "moving sum(x)" ) / :Moving Avg Window,
.
)));
en2 = HPTime()-st2;
show(en1, en2);Re : Algorithme de moyenne mobile efficaceC'est une excellente solution. Notez que toutes les valeurs manquantes dans la colonne de données ("current" dans cet exemple) créeront des valeurs vides dans la colonne de données calculées à partir de la ligne où se trouvent les premières données manquantes dans toutes les lignes ci-dessous. C'est un petit problème dont il faut être conscient.Re : Algorithme de moyenne mobile efficaceMerci Jeff. Celles-ci s'exécutent beaucoup plus rapidement que la méthode de sommation !
Re : Algorithme de moyenne mobile efficaceJe peux proposer quelques stratégies ici.
Je pense que les deux sont plus efficaces que l'opérateur Summation() que vous utilisez actuellement. Je ne peux pas dire qu'ils soient les plus efficaces possibles.
1) Utilisez la fonction Sum() sur une matrice.
If(Row()<:Moving Avg Window,
.,
Sum( :Current[Index( Row() - (:Moving Avg Window - 1), Row() )] ) / :Moving Avg Window
)

Ceci profite de la vitesse de l'opération matricielle. L'indice du Actuel colonne dans cette formule est une matrice des numéros de ligne depuis le début de la fenêtre de moyenne mobile jusqu'au numéro de ligne actuel. Mon indice de la colonne de cette façon JMP renvoie une matrice et la fonction Sum est relativement efficace sur une matrice.
Un moyen encore plus rapide consiste à calculer cette moyenne mobile sur deux colonnes. La première colonne maintient simplement une somme mobile en ajoutant la valeur de Actuel dans une ligne à la valeur de la somme glissante dans la ligne précédente, puis en soustrayant la valeur de Current de la ligne au début de la fenêtre.
Voici la formule de la somme mobile.
If(Row()<=:Moving Avg Window,
If( Row() == 1,
:Current,
Lag( :Name( "moving sum(x)" ), 1 ) + :Current
),
(Lag( :Name( "moving sum(x)" ), 1 ) + :Current) -
Lag( :Current, :Moving Avg Window )
)

Ensuite, il est facile d'avoir une colonne qui divise Somme mobile par le Fenêtre moyenne mobile .
If(Row()>=:Moving Avg Window,
:Name( "moving sum(x)" ) / :Moving Avg Window,
.
)

Je joins un tableau de données qui montre ces deux méthodes. Dans le tableau de données, le Fenêtre moyenne mobile est un Variable de tableau de données .
Pour voir l'efficacité de la deuxième méthode dans ce tableau de données, vous voudrez peut-être supprimer l'évaluation de la de la Moyenne mobile(x) colonne en premier.
-Jeff