Saltar al contenido

¿Cómo calcular el promedio móvil / móvil usando NumPy / SciPy?

Basta ya de buscar por todo internet ya que estás al sitio indicado, contamos con la solución que quieres hallar sin complicaciones.

Solución:

Si solo desea un promedio móvil no ponderado sencillo, puede implementarlo fácilmente con np.cumsum, cuales quizáses más rápido que los métodos basados ​​en FFT:

EDITAR Se corrigió una indexación incorrecta de uno en uno detectada 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 inflado con funciones especializadas.

Una forma sencilla de lograr esto es usando np.convolve. La idea detrás de esto es aprovechar la forma en que se calcula la convolución discreta y usarla para devolver una media rodante. Esto se puede hacer convolucionando con una secuencia de np.ones de una longitud igual a la longitud de la ventana deslizante que queremos.

Para ello podríamos definir la siguiente función:

def moving_average(x, w):
    return np.convolve(x, np.ones(w), 'valid') / w

Esta función tomará la convolución de la secuencia. x y una secuencia de unos de longitud w. Tenga en cuenta que el elegido mode es valid de modo que el producto de convolución solo se da para los puntos donde las secuencias se superponen completamente.


Algunos ejemplos:

x = np.array([5,3,8,10,2,1,5,1,0,2])

Para una media móvil con una ventana de longitud 2 tendríamos:

moving_average(x, 2)
# array([4. , 5.5, 9. , 6. , 1.5, 3. , 3. , 0.5, 1. ])

Y por una ventana de largo 4:

moving_average(x, 4)
# array([6.5 , 5.75, 5.25, 4.5 , 2.25, 1.75, 2.  ])

Cómo convolve ¿trabaja?

Echemos un vistazo más en profundidad a la forma en que se calcula la convolución discreta. La siguiente función tiene como objetivo replicar la forma np.convolve está calculando los valores de salida:

def mov_avg(x, w):
    for m in range(len(x)-(w-1)):
        yield sum(np.ones(w) * x[m:m+w]) / w 

Lo cual, para el mismo ejemplo anterior, también produciría:

list(mov_avg(x, 2))
# [4.0, 5.5, 9.0, 6.0, 1.5, 3.0, 3.0, 0.5, 1.0]

Entonces, lo que se está haciendo en cada paso es llevar el producto interno entre los array de unos y el actual ventana. En este caso la multiplicación por np.ones(w) es superfluo dado que estamos tomando directamente la sum de la secuencia.

A continuación se muestra un ejemplo de cómo se calculan las primeras salidas para que sea un poco más claro. Supongamos que queremos una ventana de w=4:

[1,1,1,1]
[5,3,8,10,2,1,5,1,0,2]
= (1*5 + 1*3 + 1*8 + 1*10) / w = 6.5

Y la siguiente salida se calcularía como:

  [1,1,1,1]
[5,3,8,10,2,1,5,1,0,2]
= (1*3 + 1*8 + 1*10 + 1*2) / w = 5.75

Y así sucesivamente, devolviendo un promedio móvil de la secuencia una vez que se hayan realizado todas las superposiciones.

La falta de NumPy de una función específica de dominio en particular se debe quizás a la disciplina y fidelidad del Core Team a la directiva principal de NumPy: proporcionar un N-dimensional array escribe, así como funciones para crear e indexar esos arreglos. Como muchos objetivos fundamentales, este no es pequeño y NumPy lo hace de manera brillante.

El (mucho) más grande Ciencia contiene una colección mucho mayor de bibliotecas específicas de dominio (llamadas subpaquetes por desarrolladores de SciPy), por ejemplo, optimización numérica (optimizar), procesamiento de señales (señal) y cálculo integral (integrar).

Supongo que la función que busca está en al menos uno de los subpaquetes SciPy (scipy.signal quizás); sin embargo, buscaría primero en la colección de SciPy scikits, identifique los scikit relevantes y busque la función de interés allí.

Los Scikits son paquetes desarrollados de forma independiente basados ​​en NumPy / SciPy y dirigidos a una disciplina técnica en particular (por ejemplo, imagen-scikits, scikits-learn, etc.) Varios de estos fueron (en particular, el impresionante OpenOpt para optimización numérica) fueron proyectos maduros de gran prestigio mucho antes de elegir residir bajo el relativamente nuevo scikits rúbrica. los Scikits la página de inicio le gustó a las listas anteriores alrededor de 30 scikits, aunque al menos varios de ellos ya no se encuentran en desarrollo activo.

Seguir este consejo te llevará a scikits-timeseries; sin embargo, ese paquete ya no se encuentra en desarrollo activo; En efecto, Pandas se ha convertido, AFAIK, el de factoNumPybiblioteca de series de tiempo basada en

Pandas tiene varias funciones que se pueden utilizar para calcular un media móvil; el más simple de estos es probablemente rolling_mean, que usas 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, solo llama a la función rolling_mean pasando en el objeto Serie y un tamaño de ventana, que en mi ejemplo a continuación es 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, comparar los valores 10-15 en la serie original con la nueva Serie suavizada con media móvil

>>> 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 una docena o más de otras funciones se agrupan informalmente en la documentación de Pandas bajo la rúbrica ventana en movimiento funciones; un segundo grupo de funciones relacionadas en Pandas se conoce como funciones ponderadas exponencialmente (por ejemplo, ewma, que calcula el promedio ponderado móvil exponencialmente). El hecho de que este segundo grupo no esté incluido en el primero (ventana en movimiento funciones) es quizás porque las transformaciones ponderadas exponencialmente no dependen de una ventana de longitud fija

valoraciones y comentarios

Recuerda algo, que tienes concesión de valorar este artículo si te fue útil.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *