for - Encontrar promedio móvil de puntos de datos en Python
numpy min (7)
Estoy jugando un poco más en Python y encontré un buen libro con ejemplos. Uno de los ejemplos es trazar algunos datos. Tengo un archivo .txt con dos columnas y tengo la información. Tracé los datos muy bien, pero en el ejercicio dice: Modifique más su programa para calcular y graficar el promedio continuo de los datos, definido por:
$Y_k=/frac{1}{2r}/sum_{m=-r}^r y_{k+m}$
donde r=5
en este caso (y y_k
es la segunda columna en el archivo de datos). Haga que el programa grabe los datos originales y el promedio continuo en el mismo gráfico.
Hasta ahora tengo esto:
from pylab import plot, ylim, xlim, show, xlabel, ylabel
from numpy import linspace, loadtxt
data = loadtxt("sunspots.txt", float)
r=5.0
x = data[:,0]
y = data[:,1]
plot(x,y)
xlim(0,1000)
xlabel("Months since Jan 1749.")
ylabel("No. of Sun spots")
show()
Entonces, ¿cómo calculo la suma? En Mathematica es simple ya que es manipulación simbólica (Sum [i, {i, 0,10}] por ejemplo), pero cómo calcular la suma en python que toma cada diez puntos en los datos y los promedia, y lo hace hasta el final de puntos?
Miré el libro, pero no encontré nada que explicara esto: /
El código de Heltonbiker hizo el truco ^^: D
from __future__ import division
from pylab import plot, ylim, xlim, show, xlabel, ylabel, grid
from numpy import linspace, loadtxt, ones, convolve
import numpy as numpy
data = loadtxt("sunspots.txt", float)
def movingaverage(interval, window_size):
window= numpy.ones(int(window_size))/float(window_size)
return numpy.convolve(interval, window, ''same'')
x = data[:,0]
y = data[:,1]
plot(x,y,"k.")
y_av = movingaverage(y, 10)
plot(x, y_av,"r")
xlim(0,1000)
xlabel("Months since Jan 1749.")
ylabel("No. of Sun spots")
grid(True)
show()
Y obtuve esto:
Muchas gracias ^^ :)
Antes de leer esta respuesta, tenga en cuenta que hay otra respuesta más abajo, de Roman Kh, que usa
numpy.cumsum
y es MUCHO MÁS RÁPIDO que este.
La mejor forma común de aplicar un promedio móvil / deslizante (o cualquier otra función de ventana deslizante) a una señal es usando numpy.convolve()
.
def movingaverage(interval, window_size):
window = numpy.ones(int(window_size))/float(window_size)
return numpy.convolve(interval, window, ''same'')
Aquí, interval es tu matriz x
, y window_size
es la cantidad de muestras a considerar. La ventana se centrará en cada muestra, por lo que toma muestras antes y después de la muestra actual para calcular el promedio. Tu código se convertiría en:
plot(x,y)
xlim(0,1000)
x_av = movingaverage(interval, r)
plot(x_av, y)
xlabel("Months since Jan 1749.")
ylabel("No. of Sun spots")
show()
¡Espero que esto ayude!
Como numpy.convolve es bastante lento, aquellos que necesitan una solución de rendimiento rápido pueden preferir un enfoque cumsum más fácil de entender. Aquí está el código:
cumsum_vec = numpy.cumsum(numpy.insert(data, 0, 0))
ma_vec = (cumsum_vec[window_width:] - cumsum_vec[:-window_width]) / window_width
donde los datos contienen sus datos, y ma_vec contendrá promedios móviles de la longitud de window_width .
En promedio, cumsum es aproximadamente 30-40 veces más rápido que convolve .
Creo que algo así como:
aves = [sum(data[i:i+6]) for i in range(0, len(data), 5)]
Pero siempre tengo que verificar que los índices estén haciendo lo que espero. El rango que desea es (0, 5, 10, ...) y datos [0: 6] le dará datos [0] ... datos [5]
ETA: uy, y quieres ave en lugar de suma, por supuesto. Entonces, en realidad, usa tu código y la fórmula:
r = 5
x = data[:,0]
y1 = data[:,1]
y2 = [ave(y1[i-r:i+r]) for i in range(r, len(y1), 2*r)]
y = [y1, y2]
Hay un problema con la respuesta aceptada. Creo que necesitamos usar "válido" en lugar de "igual" aquí - return numpy.convolve(interval, window, ''same'')
.
Como ejemplo, pruebe la MA de este conjunto de datos = [1,5,7,2,6,7,8,2,2,7,8,3,7,3,7,3,15,6]
- el resultado debería ser [4.2,5.4,6.0,5.0,5.0,5.2,5.4,4.4,5.4,5.6,5.6,4.6,7.0,6.8]
, pero tener "lo mismo" nos da una salida incorrecta de [2.6,3.0,4.2,5.4,6.0,5.0,5.0,5.2,5.4,4.4,5.4,5.6,5.6, 4.6,7.0,6.8,6.2,4.8]
Código oxidado para probar esto -:
result=[]
dataset=[1,5,7,2,6,7,8,2,2,7,8,3,7,3,7,3,15,6]
window_size=5
for index in xrange(len(dataset)):
if index <=len(dataset)-window_size :
tmp=(dataset[index]+ dataset[index+1]+ dataset[index+2]+ dataset[index+3]+ dataset[index+4])/5.0
result.append(tmp)
else:
pass
result==movingaverage(y, window_size)
Pruebe esto con valid & same y vea si las matemáticas tienen sentido.
Mi función promedio móvil, sin función numpy:
from __future__ import division # must be on first line of script
class Solution:
def Moving_Avg(self,A):
m = A[0]
B = []
B.append(m)
for i in range(1,len(A)):
m = (m * i + A[i])/(i+1)
B.append(m)
return B
Un promedio móvil es una convolución, y numpy será más rápido que la mayoría de las operaciones de python puro. Esto le dará el promedio móvil de 10 puntos.
import numpy as np
smoothed = np.convolve(data, np.ones(10)/10)
También recomendaría encarecidamente utilizar el gran paquete de pandas si está trabajando con datos de series temporales. Hay algunas buenas operaciones de promedio móvil integradas .
ravgs = [sum(data[i:i+5])/5. for i in range(len(data)-4)]
Este no es el enfoque más eficiente pero dará su respuesta y no estoy seguro si su ventana tiene 5 puntos o 10. Si es 10, reemplace cada 5 con 10 y el 4 con 9.