如何使这个matplotlib图更干净?
我想知道怎么把这些有噪音的数据画成一条平滑的连续线,而不去考虑每一个具体的数值。我只想展示数据的整体趋势,让它看起来更好,不用在意那些噪音和极端值。这是我正在使用的代码:
import numpy
import sys
import matplotlib.pyplot as plt
from scipy.interpolate import spline
dataset = numpy.genfromtxt(fname='data', delimiter=",")
dic = {}
for d in dataset:
dic[d[0]] = d[1]
plt.plot(range(len(dic)), dic.values(),linestyle='-', linewidth=2)
plt.savefig('plot.png')
plt.show()
2 个回答
有很多方法可以做到这一点!
在这里,我将展示如何使用各种技术来减少噪声:
- 移动平均
- LOWESS回归
- 低通滤波器
- 插值
为了保持一致,我将使用@Hooked的示例数据:
import numpy as np
import matplotlib.pyplot as plt
X = np.arange(1, 1000, 1)
Y = np.log(X ** 3) + 10 * np.random.random(X.shape)
plt.plot(X, Y, alpha = .5)
plt.show()
- 移动平均
有时候,你只需要一个移动平均。
例如,使用pandas,窗口大小设置为100:
import pandas as pd
df = pd.DataFrame(Y, X)
df_mva = df.rolling(100).mean() # moving average with a window size of 100
df_mva.plot(legend = False);
你可能需要尝试不同的窗口大小来适应你的数据。注意,df_mva
的前100个值会是NaN,但可以通过dropna
方法去掉这些值。
有关pandas滚动函数的使用细节。
- LOWESS回归
我曾成功使用LOWESS(局部加权散点平滑)来去除重复测量数据集中的噪声。关于局部回归方法的信息,包括LOWESS和LOESS,可以在这里找到。这是一种简单的方法,只需调整一个参数,根据我的经验,效果很好。
以下是如何使用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]) # some noise removed
plt.show()
可能需要调整frac
参数,这个参数是用来估计每个y值时使用的数据比例。增大frac
值可以增加平滑程度。frac
的值必须在0到1之间。
有关statsmodels lowess使用的更多细节。
- 低通滤波器
Scipy提供了一系列低通滤波器,可能适合你的需求。
应用滤波器后:
from scipy.signal import lfilter
n = 50 # larger n gives smoother curves
b = [1.0 / n] * n # numerator coefficients
a = 1 # denominator coefficient
y_lf = lfilter(b, a, Y)
plt.plot(X, y_lf)
plt.show()
查看scipy lfilter文档,了解关于如何在差分方程中使用分子和分母系数的实现细节。
在scipy.signal包中还有其他滤波器。
- 插值
最后,这里是一个径向基函数插值的示例:
from scipy.interpolate import Rbf
rbf = Rbf(X, Y, function = 'multiquadric', smooth = 500)
y_rbf = rbf(X)
plt.plot(X, y_rbf)
plt.show()
通过增加smooth
参数,可以实现更平滑的近似。可以考虑的其他function
参数包括'cubic'和'thin_plate'。在选择function
值时,我通常先尝试'thin_plate',然后是'cubic';不过在这个数据集中,'thin_plate'和'cubic'似乎都对噪声处理得不太好。
在之前的一个回答中,我了解到了一种叫做Savitzky Golay滤波器的东西。这是一种特别的低通滤波器,非常适合用来平滑数据。你想要的曲线有多“平滑”其实是个人喜好的问题,这可以通过调整窗口大小和插值多项式的阶数来实现。下面是使用sg_filter
的食谱示例:
import numpy as np
import sg_filter
import matplotlib.pyplot as plt
# Generate some sample data similar to your post
X = np.arange(1,1000,1)
Y = np.log(X**3) + 10*np.random.random(X.shape)
Y2 = sg_filter.savitzky_golay(Y, 101, 3)
plt.plot(X,Y,linestyle='-', linewidth=2,alpha=.5)
plt.plot(X,Y2,color='r')
plt.show()