如何以正确的方式平滑曲线?

让我们假设我们有一个数据集,可能会给大概

import numpy as np x = np.linspace(0,2*np.pi,100) y = np.sin(x) + np.random.random(100) * 0.2 

因此,我们有20%的数据集的变化。 我的第一个想法是使用scipy的UnivariateSpline函数,但问题是这并没有考虑到小噪声的好处。 如果考虑频率,背景比信号小得多,所以只有截止的样条可能是一个想法,但是这涉及到来回傅里叶变换,这可能导致不良行为。 另一种方式是移动平均线,但这也需要正确的延迟select。

任何提示/书籍或链接如何解决这个问题?

例

我更喜欢Savitzky-Golay滤波器 。 它使用最小二乘法将数据的一个小窗口回归到多项式上,然后使用多项式来估计窗口中心的点。 最后窗口向前移动一个数据点,重复这个过程。 这样继续下去,直到每个点相对于邻居都进行了最佳调整。 即使来自非周期性和非线性源的噪声样本也很好。

这是一个彻底的食谱例子 。 看看我的代码,以了解它是多么容易使用。 注意:我遗漏了用于定义savitzky_golay()函数的代码,因为您可以从上面链接的食谱示例中复制/粘贴它。

 import numpy as np import matplotlib.pyplot as plt x = np.linspace(0,2*np.pi,100) y = np.sin(x) + np.random.random(100) * 0.2 yhat = savitzky_golay(y, 51, 3) # window size 51, polynomial order 3 plt.plot(x,y) plt.plot(x,yhat, color='red') plt.show() 

最佳地平滑嘈杂的正弦曲线

更新:我注意到,我所链接的菜谱例子已被删除。 幸运的是,像@dodohjk指出的那样,Savitzky-Golayfilter已经被整合到了SciPy库中 。

如果您对周期性信号(如您的示例)的“平滑”版本感兴趣,那么FFT是正确的select。 进行傅里叶变换,并将低频率的频率减去:

 import numpy as np import scipy.fftpack N = 100 x = np.linspace(0,2*np.pi,N) y = np.sin(x) + np.random.random(N) * 0.2 w = scipy.fftpack.rfft(y) f = scipy.fftpack.rfftfreq(N, x[1]-x[0]) spectrum = w**2 cutoff_idx = spectrum < (spectrum.max()/5) w2 = w.copy() w2[cutoff_idx] = 0 y2 = scipy.fftpack.irfft(w2) 

在这里输入图像描述

即使你的信号不是完全周期性的,这也能很好的减less白噪声。 有很多types的滤波器可以使用(高通,低通等),适当的滤波器取决于你在找什么。

一个快速和肮脏的方式来平滑我使用的数据,基于移动平均框(通过卷积):

 x = np.linspace(0,2*np.pi,100) y = np.sin(x) + np.random.random(100) * 0.8 def smooth(y, box_pts): box = np.ones(box_pts)/box_pts y_smooth = np.convolve(y, box, mode='same') return y_smooth plot(x, y,'o') plot(x, smooth(y,3), 'r-', lw=2) plot(x, smooth(y,19), 'g-', lw=2) 

在这里输入图像描述

将移动平均数拟合到您的数据将消除噪音,看到这个答案如何做到这一点。

如果您想使用LOWESS来适合您的数据(它类似于移动平均数,但更复杂),您可以使用statsmodels库来实现:

 import numpy as np import pylab as plt import statsmodels.api as sm x = np.linspace(0,2*np.pi,100) y = np.sin(x) + np.random.random(100) * 0.2 lowess = sm.nonparametric.lowess(y, x, frac=0.1) plt.plot(x, y, '+') plt.plot(lowess[:, 0], lowess[:, 1]) plt.show() 

最后,如果您知道信号的functionforms,则可以为数据拟合曲线,这可能是最好的select。

另一种select是在statsmodel中使用KernelReg :

 from statsmodels.nonparametric.kernel_regression import KernelReg import numpy as np import matplotlib.pyplot as plt x = np.linspace(0,2*np.pi,100) y = np.sin(x) + np.random.random(100) * 0.2 # The third parameter specifies the type of the variable x; # 'c' stands for continuous kr = KernelReg(y,x,'c') plt.plot(x, y, '+') y_pred, y_std = kr.fit(x) plt.plot(x, y_pred) plt.show()