movil - series de tiempo con python
¿Cómo calcular el promedio móvil usando NumPy? (2)
Parece que no hay una función que simplemente calcule el promedio móvil en numpy / scipy, lo que lleva a soluciones intrincadas .
Mi pregunta es doble:
- ¿Cuál es la forma más fácil de (correctamente) implementar una media móvil con numpy?
- Dado que esto parece no trivial y propenso a errores, ¿hay una buena razón para no tener las baterías incluidas en este caso?
La falta de NumPy de una función específica de dominio particular tal vez se deba a la disciplina y fidelidad del equipo central a la directiva principal de NumPy: proporcionar un tipo de matriz N-dimensional , así como funciones para crear e indexar esas matrices. Al igual que muchos objetivos fundamentales, este no es pequeño, y NumPy lo hace brillantemente.
El (mucho) más grande SciPy contiene una colección mucho más grande de bibliotecas específicas de dominio (llamadas subpaquetes por SciPy devs) - por ejemplo, optimización numérica ( optimizar ), procesamiento de señal ( señal ) y cálculo integral ( integrar ).
Supongo que la función que buscas está en al menos uno de los subpaquetes de SciPy ( scipy.signal quizás); sin embargo, buscaría primero en la colección de scikits de SciPy , identificaré los scikit (s) relevantes y buscaré la función de interés allí.
Los Scikits son paquetes desarrollados independientemente basados en NumPy / SciPy y dirigidos a una disciplina técnica particular (p. Ej., Scikits-image , scikits-learn , etc.). Varios de estos fueron (en particular, el asombroso OpenOpt para la optimización numérica) muy apreciados. proyectos maduros mucho antes de elegir residir bajo la rúbrica de scikits relativamente nueva. A la página de inicio de Scikits le gustaban enumerar unos 30 de estos scikits , aunque al menos varios de ellos ya no se encuentran en desarrollo activo.
Seguir este consejo te llevaría a scikits-timeseries ; sin embargo, ese paquete ya no se encuentra en desarrollo activo; En efecto, Pandas se ha convertido, AFAIK, en la biblioteca de series de tiempo basadas en NumPy de facto .
Pandas tiene varias funciones que se pueden usar para calcular un promedio móvil ; el más simple de estos es probablemente rolling_mean , que se usa así:
>>> # the recommended syntax to import pandas
>>> import pandas as PD
>>> import numpy as NP
>>> # prepare some fake data:
>>> # the date-time indices:
>>> t = PD.date_range(''1/1/2010'', ''12/31/2012'', freq=''D'')
>>> # the data:
>>> x = NP.arange(0, t.shape[0])
>>> # combine the data & index into a Pandas ''Series'' object
>>> D = PD.Series(x, t)
Ahora, simplemente llame a la función rolling_mean pasando el objeto Serie y un tamaño de ventana , que en mi ejemplo a continuación es de 10 días .
>>> d_mva = PD.rolling_mean(D, 10)
>>> # d_mva is the same size as the original Series
>>> d_mva.shape
(1096,)
>>> # though obviously the first w values are NaN where w is the window size
>>> d_mva[:3]
2010-01-01 NaN
2010-01-02 NaN
2010-01-03 NaN
verificar que funcionó - por ejemplo, valores comparados 10 - 15 en la serie original versus la nueva serie alisada con media rodante
>>> D[10:15]
2010-01-11 2.041076
2010-01-12 2.041076
2010-01-13 2.720585
2010-01-14 2.720585
2010-01-15 3.656987
Freq: D
>>> d_mva[10:20]
2010-01-11 3.131125
2010-01-12 3.035232
2010-01-13 2.923144
2010-01-14 2.811055
2010-01-15 2.785824
Freq: D
La función rolling_mean, junto con aproximadamente una docena más o menos de otra función, se agrupan informalmente en la documentación de Pandas bajo las funciones de la ventana de movimiento de rúbrica; un segundo grupo relacionado de funciones en Pandas se conoce como funciones ponderadas exponencialmente (p. ej., ewma , que calcula el promedio ponderado exponencialmente en movimiento). El hecho de que este segundo grupo no esté incluido en el primero (funciones de ventana móvil ) es tal vez porque las transformaciones ponderadas exponencialmente no se basan en una ventana de longitud fija
Si solo quiere un promedio móvil directo no ponderado, puede implementarlo fácilmente con np.cumsum
, que puede ser más rápido que los métodos basados en FFT:
EDITAR corrigió una indexación equivocada manchada por Bean en el código. EDITAR
def moving_average(a, n=3) :
ret = np.cumsum(a, dtype=float)
ret[n:] = ret[n:] - ret[:-n]
return ret[n - 1:] / n
>>> a = np.arange(20)
>>> moving_average(a)
array([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11.,
12., 13., 14., 15., 16., 17., 18.])
>>> moving_average(a, n=4)
array([ 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5,
10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5])
Así que supongo que la respuesta es: es realmente fácil de implementar, y tal vez numpy ya está un poco abultado con funcionalidades especializadas.