正弦曲线拟合问题:拟合得到的振幅和频率远低于实际值
正弦曲线拟合温度时间序列失败问题解决方案
原始问题

我有一组时间序列数据,对应图中的黑线(为滑动均值结果)。我尝试对其进行正弦曲线拟合但未成功,当前拟合结果为接近直线的浅蓝线。(深蓝色为多项式拟合结果,可暂时忽略)。
我所尝试的方法如下:
def objective(x, a, b, c, d): return a * np.sin(b - x) + c * x**2 + d
from scipy import optimize X = X.ravel() # 序数日期构成的一维数组 y = y.ravel() # 温度值构成的一维数组 # 使用目标函数进行曲线拟合 params, _ = optimize.curve_fit(objective, X, y) # 绘制拟合曲线 plt.plot(X_extended, objective( X, params[0], params[1], params[2], params[3]))
即便我设置了极低频率或极高振幅的初始猜测值,拟合结果几乎没有变化。
解决方案
问题原因
你定义的目标函数存在两处核心缺陷导致拟合失败:
- 正弦项的角频率被固定为
1,没有作为可拟合参数开放自由度,无法匹配温度序列的年/季度周期特征 - 非线性拟合对初始参数值敏感度极高,你未传入符合数据特征的初始值,默认的全1初始值直接导致拟合陷入局部最优
修改方案
1. 调整目标函数,增加角频率参数
import numpy as np def objective(x, a, omega, phi, c, d): # 参数说明:a为正弦振幅,omega为角频率,phi为相位,c为二次项系数,d为全局偏移量 return a * np.sin(omega * x + phi) + c * x**2 + d
如果你的数据没有明显的二次趋势,也可以将二次项替换为线性项k*x,进一步降低拟合难度。
2. 传入合理初始参数值
根据温度序列的常规特征设置初始值,以日度序列的年周期为例:
from scipy import optimize X = X.ravel() y = y.ravel() # 初始值可根据你的数据实际范围调整,以下为通用参考值 p0 = [ 10, # 温度年波动振幅约为±10℃,可根据你的数据极差调整 2*np.pi/365, # 年周期对应的角频率,若为月度数据则改为2*np.pi/12 0, # 相位初始值 1e-8, # 二次趋势项系数,若改为线性项可设为1e-5量级 np.mean(y) # 全局偏移量直接用温度均值初始化 ] params, _ = optimize.curve_fit(objective, X, y, p0=p0) # 绘制拟合曲线时注意传入扩展后的X序列 plt.plot(X_extended, objective(X_extended, *params))
效果验证
按上述方案调整后即可得到符合预期的拟合结果:
可选优化
如果你的数据存在多周期特征(比如年周期叠加周周期),可以叠加多个正弦项分别设置对应频率的初始值,拟合效果会更优。
内容的提问来源于stack exchange,提问作者mneumann
相关产品推荐
相关产品推荐

