scipy.optimize.curve_fit简单谐波拟合失效问题排查
问题背景
尝试对数据集做曲线拟合,扣除拟合分量以消除数据中的长期变化,预期效果如下:
- 蓝色曲线:原始数据(已添加固定偏移量便于观察差异)
- 橙色曲线:拟合并扣除拟合项后的结果
当前拟合功能异常,实际输出效果如下:
该代码此前可正常运行(上方正确效果图即为历史运行生成),未改动核心代码却出现拟合失败问题,已尝试调整参数初始猜测值、设置参数边界等可查的解决方案,均未解决问题。
原实现代码
import matplotlib.pyplot as plt import numpy as np import pandas as pd from scipy.optimize import curve_fit df = pd.read_csv('stack1.csv') def harmonics(day, A, B, C, D): return A * np.sin( (2 * np.pi * day)/365.25 ) + B * np.cos( (2 * np.pi * day)/365.25 ) + C * np.sin( (4 * np.pi * day)/365.25 ) + D * np.cos( (4 *np.pi * day)/365.25 ) popt, pcov = curve_fit(harmonics, df['X'], df['Y']) Y_fit = list(np.asarray(df['Y']) - harmonics(np.asarray(df['X']), popt[0],popt[1],popt[2],popt[3])) plt.plot(df['X'],df['Y']+0.7) plt.plot(df['X'],Y_fit) plt.show()
故障原因
- 拟合函数结构缺失:原函数仅包含年周期、半年周期的正弦、余弦项,没有设置常数偏移项,只要数据基线不是严格围绕0波动,谐波拟合就会完全失真;如果数据存在线性长期变化趋势,缺失线性趋势项也会直接导致拟合失败。
- 拟合收敛逻辑出错:未给
curve_fit传入合理初始参数,当X轴是累计日期序列(数值通常达数千甚至数万)时,优化器会从默认全1的初始参数出发,直接收敛到错误的局部极小值;大数直接传入三角函数计算也容易引入浮点精度误差,进一步放大拟合偏差。
修复方案
补全拟合函数的常数项、线性趋势项,对X轴做中心化处理减少浮点误差,同时传入合理的初始参数提升收敛稳定性,修改后代码如下:
import matplotlib.pyplot as plt import numpy as np import pandas as pd from scipy.optimize import curve_fit df = pd.read_csv('stack1.csv') x = df['X'].values y = df['Y'].values # X轴中心化,降低大数计算的浮点误差 x_centered = x - x[0] def harmonics(day, A, B, C, D, offset, trend): year_period = 2 * np.pi * day / 365.25 half_year_period = 4 * np.pi * day / 365.25 return A * np.sin(year_period) + B * np.cos(year_period) + \ C * np.sin(half_year_period) + D * np.cos(half_year_period) + \ offset + trend * day # 传入合理初始参数,避免收敛到局部极小值 p0 = [0.5*y.std(), 0.5*y.std(), 0.25*y.std(), 0.25*y.std(), y.mean(), 0] popt, pcov = curve_fit(harmonics, x_centered, y, p0=p0) # 扣除拟合的周期、趋势分量 Y_detrended = y - harmonics(x_centered, *popt) plt.plot(x, y + 0.7, label='原始数据(加0.7偏移)') plt.plot(x, Y_detrended, label='去周期去趋势结果') plt.legend() plt.show()
如果只需要去除周期分量、保留线性趋势,删除函数中的
trend相关项即可,但常数偏移项必须保留,否则会出现基线不匹配导致拟合失效。
内容的提问来源于stack exchange,提问作者Billiam
相关产品推荐
相关产品推荐

