如何实现让90%数据点位于拟合曲线上下的指数拟合?
指数拟合约束:让90%数据点位于曲线单侧的实现方法
你当前用scipy.optimize.curve_fit实现的指数拟合基于最小二乘逻辑,仅目标最小化整体残差平方和,无法直接约束数据点的分布比例。要实现90%数据点位于拟合曲线的上方或下方,可采用以下几种可行方法:
1. 自定义损失函数的加权约束拟合
通过修改损失函数,对不符合方向的点施加高额惩罚,迫使拟合结果满足比例要求。比如要让90%点在曲线下方,就对曲线上方的点(数量超过10%时)添加惩罚项:
import numpy as np from scipy.optimize import curve_fit, minimize import matplotlib.pyplot as plt def func(x, a, b, c): return a * np.exp(-b * x) + c # 自定义损失函数:确保不超过10%的点在曲线上方 def constrained_loss(params, x, y): a, b, c = params y_pred = func(x, a, b, c) residuals = y - y_pred # 统计位于曲线上方的点数量 above_count = np.sum(residuals > 0) # 基础损失为残差平方和,若超标则添加高额惩罚 base_loss = np.sum(residuals ** 2) if above_count > 0.1 * len(x): penalty = 1e10 * (above_count - 0.1 * len(x)) return base_loss + penalty return base_loss # 用原始拟合结果作为初始参数 popt_init, _ = curve_fit(func, xdata, ydata) # 执行带约束的优化 result = minimize(constrained_loss, popt_init, args=(xdata, ydata)) popt_constrained = result.x # 可视化对比 plt.scatter(xdata, ydata, color='r', label="Demanda máxima", alpha=0.5) plt.plot(xdata, func(xdata, *popt), 'r--', label='原始拟合') plt.plot(xdata, func(xdata, *popt_constrained), 'b-', label='约束后拟合') plt.legend() plt.show()
若需要90%点在曲线上方,只需把residuals > 0改为residuals < 0即可。
2. 分位数回归
分位数回归可直接拟合对应分位数的曲线:拟合90分位数曲线,能保证90%的数据点小于等于拟合值(位于曲线下方);拟合10分位数曲线,能保证90%的数据点大于等于拟合值(位于曲线上方)。
用scipy.optimize.minimize实现分位数回归的损失函数:
# 分位数损失函数,q为目标分位数(0.9对应90%点在下方) def quantile_loss(params, x, y, q): a, b, c = params y_pred = func(x, a, b, c) residuals = y - y_pred # 分位数损失:q倍正残差 + (1-q)倍负残差的绝对值 return np.sum(q * np.maximum(residuals, 0) + (1 - q) * np.maximum(-residuals, 0)) # 拟合90分位数曲线 result_q90 = minimize(quantile_loss, popt_init, args=(xdata, ydata, 0.9)) popt_q90 = result_q90.x # 拟合10分位数曲线(对应90%点在上方) result_q10 = minimize(quantile_loss, popt_init, args=(xdata, ydata, 0.1)) popt_q10 = result_q10.x
3. 手动筛选数据拟合
如果业务场景允许,可直接筛选出符合方向的90%数据点,用这些数据做常规指数拟合,再验证剩余10%点是否满足单侧要求。比如要90%点在下方,就选取y值最小的90%数据:
# 筛选y值最小的90%数据 sorted_indices = np.argsort(ydata) selected_indices = sorted_indices[:int(0.9 * len(ydata))] x_selected = xdata[selected_indices] y_selected = ydata[selected_indices] # 用筛选后的数据拟合 popt_filtered, _ = curve_fit(func, x_selected, y_selected)
这种方法操作简单,但会丢失部分数据信息,适合数据分布均匀的场景。
内容的提问来源于stack exchange,提问作者Luís Henrique Bandéria
相关产品推荐
相关产品推荐

