Reputation: 10781
I have an array of data Y
such that Y
is a function of an independent variable X
(another array).
The values in X
vary from 0 to 360, with wraparound.
The values in Y
vary from -180 to 180, also with wraparound.
(That is, these values are angles in degrees around a circle.)
Does anyone know of any function in Python (in numpy
, scipy
, etc.) capable of low-pass filtering my Y
values as a function of X
?
In case this is at all confusing, here's a plot of example data:
Upvotes: 2
Views: 2087
Reputation: 2018
You can use convolve2D
from scipy.signal
. Here is a function, which applies smoothing to a numpy array a
. If a
has more than one dimension smoothing is applied to the innermost (fastest) dimension.
import numpy as np
from scipy import signal
def cyclic_moving_av( a, n= 3, win_type= 'boxcar' ):
window= signal.get_window( win_type, n, fftbins=False ).reshape( (1,n) )
shp_a= a.shape
b= signal.convolve2d( a.reshape( ( np.prod( shp_a[:-1], dtype=int ), shp_a[-1] ) ),
window, boundary='wrap', mode='same' )
return ( b / np.sum( window ) ).reshape( shp_a )
For instance it can be used like
import matplotlib.pyplot as plt
x = np.linspace(0, 360, 360)
y1 = 5 * np.sin(x / 90. * 3.14) + 0.5 * np.random.randn(360)
y2 = 5 * np.cos(0.8 * x / 90. * 3.14) + 0.5 * np.random.randn(360)
y_av= cyclic_moving_av( np.stack((y1,y2)), n=10 ) #1
plt.plot(x, y1, '+')
plt.plot(x, y2, '+')
plt.plot(x, y_av[0])
plt.plot(x, y_av[1])
plt.show()
Line #1
is equivalent to
y_av[0]= cyclic_moving_av( y1, n=10 )
y_av[1]= cyclic_moving_av( y2, n=10 )
win_type= 'boxcar'
results in averaging over neighbors with equal weights. See signal.get_window for other options.
Upvotes: 1
Reputation: 76386
Say you start with
import numpy as np
x = np.linspace(0, 360, 360)
y = 5 * np.sin(x / 90. * 3.14) + np.random.randn(360)
plot(x, y, '+');
To perform a circular convolution, you can do the following:
yy = np.concatenate((y, y))
smoothed = np.convolve(np.array([1] * 5), yy)[5: len(x) + 5]
This uses, at each point, the cyclic average with the previous 5 points (inclusive). Of course, there are other ways of doing so.
>>> plot(x, smoothed)
Upvotes: 1
Reputation: 4418
Here is a solution using pandas to do a moving average. First unwrap
the data (need to convert to radians and back), so there are no discontinuities (e.g., jump from 180 to -179). Then compute the moving average and finally convert back to wrapped data if desired. Also, check out this numpy cookbook recipe using np.convolve()
.
import numpy as np
import pandas as pd
# generate random data
X = pd.Series([(x + 5*np.random.random())%360 for x in range(-100, 600, 15)])
Y = pd.Series([(y + 5*np.random.random())%360 - 180 for y in range(-200, 500, 15)])
# 'unwrap' the angles so there is no wrap around
X1 = pd.Series(np.rad2deg(np.unwrap(np.deg2rad(Y))))
Y1 = pd.Series(np.rad2deg(np.unwrap(np.deg2rad(Y))))
# smooth the data with a moving average
# note: this is pandas 17.1, the api changed for version 18
X2 = pd.rolling_mean(X1, window=3)
Y2 = pd.rolling_mean(Y1, window=3)
# convert back to wrapped data if desired
X3 = X2 % 360
Y3 = (Y2 + 180)%360 - 180
Upvotes: 1