scipy curve_fit结合mpmath拟合时报cannot create mpf from错误
Scipy curve_fit 结合mpmath拟合报错修复
报错核心原因
curve_fit接口调用规则不匹配:该接口要求传入的模型函数第一个参数必须为自变量,后续参数为待拟合的标量参数,原代码定义的函数入参顺序为(参数列表c, 自变量t),顺序完全相反;且调用时写为function(c,x['Time (sec)']),是提前执行函数把返回的数组传给了curve_fit,而非传入函数本身,导致curve_fit内部迭代传参时把整个参数列表当成单个值传入计算,mpmath接收到列表类型无法转换为高精度浮点数,触发类型错误。- mpmath与numpy混用逻辑错误:mpmath原生仅支持标量输入,无法直接处理numpy数组、Python列表类型的批量输入,直接传入列表/数组时会触发类型报错。
- 拟合完成后的函数调用参数顺序、参数个数均和原函数定义不匹配,即使前面拟合成功也会运行报错。
修复方案
- 调整模型函数的入参顺序,严格遵循curve_fit要求:自变量t作为第一个入参,5个待拟合参数作为单独的后续入参,避免参数打包传递导致的顺序混乱。
- 对mpmath的标量函数做向量化包装,使其支持numpy数组的批量计算,无需手动转list传递,同时避免类型错误。
- 修正curve_fit调用逻辑:直接传入函数名(不要加括号提前执行),按参数顺序传入初始猜测值
p0。 - 统一数值计算逻辑:numpy原生支持的指数、对数运算直接用numpy实现,比mpmath运算速度快2~3个数量级,也不会出现类型兼容问题;仅
polylog这类numpy没有的特殊函数保留mpmath实现,同时替换原代码中2.71**的近似写法为np.exp,提升计算精度。 - 修正拟合完成后的预测值计算逻辑,参数顺序和新的函数定义保持一致。
修复后完整代码
import mpmath as mpm import pandas as pd import matplotlib.pyplot as plt from scipy.optimize import curve_fit import numpy as np # 将mpmath的标量函数包装为支持numpy数组批量计算的向量化版本 polylog_vec = np.vectorize(mpm.polylog) mp_exp_vec = np.vectorize(mpm.exp) mp_log_vec = np.vectorize(mpm.log) # 读取实验数据 x = pd.read_csv('pyex.csv') n = len(x['Time (sec)']) # 常量定义 T = 303.15 R = 0.082 P0 = 17.18 # 模型函数:第一个入参为自变量t,后续依次为5个待拟合参数 def function(t, c0, c1, c2, c3, c4): A11 = 1.0 + mp_exp_vec((-t/c0) + c1) A22 = c2 * np.exp(-c3/(R*T)) A33 = (P0/c4) - 1 AA = np.abs(polylog_vec(2, A11)) b11 = (1/(1-A11))**2 b22 = mp_log_vec(A11**2) B1 = mp_log_vec(b11) + b22 C11 = A11/(A11-1) C = mp_log_vec(mp_exp_vec(c1)*C11) Z = c0*(AA + 0.5*(B1 - C)*C) D = 1 + np.exp(c1) E = np.abs(polylog_vec(2, D)) G = np.log(np.exp(-2*c1)) H = np.log(D**2) - np.log(D) const = -c0*(E + 0.5*(G+H)*np.log(D)) I = Z + const return 1 - np.exp(A22*A33*I) # 初始参数猜测值,顺序和函数入参一一对应 p0 = [255.00, 0.055, 0.01, 0.007, 16.63] # 绘制初始参数对应的曲线 y_init = function(x['Time (sec)'].values, *p0) plt.plot(x['Time (sec)'], x['F (exp)'], 'o', label='实验数据') plt.plot(x['Time (sec)'], y_init, 'r', label='初始参数曲线') plt.xlabel('Time (sec)') plt.ylabel('F (exp)') plt.legend() plt.show() # 提取拟合用的数组格式数据 t = x['Time (sec)'].values.flatten() hr = x['F (exp)'].values.flatten() # 正确调用curve_fit执行拟合 popt, pcov = curve_fit(function, t, hr, p0=p0) print("拟合得到的参数:c0={:.4f}, c1={:.4f}, c2={:.4f}, c3={:.4f}, c4={:.4f}".format(*popt)) # 绘制拟合结果 y_fit = function(t, *popt) plt.plot(t, hr, 'o', label='实验数据') plt.plot(t, y_fit, 'r', label='拟合曲线') plt.xlabel('Time (sec)') plt.ylabel('F (exp)') plt.legend() plt.show()
注意:如果拟合过程中出现RuntimeWarning提示除零、对数无效等问题,可根据参数的物理意义给curve_fit传入
bounds参数设置合理的参数取值范围,避免迭代过程中参数取到非法值。
内容的提问来源于stack exchange,提问作者Surajit Hazra
相关产品推荐
相关产品推荐

