Algoritmo di media mobile efficiente<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />Ciao,
Grazie a coloro che hanno scritto qui prima, ho trovato alcune risposte utili!
Ho diversi set di dati di grandi dimensioni (20 milioni di punti ciascuno!) Da un oscilloscopio. Voglio calcolare una media mobile. Tuttavia, l'algoritmo che ho ideato è molto lento. Ecco il codice della mia formula:
"
If(Row() < 100, Empty(), Summation(i = Row() - 99, Row(), :Current[i,Empty()]) / 100)
"

Man mano che la larghezza della finestra della media (in questo caso 100 campioni) aumenta (diciamo 500, 1000 o anche 50000 o più), il tempo di calcolo diventa irragionevole. Qualcuno ha un modo più veloce per fare una media mobile?
Grazie!
Danese
Ri: Algoritmo di media mobile efficienteDi solito trovo che se riesco a evitare il tavolo tutti insieme è più veloce. Funziona più velocemente sulla mia macchina. Noterai che anche i miei numeri sono diversi da quelli di Jeff. Questo semplicemente perché rispecchio i numeri alla fine e poi esco x punti dal centro (piuttosto che gli ultimi x punti). Anche questo ha una ponderazione dei valori mancanti, quindi se hai dei valori mancanti non è un problema. Almeno credo. Se non hai bisogno di una colonna alla fine, è anche più veloce. E poiché è fondamentalmente solo una matrice di indice concata, è piuttosto veloce.
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);Ri: Algoritmo di media mobile efficienteQuesta è un'ottima soluzione. Tieni presente che qualsiasi valore mancante nella colonna dei dati ("corrente" in questo esempio) creerà valori vuoti nella colonna dei dati calcolati dalla riga in cui si verificano i primi dati mancanti a tutte le righe sottostanti. È un piccolo problema di cui essere consapevoli.Ri: Algoritmo di media mobile efficienteGrazie, Jeff. Questi funzionano molto più velocemente del metodo di sommatoria!
Ri: Algoritmo di media mobile efficientePosso offrire un paio di strategie qui.
Penso che entrambi siano più efficienti dell'operatore Summation() che stai attualmente utilizzando. Non posso dire che siano i più efficienti possibili però.
1) Utilizzare la funzione Sum() su una matrice.
If(Row()<:Moving Avg Window,
.,
Sum( :Current[Index( Row() - (:Moving Avg Window - 1), Row() )] ) / :Moving Avg Window
)

Ciò sfrutta la velocità dell'operazione della matrice. Il pedice del Attuale colonna in questa formula è una matrice dei numeri di riga dall'inizio della finestra della media mobile al numero di riga corrente. La mia sottoscrizione della colonna in questo modo JMP restituisce una matrice e la funzione Somma è relativamente efficiente su una matrice.
Un modo ancora più veloce però è calcolare questa media mobile su due colonne. La prima colonna mantiene semplicemente una somma mobile aggiungendo il valore di Attuale in una riga al valore della somma mobile nella riga precedente e quindi sottraendo il valore di Current dalla riga all'inizio della finestra.
Ecco la formula per la somma 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 )
)

Quindi è facile avere una colonna che si divide Somma mobile dal Finestra media mobile .
If(Row()>=:Moving Avg Window,
:Name( "moving sum(x)" ) / :Moving Avg Window,
.
)

Allego una tabella di dati che mostra entrambi questi metodi. Nella tabella dei dati il Finestra media mobile è un Variabile tabella dati .
Per vedere l'efficienza del secondo metodo in questa tabella di dati, potresti volerlo sopprimere la valutazione del del Media mobile(x) prima colonna.
- Jeff