这要看你如何定义“噪声”以及它是如何引起的。由于你没有提供关于你的情况的太多信息,我会把你的问题理解为“如何使曲线平滑”。卡尔曼滤波器可以实现这一点,但它过于复杂,我更喜欢简单的IIR滤波器。
import matplotlib.pyplot as plt
import numpy as np
mu, sigma = 0, 500
x = np.arange(1, 100, 0.1) # x axis
z = np.random.normal(mu, sigma, len(x)) # noise
y = x ** 2 + z # data
plt.plot(x, y, linewidth=2, linestyle="-", c="b") # it includes some noise
筛选后
from scipy.signal import lfilter
n = 15 # the larger n is, the smoother curve will be
b = [1.0 / n] * n
a = 1
yy = lfilter(b, a, y)
plt.plot(x, yy, linewidth=2, linestyle="-", c="b") # smooth by filter
lfilter
是来自scipy.signal的一个函数。
顺便说一下,如果您想要使用Kalman滤波进行平滑处理,scipy也提供了一个示例。 Kalman滤波器在这种情况下也可以工作,但并不是必须的。
根据您想要减少噪音的程度,您也可以使用scipy
中的Savitzky-Golay过滤器。
以下是来自@lyken-syu的示例:
import matplotlib.pyplot as plt
import numpy as np
mu, sigma = 0, 500
x = np.arange(1, 100, 0.1) # x axis
z = np.random.normal(mu, sigma, len(x)) # noise
y = x ** 2 + z # data
plt.plot(x, y, linewidth=2, linestyle="-", c="b") # it include some noise
并应用了Savitzky-Golay过滤器
from scipy.signal import savgol_filter
w = savgol_filter(y, 101, 2)
plt.plot(x, w, 'b') # high frequency noise removed
将window_length
增加到501:
在此处阅读有关滤波器的更多信息
ConvolutionSmoother
,但你也可以尝试其他方法。(还有KalmanSmoother
可用)import numpy as np
import matplotlib.pyplot as plt
from tsmoothie.smoother import *
mu, sigma = 0, 500
x = np.arange(1, 100, 0.1) # x axis
z = np.random.normal(mu, sigma, len(x)) # noise
y = x ** 2 + z # data
# operate smoothing
smoother = ConvolutionSmoother(window_len=30, window_type='ones')
smoother.smooth(y)
# generate intervals
low, up = smoother.get_intervals('sigma_interval', n_sigma=3)
# plot the smoothed timeseries with intervals
plt.figure(figsize=(11,6))
plt.plot(smoother.data[0], color='orange')
plt.plot(smoother.smooth_data[0], linewidth=3, color='blue')
plt.fill_between(range(len(smoother.data[0])), low[0], up[0], alpha=0.3)
我还要指出,tsmoothie可以以向量化的方式平滑多个时间序列。根据您的最终使用情况,考虑使用LOWESS(局部加权散点图平滑)去除噪声可能是值得的。我已经在重复测量数据集中成功使用过它。
有关局部回归方法(包括LOWESS和LOESS)的更多信息,请单击此处。
为了与其他答案保持一致,使用@lyken-syu的示例数据:
import numpy as np
import matplotlib.pyplot as plt
mu, sigma = 0, 500
x = np.arange(1, 100, 0.1) # x axis
z = np.random.normal(mu, sigma, len(x)) # noise
y = x ** 2 + z # signal + noise
plt.plot(x, y, linewidth = 2, linestyle = "-", c = "b") # includes some noise
plt.show()
以下是使用statsmodels实现LOWESS技术的方法:
import statsmodels.api as sm
y_lowess = sm.nonparametric.lowess(y, x, frac = 0.3) # 30 % lowess smoothing
plt.plot(y_lowess[:, 0], y_lowess[:, 1], 'b') # some noise removed
plt.show()
在估计每个y值时使用的数据分数参数可能需要变化。 增加frac
值可以增加平滑量。 frac
值必须介于0和1之间。
statsmodels lowess usage上有更多详细信息。
import pandas as pd
df = pd.DataFrame(y, x)
df_mva = df.rolling(30).mean() # moving average with a window size of 30
df_mva.plot(legend = False);
你可能需要尝试多个窗口大小来处理数据。
请注意,df_mva
的前30个值将是NaN
,但可以使用dropna
方法删除这些值。
pandas rolling function的使用细节。
最后,插值可以通过平滑来进行噪声降低。
from scipy.interpolate import Rbf
rbf = Rbf(x, y, function = 'quintic', smooth = 10)
xnew = np.linspace(x.min(), x.max(), num = 100, endpoint = True)
ynew = rbf(xnew)
plt.plot(xnew, ynew)
plt.show()
通过增加smooth
参数可以实现更平滑的逼近。考虑其他function
参数,包括'cubic'和'thin_plate'。在考虑function
值时,我通常先尝试'thin_plate',然后再尝试'cubic'; 'thin_plate' 产生了良好的结果,但需要非常高的smooth
值来处理此数据集,而'cubic'似乎无法处理噪声。
查看scipy docs中的其他Rbf
选项。Scipy提供其他一元和多元插值技术(请参见此tutorial)。
如果您的数据按照规律间隔采样,则LOWESS和滚动平均方法都能够提供更好的结果。
对于这个数据集,径向基函数插值可能过于复杂,但如果您的数据是高维的或者不在规则网格上采样,则一定值得关注。
需要注意所有这些方法;很容易消除过多噪声并扭曲基本信号。