Algoritmo de promedio móvil eficiente<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />Hola,
Gracias a aquellos que han publicado aquí antes, ¡he encontrado algunas respuestas útiles!
Tengo varios conjuntos de datos grandes (¡20 millones de puntos cada uno!) de un osciloscopio. Quiero calcular un promedio móvil. Sin embargo, el algoritmo que he ideado es muy lento. Aquí está el código de mi fórmula:
"
If(Fila() < 100, Vacío(), Sumatoria(i = Fila() - 99, Fila(), :Actual[i,Vacío()]) / 100)
"

A medida que aumenta el ancho de la ventana de promedio (en este caso, 100 muestras) (por ejemplo, 500, 1000 o incluso 50 000 o más), el tiempo de cálculo se vuelve irrazonable. ¿Alguien tiene una forma más rápida de hacer un promedio móvil?
¡Gracias!
danés
Re: Algoritmo de promedio móvil eficientePor lo general, encuentro que si puedo evitar la mesa todos juntos, es más rápido. Esto funciona más rápido en mi máquina. Notarás que Mis números también son diferentes a los de Jeff. Eso es simplemente porque reflejo los números al final y luego voy x puntos desde el centro (en lugar de los últimos x puntos). Además, esto tiene una ponderación de los valores faltantes, por lo que si tiene algún valor faltante, no es un problema. Al menos eso pienso. Si no necesita una columna al final, también es aún más rápido. Y dado que es básicamente una matriz de índices concatenados, es bastante rápido.
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: Algoritmo de promedio móvil eficienteEsa es una gran solución. Tenga en cuenta que cualquier valor faltante en la columna de datos ("actual" en este ejemplo) creará valores en blanco en la columna de datos calculados desde la fila donde se encuentran los primeros datos faltantes hasta todas las filas siguientes. Es un pequeño problema a tener en cuenta.Re: Algoritmo de promedio móvil eficienteGracias, Jeff. ¡Estos funcionan mucho más rápido que el método de suma!
Re: Algoritmo de promedio móvil eficientePuedo ofrecer un par de estrategias aquí.
Creo que ambos son más eficientes que el operador Summation() que está utilizando actualmente. Sin embargo, no puedo decir que sean los más eficientes posibles.
1) Use la función Sum() en una matriz.
If(Row()<:Moving Avg Window,
.,
Sum( :Current[Index( Row() - (:Moving Avg Window - 1), Row() )] ) / :Moving Avg Window
)

Esto aprovecha la velocidad de la operación matricial. El subíndice de la Actual La columna de esta fórmula es una matriz de los números de fila desde el inicio de la ventana de promedio móvil hasta el número de fila actual. Al suscribir la columna de esta manera, JMP devuelve una matriz y la función Sum es relativamente eficiente sobre una matriz.
Sin embargo, una forma aún más rápida es calcular este promedio móvil en dos columnas. La primera columna simplemente mantiene una suma móvil al sumar el valor de Actual en una fila al valor de la suma móvil en la fila anterior y luego restando el valor de Actual de la fila al comienzo de la ventana.
Esta es la fórmula de la suma móvil.
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 )
)

Entonces es fácil tener una columna que divide Suma móvil por el Ventana promedio móvil .
If(Row()>=:Moving Avg Window,
:Name( "moving sum(x)" ) / :Moving Avg Window,
.
)

Adjunto una tabla de datos que muestra ambos métodos. En la tabla de datos la Ventana promedio móvil es un Variable de la tabla de datos .
Para ver la eficiencia del segundo método en esta tabla de datos, es posible que desee suprimir la evaluación de la de la Promedio móvil (x) columna primero.
-Jeff