使用scipy.curve_fit拟合函数a x +b log((x-x0)/c)失败求助
解决scipy curve_fit拟合自定义对数函数的收敛问题
嘿,我来帮你搞定这个拟合失败的问题!你遇到的maxfev超限报错,主要是因为几个关键细节没处理到位,我一步步给你拆解并给出修复后的完整方案:
问题根源分析
- 函数定义域踩坑:你的拟合函数
a*x + b*np.log((x-x0)/c)里,对数项要求(x-x0)/c > 0。如果初始参数猜得不对,很容易出现负数/零取对数的情况,函数返回NaN直接打断拟合流程。 - 参数拼写错误:你把
maxfev(最大函数评估次数)写成了maxrev,这是个低级笔误,但会导致参数不生效。 - 缺少合理的初始参数猜测:curve_fit默认用全1的初始值,但你的函数有4个参数,和实际数据的参数范围偏差太大时,拟合算法根本找不到收敛方向。
修复后的完整代码
import matplotlib.pyplot as plt import pandas as pd from scipy.optimize import curve_fit import numpy as np # 定义拟合函数,加入定义域防护避免NaN def func(x, a, b, c, x0): arg = (x - x0)/c # 把小于等于0的输入替换成极小正数,防止对数报错 arg = np.where(arg <= 0, 1e-10, arg) return a * x + b * np.log(arg) # 读取数据 data1 = pd.read_csv(r'C:\Users\TanviPradhan\OneDrive - EFFECT Photonics\Desktop\CD581379_A-12469303362S__GAIN_DIODE_TEST__2021-08-03_16.57.36.txt', skipinitialspace=True, comment="%") # 提取x和y数据(转成numpy数组更稳定) x = data1["InjectedCurrent(mA)"].values y = data1["GAIN_X(V)"].values # 拆分训练/测试集 train_x = x[:80] train_y = y[:80] test_x = x[80:] test_y = y[80:] # -------------------------- # 核心关键:给出贴合数据的初始参数猜测p0 # -------------------------- # 先估算线性部分的斜率a a_guess = np.polyfit(x, y, 1)[0] # x0必须小于所有x的最小值,这里取x最小值的90% x0_guess = x.min() * 0.9 # c先设为1,后续拟合会自动调整 c_guess = 1.0 # b根据数据波动先给个小初始值 b_guess = 0.1 p0 = [a_guess, b_guess, c_guess, x0_guess] # 添加参数边界约束(可选但推荐):确保x0小于x最小值,c为正 bounds = ([-np.inf, -np.inf, 1e-5, -np.inf], [np.inf, np.inf, np.inf, x.min() - 1e-5]) try: # 执行拟合,修正maxfev拼写,传入初始参数和边界 params, pcov = curve_fit(func, x, y, p0=p0, maxfev=1000000, bounds=bounds) a_fit, b_fit, c_fit, x0_fit = params print(f"拟合完成!参数:a={a_fit:.4f}, b={b_fit:.4f}, c={c_fit:.4f}, x0={x0_fit:.4f}") # 生成拟合曲线用于可视化 x_fit = np.linspace(x.min(), x.max(), 100) y_fit = func(x_fit, *params) # 绘图展示训练/测试数据和拟合结果 fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(7,14)) fig.suptitle('Wafer xx') # 训练数据子图 ax1.scatter(train_x, train_y, label='Training Data') ax1.plot(x_fit, y_fit, 'r-', linewidth=2, label='Fitted Curve') ax1.set_xlabel("Injected Current (mA)") ax1.set_ylabel("GAIN_X(V)") ax1.set_title('Training Data & Fitted Curve') ax1.grid(True) ax1.legend() # 测试数据子图 ax2.scatter(test_x, test_y, label='Testing Data') ax2.plot(x_fit, y_fit, 'r-', linewidth=2, label='Fitted Curve') ax2.set_xlabel("Injected Current (mA)") ax2.set_ylabel("GAIN_X(V)") ax2.set_title('Testing Data & Fitted Curve') ax2.grid(True) ax2.legend() plt.tight_layout() plt.show() except Exception as e: print(f"拟合过程出错:{str(e)}")
额外小贴士
- 如果还是不收敛,可以先拿前20-30个趋势最明显的数据点拟合,得到的参数再作为初始值拟合全量数据。
- 检查你的数据里有没有异常值(比如突然跳变的点),异常值会严重干扰拟合,建议先做清洗。
- 要是curve_fit还是搞不定,可以试试
scipy.optimize.least_squares,它对复杂非线性函数的拟合稳定性更强。
内容的提问来源于stack exchange,提问作者Tanvi Pradhan
相关产品推荐
相关产品推荐

