Promedio móvil ponderado en Python

Tengo datos muestreados a intervalos esencialmente aleatorios. Me gustaría calcular un promedio móvil ponderado utilizando numpy (u otro paquete de Python). Tengo una implementación aproximada de un promedio móvil, pero tengo problemas para encontrar una buena manera de hacer un promedio móvil ponderado, de modo que los valores hacia el centro del contenedor se ponderan más que los valores hacia los bordes.

Aquí genero algunos datos de muestra y luego tomo un promedio móvil. ¿Cómo puedo implementar más fácilmente un promedio móvil ponderado? ¡Gracias!

import numpy as np import matplotlib.pyplot as plt #first generate some datapoint for a randomly sampled noisy sinewave x = np.random.random(1000)*10 noise = np.random.normal(scale=0.3,size=len(x)) y = np.sin(x) + noise #plot the data plt.plot(x,y,'ro',alpha=0.3,ms=4,label='data') plt.xlabel('Time') plt.ylabel('Intensity') #define a moving average function def moving_average(x,y,step_size=.1,bin_size=1): bin_centers = np.arange(np.min(x),np.max(x)-0.5*step_size,step_size)+0.5*step_size bin_avg = np.zeros(len(bin_centers)) for index in range(0,len(bin_centers)): bin_center = bin_centers[index] items_in_bin = y[(x>(bin_center-bin_size*0.5) ) & (x<(bin_center+bin_size*0.5))] bin_avg[index] = np.mean(items_in_bin) return bin_centers,bin_avg #plot the moving average bins, average = moving_average(x,y) plt.plot(bins, average,label='moving average') plt.show() 

La salida: Datos y media móvil.

Usando el consejo de crs17 para usar “weight =” en la función np.average, encontré la función de promedio ponderado, que usa una función gaussiana para ponderar los datos:

 def weighted_moving_average(x,y,step_size=0.05,width=1): bin_centers = np.arange(np.min(x),np.max(x)-0.5*step_size,step_size)+0.5*step_size bin_avg = np.zeros(len(bin_centers)) #We're going to weight with a Gaussian function def gaussian(x,amp=1,mean=0,sigma=1): return amp*np.exp(-(x-mean)**2/(2*sigma**2)) for index in range(0,len(bin_centers)): bin_center = bin_centers[index] weights = gaussian(x,mean=bin_center,sigma=width) bin_avg[index] = np.average(y,weights=weights) return (bin_centers,bin_avg) 

Los resultados se ven bien: Promedio de trabajo ponderado usando numpy

Podría usar numpy.average que le permite especificar pesos:

 >>> bin_avg[index] = np.average(items_in_bin, weights=my_weights) 

Entonces, para calcular los pesos, puede encontrar las coordenadas x de cada punto de datos en el contenedor y calcular sus distancias al centro del contenedor.

Esto no le dará una solución exacta, pero hará su vida más fácil y probablemente será lo suficientemente bueno … Primero, promedie sus muestras en contenedores pequeños. Una vez que haya vuelto a muestrear sus datos para que estén equidistados, puede usar trucos de zancada y np.average para hacer un promedio ponderado:

 from numpy.lib.stride_tricks import as_strided def moving_weighted_average(x, y, step_size=.1, steps_per_bin=10, weights=None): # This ensures that all samples are within a bin number_of_bins = int(np.ceil(np.ptp(x) / step_size)) bins = np.linspace(np.min(x), np.min(x) + step_size*number_of_bins, num=number_of_bins+1) bins -= (bins[-1] - np.max(x)) / 2 bin_centers = bins[:-steps_per_bin] + step_size*steps_per_bin/2 counts, _ = np.histogram(x, bins=bins) vals, _ = np.histogram(x, bins=bins, weights=y) bin_avgs = vals / counts n = len(bin_avgs) windowed_bin_avgs = as_strided(bin_avgs, (n-steps_per_bin+1, steps_per_bin), bin_avgs.strides*2) weighted_average = np.average(windowed_bin_avgs, axis=1, weights=weights) return bin_centers, weighted_average 

Ahora puedes hacer algo como esto:

 #plot the moving average with triangular weights weights = np.concatenate((np.arange(0, 5), np.arange(0, 5)[::-1])) bins, average = moving_weighted_average(x, y, steps_per_bin=len(weights), weights=weights) plt.plot(bins, average,label='moving average') plt.show() 

introduzca la descripción de la imagen aquí