Effizienter gleitender Durchschnittsalgorithmus<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />Hallo,
Danke an diejenigen, die hier schon einmal gepostet haben, ich habe einige nützliche Antworten gefunden!
Ich habe mehrere große Datensätze (jeweils 20 Millionen Punkte!) von einem Oszilloskop. Ich möchte einen gleitenden Durchschnitt berechnen. Der von mir entwickelte Algorithmus ist jedoch sehr langsam. Hier ist der Code für meine Formel:
"
If(Row() < 100, Empty(), Summation(i = Row() - 99, Row(), :Current[i,Empty()]) / 100)
"

Wenn die Breite des Mittelungsfensters (in diesem Fall 100 Samples) größer wird (z. B. 500, 1000 oder sogar 50000 oder mehr), wird die Berechnungszeit unangemessen. Hat jemand eine schnellere Möglichkeit, einen gleitenden Durchschnitt zu erstellen?
Danke!
Däne
Betreff: Effizienter Algorithmus für den gleitenden DurchschnittIch finde normalerweise, dass es schneller geht, wenn ich den Tisch alle zusammen vermeiden kann. Das läuft auf meinem Rechner schneller. Sie werden feststellen, dass meine Zahlen auch anders sind als Jeffs. Das liegt einfach daran, dass ich die Zahlen am Ende spiegele und dann x Punkte von der Mitte aus gehe (und nicht die letzten x Punkte). Auch dies hat eine Gewichtung fehlender Werte. Wenn Sie also fehlende Werte haben, ist dies kein Problem. Zumindest glaube ich das. Wenn Sie am Ende keine Spalte benötigen, geht es auch noch schneller. Und da es sich im Grunde nur um eine verkettete Indexmatrix handelt, ist es ziemlich schnell.
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);Betreff: Effizienter Algorithmus für den gleitenden DurchschnittDas ist eine großartige Lösung. Beachten Sie, dass alle fehlenden Werte in der Datenspalte (in diesem Beispiel "aktuell") leere Werte in der berechneten Datenspalte ab der Zeile erstellen, in der die ersten fehlenden Daten für alle darunter liegenden Zeilen auftreten. Es ist ein kleines Problem, dessen Sie sich bewusst sein sollten.Betreff: Effizienter Algorithmus für den gleitenden DurchschnittDanke, Jeff. Diese laufen viel schneller als die Summationsmethode!
Betreff: Effizienter Algorithmus für den gleitenden DurchschnittIch kann hier ein paar Strategien anbieten.
Ich denke, beide sind effizienter als der Operator Summation(), den Sie derzeit verwenden. Ich kann aber nicht sagen, dass sie am effizientesten sind.
1) Verwenden Sie die Sum()-Funktion für eine Matrix.
If(Row()<:Moving Avg Window,
.,
Sum( :Current[Index( Row() - (:Moving Avg Window - 1), Row() )] ) / :Moving Avg Window
)

Dies nutzt die Geschwindigkeit der Matrixoperation aus. Der Index der Aktuell Spalte in dieser Formel ist eine Matrix der Zeilennummern vom Beginn des gleitenden Durchschnittsfensters bis zur aktuellen Zeilennummer. Wenn ich die Spalte auf diese Weise subskriptiere, gibt JMP eine Matrix zurück, und die Sum-Funktion ist gegenüber einer Matrix relativ effizient.
Ein noch schnellerer Weg ist jedoch, diesen gleitenden Durchschnitt über zwei Spalten zu berechnen. Die erste Spalte behält einfach eine gleitende Summe bei, indem der Wert von hinzugefügt wird Aktuell in einer Zeile auf den Wert der laufenden Summe in der vorherigen Zeile und subtrahieren dann den Wert von Current von der Zeile am Anfang des Fensters.
Hier ist die Formel für die gleitende Summe.
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 )
)

Dann ist es einfach, eine Spalte zu haben, die sich teilt Gleitende Summe bis zum Moving Avg-Fenster .
If(Row()>=:Moving Avg Window,
:Name( "moving sum(x)" ) / :Moving Avg Window,
.
)

Ich hänge eine Datentabelle an, die diese beiden Methoden zeigt. In der Datentabelle die Moving Avg-Fenster ist ein Datentabellenvariable .
Um die Effizienz der zweiten Methode in dieser Datentabelle zu sehen, möchten Sie vielleicht die Auswertung unterdrücken von der von der Gleitender Durchschnitt (x) Spalte zuerst.
-Jeff