¿Existe una función SciPy o una función o módulo NumPy para Python que calcule la media de ejecución de una matriz 1D dada una ventana específica?
python
numpy
scipy
moving-average
Shejo284
fuente
fuente
UPD: Alleo y jasaarim han propuesto soluciones más eficientes .
Puedes usar
np.convolve
para eso:Explicación
La media de ejecución es un caso de la operación matemática de convolución . Para la media de ejecución, desliza una ventana a lo largo de la entrada y calcula la media del contenido de la ventana. Para señales 1D discretas, la convolución es lo mismo, excepto que, en lugar de la media, calcula una combinación lineal arbitraria, es decir, multiplique cada elemento por un coeficiente correspondiente y sume los resultados. Esos coeficientes, uno para cada posición en la ventana, a veces se llaman la convolución del núcleo . Ahora, la media aritmética de los valores de N es
(x_1 + x_2 + ... + x_N) / N
, entonces el núcleo correspondiente es(1/N, 1/N, ..., 1/N)
, y eso es exactamente lo que obtenemos al usarnp.ones((N,))/N
.Bordes
El
mode
argumento denp.convolve
especifica cómo manejar los bordes. Elegí elvalid
modo aquí porque creo que así es como la mayoría de la gente espera que funcione la carrera, pero es posible que tengas otras prioridades. Aquí hay una gráfica que ilustra la diferencia entre los modos:fuente
numpy.cumsum
tiene una mayor complejidad.Solución eficiente
La convolución es mucho mejor que el enfoque directo, pero (supongo) usa FFT y, por lo tanto, es bastante lenta. Sin embargo, especialmente para calcular el funcionamiento, el siguiente enfoque funciona bien
El código para verificar
Tenga en cuenta que
numpy.allclose(result1, result2)
esTrue
, dos métodos son equivalentes. A mayor N, mayor diferencia en el tiempo.advertencia: aunque cumsum es más rápido, habrá un mayor error de coma flotante que puede causar que sus resultados sean inválidos / incorrectos / inaceptables
los comentarios señalaron este problema de error de coma flotante aquí, pero lo estoy haciendo más obvio aquí en la respuesta. .
np.longdouble
pero su error de coma flotante seguirá siendo significativo para un número relativamente grande de puntos (alrededor de> 1e5 pero depende de sus datos)fuente
numpy.convolve
O (mn); sus documentos mencionan quescipy.signal.fftconvolve
usa FFT.running_mean([1,2,3], 2)
daarray([1, 2])
. Reemplazarx
por[float(value) for value in x]
hace el truco.x
contiene flotadores. Ejemplo:running_mean(np.arange(int(1e7))[::-1] + 0.2, 1)[-1] - 0.2
regresa0.003125
mientras uno espera0.0
. Más información: en.wikipedia.org/wiki/Loss_of_significanceActualización: el siguiente ejemplo muestra la
pandas.rolling_mean
función anterior que se ha eliminado en versiones recientes de pandas. Un equivalente moderno de la llamada a la función a continuación seríapandas es más adecuado para esto que NumPy o SciPy. Su función rolling_mean hace el trabajo convenientemente. También devuelve una matriz NumPy cuando la entrada es una matriz.
Es difícil superar el
rolling_mean
rendimiento con cualquier implementación personalizada de Python puro. Aquí hay un ejemplo de rendimiento contra dos de las soluciones propuestas:También hay buenas opciones sobre cómo lidiar con los valores de borde.
fuente
df.rolling(windowsize).mean()
ahora funciona en su lugar (muy rápidamente podría agregar). para 6,000 series de filas%timeit test1.rolling(20).mean()
devolvió 1000 bucles, lo mejor de 3: 1.16 ms por bucledf.rolling()
funciona lo suficientemente bien, el problema es que incluso este formulario no admitirá ndarrays en el futuro. Para usarlo, primero tendremos que cargar nuestros datos en un marco de datos de Pandas. Me encantaría ver esta función agregada a cualquieranumpy
oscipy.signal
.%timeit bottleneck.move_mean(x, N)
es de 3 a 15 veces más rápido que los métodos cumsum y pandas en mi pc. Echa un vistazo a su punto de referencia en el archivo README del repositorio .Puede calcular una media de ejecución con:
Pero es lento.
Afortunadamente, numpy incluye una función de convolución que podemos usar para acelerar las cosas. La media de ejecución es equivalente a convolucionarse
x
con un vector que esN
largo, con todos los miembros iguales a1/N
. La implementación numpy de convolve incluye el transitorio inicial, por lo que debe eliminar los primeros puntos N-1:En mi máquina, la versión rápida es 20-30 veces más rápida, dependiendo de la longitud del vector de entrada y el tamaño de la ventana de promedio.
Tenga en cuenta que convolve incluye un
'same'
modo que parece que debería abordar el problema transitorio inicial, pero lo divide entre el principio y el final.fuente
mode='valid'
en un procesoconvolve
que no requiera ningún procesamiento posterior.mode='valid'
elimina el transitorio de ambos extremos, ¿verdad? Silen(x)=10
yN=4
, para una media en ejecución, quisiera 10 resultados perovalid
devuelve 7.modes = ('full', 'same', 'valid'); [plot(convolve(ones((200,)), ones((50,))/50, mode=m)) for m in modes]; axis([-10, 251, -.1, 1.1]); legend(modes, loc='lower center')
(con pyplot y numpy importado).runningMean
Tengo el efecto secundario de promediar con ceros, cuando sales de la matriz conx[ctr:(ctr+N)]
el lado derecho de la matriz.runningMeanFast
También tiene este problema de efecto de borde.en mis pruebas en Tradewave.net TA-lib siempre gana:
resultados:
fuente
NameError: name 'info' is not defined
. Recibo este error, señor.Para obtener una solución lista para usar, consulte https://scipy-cookbook.readthedocs.io/items/SignalSmooth.html . Proporciona promedio de ejecución con el
flat
tipo de ventana. Tenga en cuenta que esto es un poco más sofisticado que el simple método de convolución “hágalo usted mismo”, ya que trata de manejar los problemas al principio y al final de los datos reflejándolos (lo que puede o no funcionar en su caso. ..).Para empezar, puedes probar:
fuente
numpy.convolve
la diferencia solo para alterar la secuencia.w
el tamaño de la ventana ys
los datos?Puede usar scipy.ndimage.filters.uniform_filter1d :
uniform_filter1d
:'reflect'
está el valor predeterminado, pero en mi caso, prefería'nearest'
También es bastante rápido (casi 50 veces más rápido que
np.convolve
y 2-5 veces más rápido que el enfoque cumsum dado anteriormente ):Aquí hay 3 funciones que le permiten comparar el error / velocidad de diferentes implementaciones:
fuente
uniform_filter1d
,np.convolve
con un rectángulo, ynp.cumsum
seguido denp.subtract
. mis resultados: (1.) convolve es el más lento. (2.) cumsum / sustract es aproximadamente 20-30x más rápido. (3.) uniform_filter1d es aproximadamente 2-3 veces más rápido que cumsum / sustract. el ganador es definitivamente uniform_filter1d.uniform_filter1d
es más rápido que lacumsum
solución (en aproximadamente 2-5x). yuniform_filter1d
no recibe un error masivo de coma flotante como lo hace lacumsum
solución.Sé que esta es una vieja pregunta, pero aquí hay una solución que no utiliza ninguna estructura de datos o bibliotecas adicionales. Es lineal en el número de elementos de la lista de entrada y no se me ocurre otra forma de hacerlo más eficiente (en realidad, si alguien sabe de una mejor manera de asignar el resultado, hágamelo saber).
NOTA: esto sería mucho más rápido usando una matriz numpy en lugar de una lista, pero quería eliminar todas las dependencias. También sería posible mejorar el rendimiento mediante la ejecución de subprocesos múltiples
La función supone que la lista de entrada es unidimensional, así que tenga cuidado.
Ejemplo
Suponga que tenemos una lista
data = [ 1, 2, 3, 4, 5, 6 ]
en la que queremos calcular una media móvil con un período de 3, y que también desea una lista de salida que tenga el mismo tamaño que la entrada (es el caso más frecuente).El primer elemento tiene el índice 0, por lo que la media móvil debe calcularse en los elementos del índice -2, -1 y 0. Obviamente no tenemos datos [-2] y datos [-1] (a menos que desee utilizar un valor especial condiciones de contorno), por lo que suponemos que esos elementos son 0. Esto es equivalente a rellenar con cero la lista, excepto que en realidad no la rellenamos, solo hacemos un seguimiento de los índices que requieren relleno (de 0 a N-1).
Entonces, para los primeros N elementos, seguimos sumando los elementos en un acumulador.
A partir de los elementos N + 1 en adelante, la acumulación simple no funciona. esperamos
result[3] = (2 + 3 + 4)/3 = 3
pero esto es diferente de(sum + 4)/3 = 3.333
.La forma de calcular el valor correcto es restar
data[0] = 1
desum+4
, dando asísum + 4 - 1 = 9
.Esto sucede porque actualmente
sum = data[0] + data[1] + data[2]
, pero también es cierto para todosi >= N
porque, antes de la resta,sum
esdata[i-N] + ... + data[i-2] + data[i-1]
.fuente
Siento que esto se puede resolver elegantemente usando un cuello de botella
Ver muestra básica a continuación:
"mm" es la media móvil de "a".
"ventana" es el número máximo de entradas a considerar para la media móvil.
"min_count" es el número mínimo de entradas a considerar para la media móvil (por ejemplo, para los primeros elementos o si la matriz tiene valores nan).
Lo bueno es que Bottleneck ayuda a lidiar con los valores nanométricos y también es muy eficiente.
fuente
Todavía no he comprobado qué tan rápido es esto, pero podrías intentarlo:
fuente
Esta respuesta contiene soluciones que utilizan la biblioteca estándar de Python para tres escenarios diferentes.
Promedio corriente con
itertools.accumulate
Esta es una solución Python 3.2+ de memoria eficiente que calcula el promedio de ejecución sobre un valor iterable de apalancamiento
itertools.accumulate
.Tenga en cuenta que
values
puede ser iterable, incluidos generadores o cualquier otro objeto que produzca valores sobre la marcha.Primero, construye perezosamente la suma acumulativa de los valores.
A continuación,
enumerate
la suma acumulativa (a partir de 1) y construir un generador que produce la fracción de valores acumulados y el índice de enumeración actual.Puede emitir
means = list(rolling_avg)
si necesita todos los valores en la memoria a la vez o llamar de formanext
incremental.(Por supuesto, también puede iterar
rolling_avg
con unfor
bucle, que llamaránext
implícitamente).Esta solución se puede escribir como una función de la siguiente manera.
Una rutina a la que puede enviar valores en cualquier momento
Esta rutina consume los valores que envía y mantiene un promedio de los valores vistos hasta ahora.
Es útil cuando no tiene un valor iterable pero adquiere los valores para promediar uno por uno en diferentes momentos a lo largo de la vida de su programa.
La corutina funciona así:
Calcular el promedio sobre una ventana deslizante de tamaño
N
Esta función de generador toma un tamaño iterable y de ventana
N
y produce el promedio sobre los valores actuales dentro de la ventana. Utiliza adeque
, que es una estructura de datos similar a una lista, pero optimizada para modificaciones rápidas (pop
,append
) en ambos puntos finales .Aquí está la función en acción:
fuente
Llegué un poco tarde a la fiesta, pero hice mi propia pequeña función que NO envuelve los extremos o los pads con ceros que luego se usan para encontrar el promedio también. Como otro tratamiento es que también vuelve a muestrear la señal en puntos linealmente espaciados. Personalice el código a voluntad para obtener otras funciones.
El método es una simple multiplicación matricial con un núcleo gaussiano normalizado.
Un uso simple en una señal sinusoidal con ruido distribuido normal agregado:
fuente
sum
, en sunp.sum
lugar se usa 2 El@
operador (no tengo idea de qué se trata) arroja un error. Puede que lo investigue más tarde, pero ahora me falta el tiempo@
es el operador de multiplicación de matrices que implementa np.matmul . Compruebe si suy_in
matriz es una matriz numpy, ese podría ser el problema.En lugar de numpy o scipy, recomendaría pandas para hacer esto más rápidamente:
Esto toma el promedio móvil (MA) de 3 períodos de la columna "datos". También puede calcular las versiones desplazadas, por ejemplo, la que excluye la celda actual (desplazada hacia atrás) se puede calcular fácilmente como:
fuente
pandas.rolling_mean
mientras que la mía usapandas.DataFrame.rolling
. También puede calcular el movimiento,min(), max(), sum()
etc., así comomean()
con este método fácilmente.pandas.rolling_min, pandas.rolling_max
etc. Son similares pero diferentes.Hay un comentario de mab enterrado en una de las respuestas anteriores que tiene este método.
bottleneck
tienemove_mean
cuál es un promedio móvil simple:min_count
es un parámetro útil que básicamente llevará la media móvil hasta ese punto en su matriz. Si no establecemin_count
, será igualwindow
, y todo hasta loswindow
puntos seránan
.fuente
Otro enfoque para encontrar el promedio móvil sin usar panda numpy
imprimirá [2.0, 4.0, 6.0, 6.5, 7.4, 7.833333333333333]
fuente
Esta pregunta ahora es incluso más antigua que cuando NeXuS escribió sobre ella el mes pasado, PERO me gusta cómo su código trata los casos extremos. Sin embargo, debido a que es un "promedio móvil simple", sus resultados van a la zaga de los datos a los que se aplican. Pensé que se trata de casos extremos de un modo más satisfactorio que los modos de NumPy
valid
,same
yfull
que podría lograrse mediante la aplicación de un enfoque similar al de unconvolution()
método basado.Mi contribución utiliza un promedio de ejecución central para alinear sus resultados con sus datos. Cuando hay muy pocos puntos disponibles para usar la ventana de tamaño completo, los promedios de ejecución se calculan desde ventanas sucesivamente más pequeñas en los bordes de la matriz. [En realidad, desde ventanas sucesivamente más grandes, pero ese es un detalle de implementación.]
Es relativamente lento porque usa
convolve()
, y probablemente podría ser mejorado por un verdadero Pythonista, sin embargo, creo que la idea sigue en pie.fuente
Hay muchas respuestas anteriores sobre el cálculo de una media continua. Mi respuesta agrega dos características adicionales:
Esta segunda característica es particularmente útil para determinar qué valores difieren de la tendencia general en una cierta cantidad.
Yo uso numpy.cumsum ya que es el método más eficiente en el tiempo ( ver la respuesta de Alleo arriba ).
Este código funciona incluso para Ns solamente. Se puede ajustar para números impares cambiando la inserción np.de padded_x y n_nan.
Ejemplo de salida (sin formato en negro, movavg en azul):
Este código se puede adaptar fácilmente para eliminar todos los valores promedio móviles calculados a partir de un valor menor que el valor de corte = 3 valores distintos de nan.
fuente
Utilice solo la biblioteca estándar de Python (memoria eficiente)
Simplemente dé otra versión del uso de la biblioteca estándar
deque
solamente. Es una gran sorpresa para mí que la mayoría de las respuestas estén usandopandas
onumpy
.En realidad encontré otra implementación en documentos de Python
Sin embargo, la implementación me parece un poco más compleja de lo que debería ser. Pero debe estar en los documentos estándar de Python por una razón, ¿alguien podría comentar sobre la implementación del mío y el documento estándar?
fuente
O(n*d)
cálculos (d
siendo el tamaño de la ventana, eln
tamaño de iterable) y lo están haciendoO(n)
Con las variables de @ Aikude, escribí one-liner.
fuente
Aunque hay soluciones para esta pregunta aquí, eche un vistazo a mi solución. Es muy simple y funciona bien.
fuente
Al leer las otras respuestas, no creo que esto sea lo que pedía la pregunta, pero llegué aquí con la necesidad de mantener un promedio de una lista de valores que crecía en tamaño.
Entonces, si desea mantener una lista de valores que está adquiriendo desde algún lugar (un sitio, un dispositivo de medición, etc.) y el promedio de los últimos
n
valores actualizados, puede usar el siguiente código, que minimiza el esfuerzo de agregar nuevos elementos:Y puedes probarlo con, por ejemplo:
Lo que da:
fuente
Otra solución simplemente usando una biblioteca estándar y deque:
fuente
Para fines educativos, permítanme agregar dos soluciones Numpy más (que son más lentas que la solución cumsum):
Funciones utilizadas: as_strided , add.reduceat
fuente
Todas las soluciones mencionadas son pobres porque carecen de
numpy.cumsum
, oO(len(x) * w)
implementaciones como convoluciones.Dado
Tenga en cuenta que
x_[:w].sum()
es igualx[:w-1].sum()
. Así, por primera media lanumpy.cumsum(...)
sumax[w] / w
(a travésx_[w+1] / w
), y resta0
(dex_[0] / w
). Esto resulta enx[0:w].mean()
A través de cumsum, actualizará el segundo promedio agregando
x[w+1] / w
y restando adicionalmentex[0] / w
, lo que da como resultadox[1:w+1].mean()
.Esto continúa hasta que
x[-w:].mean()
se alcanza.Esta solución es vectorizada
O(m)
, legible y numéricamente estable.fuente
¿Qué tal un filtro de media móvil ? También es un trazador de líneas y tiene la ventaja de que puede manipular fácilmente el tipo de ventana si necesita algo más que el rectángulo, es decir. un promedio móvil simple de N de una matriz a:
Y con la ventana triangular aplicada:
Nota: Por lo general, descarto las primeras N muestras como falsas, por lo tanto,
[N:]
al final, pero no es necesario y solo es una elección personal.fuente
Si elige rodar la suya, en lugar de usar una biblioteca existente, tenga en cuenta el error de coma flotante y trate de minimizar sus efectos:
Si todos sus valores son aproximadamente del mismo orden de magnitud, esto ayudará a preservar la precisión al agregar siempre valores de magnitudes más o menos similares.
fuente