使用seaborn/matplotlib为时间戳x轴的销量数据绘制平滑拟合线
平滑拟合线实现方案
错误原因
你此前的代码存在两个核心问题:
- 生成插值采样点
xnew时误用了销量sales的数值区间,应该使用时间day_id的区间 scipy插值方法不支持直接传入datetime类型的时间戳,需要先转换为数值格式
方案1:修正的样条插值实现
import numpy as np import pandas as pd import seaborn as sns import matplotlib.pyplot as plt from scipy.interpolate import make_interp_spline # 原有绘图逻辑 sns.lineplot(x = 'day_id', y = 'sales', data = df, label='实际销量') plt.axvline('2021-08-28',0,1, ls = '--', lw = 3, c ='black') plt.text('2021-08-28',5,'2021-08-28', ha='center', va='center', bbox={'facecolor':'white','alpha':1,'edgecolor':'none','pad':1}) # 转换时间为数值型时间戳 x_ts = df['day_id'].astype(np.int64) # 生成300个均匀时间采样点 xnew_ts = np.linspace(x_ts.min(), x_ts.max(), 300) # 三次样条拟合 spl = make_interp_spline(x_ts, df['sales'], k=3) y_smooth = spl(xnew_ts) # 数值时间戳转回日期格式,适配原有x轴 xnew_date = pd.to_datetime(xnew_ts) # 绘制平滑线 plt.plot(xnew_date, y_smooth, c='red', lw=2, label='平滑趋势线') plt.legend() plt.show()
方案2:更易用的局部加权平滑(LOWESS)实现
不需要手动处理时间格式转换,直接用seaborn内置方法完成拟合,更适合快速展示整体趋势:
import seaborn as sns import matplotlib.pyplot as plt import pandas as pd # 原有绘图逻辑 sns.lineplot(x = 'day_id', y = 'sales', data = df, label='实际销量') plt.axvline('2021-08-28',0,1, ls = '--', lw = 3, c ='black') plt.text('2021-08-28',5,'2021-08-28', ha='center', va='center', bbox={'facecolor':'white','alpha':1,'edgecolor':'none','pad':1}) # 绘制LOWESS平滑线,scatter=False关闭散点绘制 sns.regplot(x=df['day_id'].astype(np.int64), y=df['sales'], lowess=True, scatter=False, color='red', line_kws={'lw':2}, label='平滑趋势线') # 还原x轴为日期显示格式 plt.xticks(plt.xticks()[0], pd.to_datetime(plt.xticks()[0]).strftime('%Y-%m-%d')) plt.legend() plt.show()
可通过调整lowess的frac参数控制平滑程度,默认值为0.6,数值越大拟合线越平滑。如果只需要整体升降趋势,可以去掉lowess=True参数,使用默认的线性拟合即可。
内容的提问来源于stack exchange,提问作者Jonas Palačionis
相关产品推荐
相关产品推荐

