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

如何在Python中优化误差函数的峰区数据拟合效果

误差函数拟合峰区效果不佳的优化方案

我正在使用包含参数a,b,mu,sigma的误差函数对数据进行拟合,但峰区(如下方高亮区域)的拟合效果不佳。请问该如何优化误差函数的拟合效果?我也可通过修改func(x, a, b, mu, sigma)来提升峰区的拟合质量。

拟合效果示意图

现有拟合代码

import matplotlib.pyplot as plt
import scipy.optimize as optimize
import numpy as np
import pandas as pd
import scipy

def func(x, a, b, mu, sigma):
    y = a * scipy.special.erf((x-mu) / (sigma * np.sqrt(2))) + b
    return y

def fit_and_print_params(x, y, label, color, linestyle):
    # Initial guess for the parameters
    p0 = [1, 0, 1, 10]

    # Fit the curve
    popt, pcov = optimize.curve_fit(func, x, y, p0=p0, maxfev=8000)
    a_opt = popt[0]
    b_opt = popt[1]
    mu_opt = popt[2]
    sigma_opt = popt[3]

    print(f"Optimized values for {label}:")
    print("a =", a_opt)
    print("b =", b_opt)
    print("mu =", mu_opt)
    print("sigma =", sigma_opt)

    # Calculate R-squared
    y_fit = func(x, *popt)
    residuals = y - y_fit
    ss_residuals = np.sum(residuals**2)
    ss_total = np.sum((y - np.mean(y))**2)
    r_squared = 1 - (ss_residuals / ss_total)
    print("R-squared:", r_squared)

    plt.plot(x, func(x, *popt), label=f"Fitted Curve ({label})", color=color, linestyle=linestyle)

Folder1 = '4_1012_nodes_var_1_K_1e3_Cb_1e-3_D_1e-10'
Folder2 = '4_1012_nodes_var_5_K_1e3_Cb_1e-3_D_1e-10'
Folder3 = '4_1012_nodes_var_10_K_1e3_Cb_1e-3_D_1e-10'
Drive='D'

K=1.0e3
Cb0=0.001
DiffCoeff=1.0e-10

y1 = pd.read_csv(rf'{Drive}:\\Users\\debanikb\\OneDrive - Technion\\Research_Technion\\Python_PNM\\Surfactant A-D\\{Folder1}\\Averaged4_0.5__{K}_{Cb0}_{DiffCoeff}_501files.txt',header=None)
y1 =(1 - np.ravel(y1.to_numpy() / 1012))

y2 = pd.read_csv(rf'{Drive}:\\Users\\debanikb\\OneDrive - Technion\\Research_Technion\\Python_PNM\\Surfactant A-D\\{Folder2}\\Averaged4_0.5__{K}_{Cb0}_{DiffCoeff}_501files.txt',header=None)
y2 =(1 - np.ravel(y2.to_numpy() / 1012))

y3 = pd.read_csv(rf'{Drive}:\\Users\\debanikb\\OneDrive - Technion\\Research_Technion\\Python_PNM\\Surfactant A-D\\{Folder3}\\Averaged4_0.5__{K}_{Cb0}_{DiffCoeff}_501files.txt',header=None)
y3 =(1 - np.ravel(y3.to_numpy() / 1012))

x = pd.read_csv(rf'{Drive}:\\Users\\debanikb\\OneDrive - Technion\\Research_Technion\\Python_PNM\\Surfactant A-D\\{Folder1}\\All_values_0_1000001_10000.txt',header=None)
x = np.ravel(x.to_numpy())

plt.plot(x, y1, 'o',linewidth=0.5,label='Simulation (var 1)')
fit_and_print_params(x, y1, 'var 1', color='red', linestyle='--')

plt.plot(x, y2, 'o', label='Simulation (var 5)')
fit_and_print_params(x, y2, 'var 5', color='blue', linestyle='-.') 

plt.plot(x, y3, 'o', label='Simulation (var 10)')
fit_and_print_params(x, y3, 'var 10', color='black', linestyle=':')

plt.xlabel("Elapsed time (sec)",fontsize=15.0)
plt.ylabel("Invaded fraction",fontsize=15.0)
plt.legend(loc='lower right')
plt.xlim(0, 1e6)
plt.show()

具体优化方案

1. 修正初始参数猜测

当前初始值p0 = [1, 0, 1, 10]和实际数据偏差极大,尤其是mu(峰的中心位置)和sigma(峰的宽度)。建议从图中手动读取峰对应的x值作为mu初始值,根据峰的左右跨度估算sigma,比如如果峰在x=2e5附近,跨度约1e5,可以设置p0 = [0.5, 0.5, 2e5, 5e4],让拟合算法更快收敛到最优解,避免陷入局部最优。

2. 引入加权拟合

给峰区数据点更高权重,让拟合过程更关注该区域的误差。比如判断峰区范围(如1e5 < x < 5e5),给这些点设置权重2,其他点权重1,然后在curve_fit中传入权重的倒数作为残差标准差:

weights = np.where((x > 1e5) & (x < 5e5), 2, 1)
popt, pcov = optimize.curve_fit(func, x, y, p0=p0, maxfev=8000, sigma=1/weights)

3. 修改拟合函数形式

单一误差函数的灵活性不足,可尝试以下改进:

  • 添加峰区陡峭度参数:引入额外参数调整峰区的过渡斜率:
def func(x, a, b, mu, sigma, c):
    y = a * scipy.special.erf(c*(x-mu) / (sigma * np.sqrt(2))) + b
    return y
  • 叠加双误差函数:如果峰区存在复合特征,用两个误差函数叠加拟合:
def func(x, a1, b1, mu1, sigma1, a2, b2, mu2, sigma2):
    y1 = a1 * scipy.special.erf((x-mu1)/(sigma1*np.sqrt(2))) + b1
    y2 = a2 * scipy.special.erf((x-mu2)/(sigma2*np.sqrt(2))) + b2
    return y1 + y2
  • 替换为逻辑斯蒂函数:逻辑斯蒂函数的S型过渡更灵活,可能更适配你的峰区形状:
def func(x, a, b, mu, sigma):
    y = a / (1 + np.exp(-(x - mu)/sigma)) + b
    return y

4. 约束参数范围

通过bounds参数限制参数的物理合理范围,避免拟合出异常值:

# 示例:a在0-1,b在0-1,mu在0-1e6,sigma在1e4-1e5
bounds = ([0, 0, 0, 1e4], [1, 1, 1e6, 1e5])
popt, pcov = optimize.curve_fit(func, x, y, p0=p0, maxfev=8000, bounds=bounds)

5. 数据预处理

  • 检查峰区是否存在异常值,剔除偏离趋势的点;
  • 对x轴做对数变换(如果数据随时间呈指数变化),让峰区特征更明显:
x_log = np.log10(x[x>0])  # 避开x=0的对数无意义点
# 用x_log拟合后,再转换回原x轴绘制曲线

内容的提问来源于stack exchange,提问作者KeplerNick123

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 09:15:06