You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 15:06:24