如何在scipy curve_fit中实现过初始点且约束初始斜率的曲线拟合?
带初始点与指定初始斜率约束的曲线拟合方案
针对你需要拟合曲线同时满足过初始点和指定初始斜率的需求,以下是两种可行的实现方案,同时修正了原代码中拟合函数的冗余参数问题:
方案1:手动构造损失函数(兼容所有scipy版本)
通过scipy.optimize.minimize自定义损失函数,将数据点拟合误差、初始点约束误差、初始斜率约束误差整合为一个目标函数,通过权重控制约束的优先级。
代码实现
import numpy as np import matplotlib.pyplot as plt from scipy.optimize import minimize # 生成原始数据 x_data = np.linspace(1, 2) p = [2, 3, -4, 1] y_data = np.polyval(p, x_data) # 约束参数 x0 = x_data[0] # 初始点x坐标 y0 = y_data[0] # 初始点y坐标 m0 = 8 # 指定初始斜率 # 修正后的拟合函数(原代码中参数b未被使用,此处补全) def fit_func(x, a, b, c): return a * np.exp(x) + b * np.sin(x) + c # 拟合函数的一阶导数(用于计算斜率) def fit_deriv(x, a, b, c): return a * np.exp(x) + b * np.cos(x) # 自定义损失函数 def loss(params): a, b, c = params # 普通数据点的加权拟合误差(对应原curve_fit的sigma设置) sigma = np.ones(len(x_data)) sigma[0] = 0.01 # 放大初始点权重,强化过点约束 data_error = np.sum(((fit_func(x_data, a, b, c) - y_data)/sigma)**2) # 初始斜率约束误差(用大权重确保约束优先满足) slope_error = 1000 * (fit_deriv(x0, a, b, c) - m0)**2 # 初始点约束误差(可选,已通过sigma强化,此处进一步加固) point_error = 1000 * (fit_func(x0, a, b, c) - y0)**2 return data_error + slope_error + point_error # 求解最优参数 initial_guess = [1, 1, 0] # 参数初始猜测值 result = minimize(loss, initial_guess) popt = result.x # 可视化结果 plt.plot(x_data, y_data, label='原始数据') plt.plot(x_data, fit_func(x_data, *popt), label='拟合曲线') # 标记初始点与指定斜率的切线 plt.scatter(x0, y0, color='red', zorder=5, label='初始点') tangent_x = np.linspace(x0-0.1, x0+0.1) tangent_y = y0 + m0*(tangent_x - x0) plt.plot(tangent_x, tangent_y, '--', color='red', label=f'指定初始斜率m0={m0}') plt.grid(True) plt.legend() plt.show()
方案2:使用curve_fit的内置约束(需scipy≥1.9.0)
从scipy 1.9.0开始,curve_fit支持通过constraints参数直接添加等式/不等式约束,无需手动构造损失函数,实现更简洁。
代码实现
import numpy as np import matplotlib.pyplot as plt from scipy.optimize import curve_fit # 生成原始数据 x_data = np.linspace(1, 2) p = [2, 3, -4, 1] y_data = np.polyval(p, x_data) # 约束参数 x0 = x_data[0] y0 = y_data[0] m0 = 8 # 修正后的拟合函数 def fit_func(x, a, b, c): return a * np.exp(x) + b * np.sin(x) + c # 拟合函数的一阶导数 def fit_deriv(x, a, b, c): return a * np.exp(x) + b * np.cos(x) # 定义等式约束:初始点函数值等于y0,初始斜率等于m0 constraints = [ {'type': 'eq', 'fun': lambda params: fit_func(x0, *params) - y0}, {'type': 'eq', 'fun': lambda params: fit_deriv(x0, *params) - m0} ] # 原sigma设置(可选,因已有等式约束,可省略) sigma = np.ones(len(x_data)) sigma[0] = 0.01 # 带约束拟合 popt, pcov = curve_fit( fit_func, x_data, y_data, sigma=sigma, constraints=constraints, p0=[1, 1, 0] ) # 可视化结果 plt.plot(x_data, y_data, label='原始数据') plt.plot(x_data, fit_func(x_data, *popt), label='拟合曲线') plt.scatter(x0, y0, color='red', zorder=5) tangent_x = np.linspace(x0-0.1, x0+0.1) tangent_y = y0 + m0*(tangent_x - x0) plt.plot(tangent_x, tangent_y, '--', color='red', label=f'指定初始斜率m0={m0}') plt.grid(True) plt.legend() plt.show()
关键注意事项
- 修正原代码问题:原
fit_func定义中包含参数b但未使用,导致拟合时b为冗余参数,已修正为b*np.sin(x)。 - 约束优先级:方案1中通过权重控制约束优先级,权重越大,约束越优先满足;方案2通过等式约束直接强制满足条件,无需权重设置。
- 版本兼容性:方案2需要scipy版本≥1.9.0,若版本较低,建议使用方案1。
内容的提问来源于stack exchange,提问作者Mechanician
相关产品推荐
相关产品推荐

