ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

数学建模核心技能:插值与拟合的原理、Python实现与竞赛实战

数学建模核心技能:插值与拟合的原理、Python实现与竞赛实战 1. 从“猜数游戏”到数学建模为什么插值与拟合是预测的基石我刚开始接触数学建模的时候总觉得“预测”是个很玄乎的词好像非得用上什么高深的神经网络、复杂的时序模型才算数。直到有一次我接手一个关于城市用电负荷预测的项目手头的数据是每隔一小时采集一次但领导突然要求我给出每15分钟的负荷估算。看着那些离散的、像星星一样散落在时间轴上的数据点我瞬间懵了——中间那些“空白”时间点的负荷是多少总不能凭空瞎编吧。就是在那次我真正理解了插值和拟合这两个看似基础的工具它们不是什么花哨的“魔法”而是解决“数据不够用”和“规律看不清”这两大核心痛点的“脚手架”。没有它们很多预测模型根本无从谈起。简单来说你可以把插值和拟合想象成两种不同的“猜数游戏”。插值就像你已知一条绳子上几个固定结的位置要精准地描绘出绳子在这几个结之间每一处的弯曲形态要求必须完全穿过已知点。它解决的是“数据补全”问题比如从每小时数据推测每刻钟数据、从稀疏的测量点生成连续的地形曲面。而拟合则像是你看到一群飞鸟大致排成了“人”字形你画一条最符合它们整体趋势的斜线而不强求穿过每一只鸟。它解决的是“趋势把握”问题目的是找到数据背后隐藏的“函数关系”用于预测未知点。在数学建模尤其是预测类问题中这两者往往是预处理数据、理解系统、进而构建模型的关键第一步。无论是亚太杯数学建模中涉及的经济数据预测还是国赛中遇到的环境监测问题处理不完整、有噪声的数据时插值和拟合都是绕不开的基本功。2. 插值在已知点之间“精雕细琢”的艺术当你的数据点本身是精确的、可靠的但数量不足或分布不均无法满足连续分析或高密度输出的需求时插值就是你的首选工具。它的核心哲学是“尊重原始数据”构造的函数必须严格通过每一个已知数据点。2.1 线性插值快速但“棱角分明”的直尺这是最直观、计算量最小的方法。原理很简单在两个已知点之间连一条直线认为未知点就在这条直线上。假设我们有两个数据点 $(x_0, y_0)$ 和 $(x_1, y_1)$要计算 $x$ 在 $[x_0, x_1]$ 区间内某点的 $y$ 值公式为 $$y y_0 \frac{(y_1 - y_0)}{(x_1 - x_0)} (x - x_0)$$应用场景与实操Python示例 比如在分析某商品24小时内的销售数据每小时一个点你想估计凌晨2:30的销售额。用线性插值再合适不过。import numpy as np from scipy.interpolate import interp1d import matplotlib.pyplot as plt # 已知数据时间点小时和销售额 x_known np.array([2, 3]) # 凌晨2点和3点 y_known np.array([150, 180]) # 对应销售额 # 创建线性插值函数 f_linear interp1d(x_known, y_known, kindlinear) # 想要预测的时间点 x_new 2.5 y_pred f_linear(x_new) print(f凌晨2:30的预测销售额为{y_pred[0]} 元) # 可视化 x_plot np.linspace(2, 3, 100) y_plot f_linear(x_plot) plt.scatter(x_known, y_known, colorred, label已知数据点, zorder5) plt.plot(x_plot, y_plot, label线性插值曲线) plt.scatter([x_new], [y_pred], colorgreen, s100, label预测点 (2:30)) plt.xlabel(时间 (小时)) plt.ylabel(销售额 (元)) plt.legend() plt.grid(True, linestyle--, alpha0.7) plt.title(线性插值示例销售额估计) plt.show()注意线性插值最大的问题是会产生“折角”在数据点处不可导。如果你的物理过程或经济模型本身是光滑的比如物体运动轨迹、温度变化用线性插值就会显得很“生硬”不符合常理。它只适用于变化平缓、或你对中间过程光滑性无要求的场景。2.2 三次样条插值让曲线变得“丝滑”为了解决线性插值的光滑性问题三次样条插值应运而生。它不只是连接两点而是用一段三次多项式来连接每两个相邻点并且要求在所有连接点节点处不仅函数值连续一阶导数斜率和二阶导数曲率也连续。这就保证了整条曲线非常光滑。为什么是“三次”从数学上讲一次多项式直线太僵硬二次多项式在节点处保证一阶导数连续就需要全局调整计算复杂。三次多项式是能在相邻区间独立确定、同时满足函数值、一阶导、二阶导连续的最低阶多项式在计算复杂度和曲线光滑度之间取得了完美平衡。实操心得与一个关键参数 在SciPy中interp1d函数的kind参数选择cubic指三次样条即可。但这里有一个极易踩坑的点边界条件。# 使用更多数据点演示样条插值 x_known np.array([0, 2, 5, 8, 10]) y_known np.array([5, 8, 3, 6, 1]) # 默认边界条件‘not-a-knot’ f_spline_default interp1d(x_known, y_known, kindcubic) # 强制指定边界二阶导数为0自然样条 f_spline_natural interp1d(x_known, y_known, kindcubic, assume_sortedTrue, fill_valueextrapolate) # 注意上述代码中interp1d的kindcubic在数据点少于4个时会回退到三次多项式。 # 对于真正的样条插值更推荐使用CubicSpline类它能更直观地设置边界条件。 from scipy.interpolate import CubicSpline cs_natural CubicSpline(x_known, y_known, bc_typenatural) # 自然样条边界二阶导为0 cs_clamped CubicSpline(x_known, y_known, bc_typeclamped) # 固定边界一阶导需要指定导数值 x_plot np.linspace(0, 10, 200) plt.figure(figsize(12, 5)) plt.scatter(x_known, y_known, colorred, s100, label已知数据点, zorder5) plt.plot(x_plot, f_spline_default(x_plot), label默认样条 (not-a-knot), linewidth2) plt.plot(x_plot, cs_natural(x_plot), --, label自然样条 (二阶导为0), linewidth2) plt.xlabel(X) plt.ylabel(Y) plt.legend() plt.grid(True, linestyle--, alpha0.7) plt.title(不同边界条件的三次样条插值对比) plt.show()踩坑记录我曾用样条插值处理一段发动机转速信号数据两端变化剧烈。使用了默认边界条件后插值曲线在起始和结束位置产生了非常不合理的“震荡”和“过冲”导致后续分析出错。后来改为‘自然样条’边界二阶导数为0模拟无外力作用下的自然状态曲线两端才变得平缓合理。所以如果你的数据边界物理意义明确务必根据实际情况选择或推导边界条件不能无脑用默认设置。2.3 克里金插值当空间相关性说话时前面两种方法主要针对一维或可分离的多维数据。当地理空间、地质统计等领域的建模问题中数据点在二维或三维空间上分布且具有明显的空间自相关性即距离近的点更相似时克里金插值就闪亮登场了。它不仅是插值更是一种最优无偏估计。核心思想克里金认为未知点的值 $Z(u)$ 可以由周围已知点 $Z(u_\alpha)$ 的线性组合来估计 $$\hat{Z}(u) \sum_{\alpha1}^{n} \lambda_\alpha Z(u_\alpha)$$ 关键在于权重 $\lambda_\alpha$ 不是根据简单距离反比确定而是通过一个变差函数模型来计算这个模型描述了数据随距离变化的空间结构如相关性如何衰减。变差函数克里金的灵魂变差函数 $\gamma(h)$ 定义为相距为 $h$ 的两点之间方差的一半。通过已知数据点对我们可以拟合出一个变差函数模型如球状模型、指数模型、高斯模型。# 假设我们有一个空间数据集 (x, y, value) import numpy as np import pykrige.kriging_tools as kt from pykrige.ok import OrdinaryKriging import matplotlib.pyplot as plt # 生成模拟数据空间中有一些测量点 np.random.seed(42) n_points 20 x np.random.rand(n_points) * 100 y np.random.rand(n_points) * 100 # 假设值随x,y有趋势并加上一些空间相关的噪声 z 0.5*x 0.3*y 10*np.random.randn(n_points) np.random.randn(n_points)*5*(x/100) # 创建普通克里金对象 # 需要指定变差函数模型这里尝试‘spherical’球状模型 OK OrdinaryKriging(x, y, z, variogram_modelspherical, verboseFalse, enable_plottingFalse, nlags10) # nlags是计算变差函数时使用的距离分段数 # 生成需要插值的网格 gridx np.arange(0, 100, 2) gridy np.arange(0, 100, 2) z_pred, sigma OK.execute(grid, gridx, gridy) # sigma是克里金方差估计误差 # 绘图 plt.figure(figsize(15, 5)) # 子图1原始散点 plt.subplot(131) scatter plt.scatter(x, y, cz, s100, cmapjet, edgecolork) plt.colorbar(scatter, label测量值) plt.title(原始空间测量点) plt.xlabel(X坐标) plt.ylabel(Y坐标) plt.grid(True, alpha0.3) # 子图2克里金插值结果 plt.subplot(132) contour plt.contourf(gridx, gridy, z_pred.data, 20, cmapjet) plt.colorbar(contour, label插值预测值) plt.scatter(x, y, cblack, s20, alpha0.8) # 叠加原始点位置 plt.title(克里金插值表面) plt.xlabel(X坐标) plt.ylabel(Y坐标) # 子图3克里金标准差预测不确定性 plt.subplot(133) contour_sigma plt.contourf(gridx, gridy, sigma.data, 20, cmapYlOrRd) plt.colorbar(contour_sigma, label预测标准差) plt.scatter(x, y, cblack, s20, alpha0.8) plt.title(克里金预测标准差) plt.xlabel(X坐标) plt.ylabel(Y坐标) plt.suptitle(克里金空间插值示例) plt.tight_layout() plt.show()经验之谈克里金插值强大但门槛也高。最大的挑战在于变差函数模型的拟合。你需要通过实验半方差图选择一个合适的模型球状、指数、高斯等并拟合其参数块金值、基台值、变程。如果模型选得不好插值结果可能还不如简单的反距离加权法。在数学建模竞赛中如果选用克里金一定要在论文中展示你选择变差函数模型的依据和过程这是体现你建模严谨性的关键。3. 拟合寻找数据背后的“主旋律”拟合与插值的根本区别在于它承认数据有噪声、有误差不追求曲线穿过每一个点而是寻找一个函数形式使得该函数在整体上“最好地”逼近数据点。这个“最好”通常用残差平方和最小最小二乘法来衡量。3.1 线性回归从最简单的关系开始一元线性回归 $y ax b$ 是拟合的入门但千万别小看它。在数学建模中很多复杂关系可以通过变量变换转化为线性形式来处理。核心原理最小二乘法。目标是找到参数 $a$ 和 $b$使得 $\sum_{i1}^{n} (y_i - (ax_i b))^2$ 最小。通过求偏导数为零可以得到解析解正规方程。实操进阶多项式拟合与过拟合陷阱当我们用更高次的多项式 $y \sum_{k0}^{m} a_k x^k$ 去拟合数据时模型能力变强但风险也随之而来。import numpy as np import matplotlib.pyplot as plt from sklearn.preprocessing import PolynomialFeatures from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error # 生成带有噪声的非线性数据 np.random.seed(0) x np.linspace(0, 10, 30) y_true np.sin(x) * 2 0.5 * x # 真实关系正弦叠加线性趋势 y_noise y_true np.random.randn(30) * 0.8 # 加入噪声 x x.reshape(-1, 1) degrees [1, 3, 10, 15] # 尝试不同的多项式阶数 plt.figure(figsize(15, 10)) x_plot np.linspace(0, 10, 300).reshape(-1, 1) for i, degree in enumerate(degrees): # 生成多项式特征 poly PolynomialFeatures(degreedegree, include_biasFalse) x_poly poly.fit_transform(x) x_plot_poly poly.transform(x_plot) # 线性回归拟合 model LinearRegression() model.fit(x_poly, y_noise) y_plot_pred model.predict(x_plot_poly) y_train_pred model.predict(x_poly) train_mse mean_squared_error(y_noise, y_train_pred) # 绘图 plt.subplot(2, 2, i1) plt.scatter(x, y_noise, colorblue, alpha0.6, label带噪声数据) plt.plot(x_plot, y_plot_pred, colorred, linewidth2, labelf{degree}次拟合) plt.plot(x_plot, y_true, g--, alpha0.7, label真实关系, linewidth1.5) plt.title(f多项式阶数 {degree}\n训练集MSE: {train_mse:.4f}) plt.xlabel(X) plt.ylabel(Y) plt.legend() plt.grid(True, alpha0.3) plt.suptitle(多项式拟合从欠拟合到过拟合) plt.tight_layout() plt.show()从上图可以清晰看到1次线性明显欠拟合无法捕捉数据的波动。3次拟合效果较好既捕捉了趋势又不过分复杂。10次和15次过拟合。曲线疯狂扭曲以穿过每一个噪声点在数据点之间尤其是两端产生了剧烈的、不合理的震荡。它在训练集上误差MSE可能很小但对新数据的预测能力会极差。关键心得在数学建模论文中如果你使用了多项式拟合绝不能仅仅因为高阶多项式在训练集上误差更小就选择它。必须通过交叉验证、观察学习曲线、或使用正则化如岭回归、Lasso来防止过拟合。在论文中展示不同阶数模型的对比图并理性解释最终选择某阶数的原因如根据AIC/BIC准则、验证集误差最小是获得高分的关键。3.2 非线性拟合当关系不再“直来直去”现实世界更多是指数增长、饱和增长如逻辑斯蒂曲线、周期波动等复杂关系。这时就需要非线性拟合。以经典的逻辑斯蒂增长模型为例它常用于描述种群增长、产品生命周期、疫情传播等。模型公式$y \frac{L}{1 e^{-k(x - x_0)}}$ 其中$L$ 是增长上限$k$ 是增长率$x_0$ 是中心点。实操难点初始参数猜测非线性拟合通常使用迭代算法如Levenberg-Marquardt而算法收敛与否、收敛到哪个局部最优解严重依赖于初始参数值的猜测。import numpy as np import matplotlib.pyplot as plt from scipy.optimize import curve_fit # 定义逻辑斯蒂函数 def logistic_func(x, L, k, x0): return L / (1 np.exp(-k * (x - x0))) # 生成模拟数据如某APP的累计用户数 np.random.seed(123) x_data np.arange(0, 30, 1.5) # 真实参数L100, k0.5, x015 y_true logistic_func(x_data, L100, k0.5, x015) # 加入噪声 y_data y_true np.random.randn(len(x_data)) * 5 # 尝试拟合 # 难点如何给p0 (初始猜测) 一个合理的值 # 糟糕的初始猜测 p0_bad [50, 0.1, 5] # 与真实值相差甚远 # 根据数据观察的较好猜测 # L: 看数据最大值大概在100左右 # k: 增长快慢从数据看中等速度尝试0.3-0.8 # x0: 增长中心点数据在x15附近增长最快猜测15 p0_good [100, 0.5, 15] plt.figure(figsize(14, 5)) for i, (p0, label) in enumerate(zip([p0_bad, p0_good], [糟糕初始值, 较好初始值])): try: popt, pcov curve_fit(logistic_func, x_data, y_data, p0p0, maxfev5000) L_fit, k_fit, x0_fit popt print(f{label}拟合结果: L{L_fit:.2f}, k{k_fit:.2f}, x0{x0_fit:.2f}) y_fit logistic_func(x_data, *popt) plt.subplot(1, 2, i1) plt.scatter(x_data, y_data, label观测数据, colorblue) x_plot np.linspace(0, 30, 200) plt.plot(x_plot, logistic_func(x_plot, *popt), r-, label拟合曲线, linewidth2) plt.plot(x_plot, logistic_func(x_plot, 100, 0.5, 15), g--, alpha0.5, label真实曲线, linewidth1.5) plt.title(f{label}\n拟合: L{L_fit:.1f}, k{k_fit:.2f}, x0{x0_fit:.1f}) plt.xlabel(时间) plt.ylabel(用户数) plt.legend() plt.grid(True, alpha0.3) except Exception as e: print(f{label}拟合失败: {e}) plt.subplot(1, 2, i1) plt.scatter(x_data, y_data, colorblue) plt.title(f{label} - 拟合失败) plt.grid(True, alpha0.3) plt.suptitle(非线性拟合初始参数的重要性) plt.tight_layout() plt.show()避坑指南非线性拟合失败十有八九是初始值没设好。我的经验是1.可视化数据对参数物理意义做粗略估计如上限L、中点x0。2. 使用网格搜索在一个合理范围内尝试多组初始值。3. 如果可能将模型线性化后先用线性回归估计初始值如对逻辑斯蒂模型取对数变换。在论文中详细说明你如何确定初始参数值能体现你对模型深刻的理解和严谨的态度。3.3 拟合优度评价你的模型到底有多“好”拟合完模型不能只说“看起来不错”。需要用定量指标来评价。决定系数 R-squared ($R^2$)最常用的指标表示模型解释的数据变异比例。越接近1越好。 $$R^2 1 - \frac{SS_{res}}{SS_{tot}}$$ 其中 $SS_{res}$ 是残差平方和$SS_{tot}$ 是总平方和。调整后的R-squared当模型变量如多项式阶数增加时$R^2$ 总会增加这可能会误导。调整R-squared引入了惩罚项更适用于比较不同复杂度的模型。均方根误差 RMSE$\sqrt{MSE}$其量纲与原始数据相同更直观。比如预测房价RMSE5万元意味着平均预测误差在5万左右。赤池信息准则 AIC / 贝叶斯信息准则 BIC在模型复杂度参数个数和拟合优度之间取得平衡。用于模型选择值越小越好。它们惩罚了复杂模型有助于防止过拟合。from sklearn.metrics import r2_score, mean_squared_error import statsmodels.api as sm # 接续前面的多项式拟合例子评估3次和10次多项式模型 def evaluate_model(degree, x, y): poly PolynomialFeatures(degreedegree, include_biasTrue) # 这里include_biasTrue让statsmodels能识别截距 x_poly poly.fit_transform(x) # 使用statsmodels可以方便地得到AIC/BIC model sm.OLS(y, x_poly).fit() y_pred model.predict(x_poly) r2 r2_score(y, y_pred) rmse np.sqrt(mean_squared_error(y, y_pred)) aic model.aic bic model.bic return {degree: degree, R2: r2, RMSE: rmse, AIC: aic, BIC: bic, model_obj: model} # 评估 results [] for deg in [3, 10]: res evaluate_model(deg, x, y_noise) results.append(res) print(f阶数 {deg}: R2 {res[R2]:.4f}, RMSE {res[RMSE]:.4f}, AIC {res[AIC]:.2f}, BIC {res[BIC]:.2f}) # 打印更详细的统计报告以3次多项式为例 print(\n3次多项式模型的详细统计报告:) print(results[0][model_obj].summary())通过对比会发现10次多项式的R2可能略高于3次但其AIC和BIC值通常会显著更大这强烈提示10次模型存在过拟合。在数学建模论文中同时汇报R2、RMSE和AIC/BIC并进行对比分析会让你的模型评估部分非常扎实。4. 在数学建模竞赛中实战如何选择与融合在像国赛、美赛、亚太杯这样的数学建模竞赛中面对一个具体问题你该如何在插值和拟合之间做选择甚至结合使用4.1 问题诊断与工具选择流程图首先问自己几个问题数据状态我的数据是精确的测量值还是包含显著噪声的观测值核心需求我是需要填补缺失值、生成连续表面插值还是想发现变量间的函数关系、进行预测拟合数据特性数据在空间或时间上是否有相关性根据回答可以参考以下决策思路开始 | v 数据是否精确、可靠且需要确保通过每个已知点 |是 |否 v v 考虑插值方法 考虑拟合方法 | | v v 需要光滑曲线 关系大致是线性的 |是 |否 |是 |否 v v v v 样条插值 线性插值 线性回归 非线性拟合 | | | | v v v v (考虑边界条件) (评估过拟合风险) (谨慎设置初始参数) | | --------- 是空间数据 ------- |是 v 克里金插值 | v (分析变差函数)4.2 案例拆解2024年数学建模国赛C题“生产物料排序”思路联想虽然原题是关于排序优化但其中很可能涉及对设备处理能力随时间变化的建模。假设题目给出了设备在不同生产批次后的效率检测点数据稀疏且含噪声。步骤一数据审视与预处理拿到效率-时间数据点先画散点图观察。如果点很少且检测本身很精确可以考虑用样条插值来构建一个连续的处理能力函数用于后续优化模型的积分或微分运算。如果数据点较多且波动大有噪声则应该用拟合。观察趋势是线性下降设备磨损还是指数衰减或者是先升后降磨合期后老化选择一个合适的函数形式如指数衰减函数 $y A e^{-Bt} C$。步骤二模型构建与验证若选择拟合用部分数据如前80%训练用剩余20%验证计算RMSE和R2。比较不同模型线性、指数、多项式使用AIC/BIC选择。在论文中必须包含残差分析图。检查残差是否随机分布。如果残差呈现规律如U型说明模型函数形式选错了未能捕捉数据中的某种趋势。步骤三结果解释与集成将得到的拟合函数如效率衰减模型作为约束条件或目标函数系数集成到你的主优化排序模型中去。重要在论文中清晰阐述“为什么选择这个插值/拟合方法”并讨论该选择对最终优化结果的敏感性。例如“我们采用了三次样条插值来刻画设备效率的连续变化经测试将其替换为线性插值最终的总完工时间结果差异小于5%表明模型对该假设具有一定的鲁棒性。” 这样的分析能极大提升论文的深度。4.3 高级技巧局部加权回归与稳健拟合当数据不同区域的噪声水平或关系不一致时全局统一的模型可能失效。局部加权回归在预测每个点时给训练数据点赋予不同的权重距离预测点越近的点权重越大。这相当于为每个点局部地拟合一个低阶多项式能很好地捕捉局部特征。from statsmodels.nonparametric.smoothers_lowess import lowess # lowess 是局部加权散点平滑方法 smoothed lowess(y_noise, x.flatten(), frac0.3) # frac是用于局部回归的数据比例 plt.plot(smoothed[:, 0], smoothed[:, 1], r-, labelLOWESS平滑, linewidth3)稳健拟合最小二乘法对异常值非常敏感。一个离群点就能把直线“拉偏”。稳健拟合方法如RANSAC, Theil-Sen, Huber回归通过降低异常点的权重来得到更稳定的模型。from sklearn.linear_model import RANSACRegressor, LinearRegression # 假设数据中有几个异常点 x_ransac x.copy() y_ransac y_noise.copy() y_ransac[5] 50 # 制造一个异常点 y_ransac[15] -20 # 制造另一个异常点 # 普通最小二乘 lr LinearRegression().fit(x_ransac, y_ransac) # RANSAC ransac RANSACRegressor(random_state0).fit(x_ransac, y_ransac) # 绘图对比可以看到RANSAC的线基本不受异常点影响在数学建模中如果数据质量不高预处理时考虑使用这些稳健方法并在论文中说明是加分项。5. 从MATLAB到Python工具链的平滑过渡很多数模老手习惯用MATLAB其interp1,spline,polyfit,fit函数确实强大易用。但Python的生态现在已毫不逊色且更通用。MATLAB与Python关键函数对照表任务MATLAB函数Python (SciPy/NumPy/sklearn) 库与函数关键差异点一维插值interp1(x, y, xi, method)scipy.interpolate.interp1d(x, y, kind...)Python的interp1d返回一个函数更面向对象。样条插值spline(x, y)csape(x, y, 边界条件)scipy.interpolate.CubicSpline(x, y, bc_type...)CubicSpline对象功能更清晰边界条件设置明确。多项式拟合p polyfit(x, y, n)y_fit polyval(p, xi)numpy.polyfit(x, y, deg)numpy.polyval(p, xi)几乎一致。Python的np.polyfit也返回系数。非线性拟合fit(x, y, 模型名, StartPoint, p0)scipy.optimize.curve_fit(func, xdata, ydata, p0p0)curve_fit更通用但需要自己定义模型函数。MATLAB的fit有内置模型库。评价指标rsquare(需计算)模型对象的RMSE等sklearn.metrics.r2_scoresklearn.metrics.mean_squared_errorPython的sklearn.metrics模块提供了非常统一的评价函数接口。Python工作流示例# 一个完整的数据分析工作流可能如下 import pandas as pd import numpy as np from scipy import interpolate, optimize from sklearn.metrics import r2_score import matplotlib.pyplot as plt # 1. 读入数据 data pd.read_csv(your_data.csv) x, y data[x].values, data[y].values # 2. 数据清洗 (处理缺失值、异常值) # 例如用前后均值填充缺失值 y_filled pd.Series(y).interpolate(methodlinear).values # 3. 探索性分析 - 画图 plt.scatter(x, y_filled, alpha0.5) plt.title(Raw Data) plt.show() # 4. 根据需求选择方法 # 情况A: 需要插值到更密的网格 if needs_interpolation: f_interp interpolate.CubicSpline(x, y_filled, bc_typenatural) x_dense np.linspace(x.min(), x.max(), 500) y_dense f_interp(x_dense) # 情况B: 需要拟合趋势模型 if needs_fitting: def my_model(x, a, b, c): return a * np.exp(-b * x) c popt, pcov optimize.curve_fit(my_model, x, y_filled, p0[1, 0.1, 0]) y_pred my_model(x, *popt) r2 r2_score(y_filled, y_pred) print(f拟合R2: {r2:.3f})掌握这个工作流你就能用Python高效地解决大多数数模中遇到的插值与拟合问题。6. 常见“坑点”与我的调试心得插值外推的灾难无论是线性插值还是样条插值绝对不要轻易用于外推预测已知数据范围之外的点。插值函数在边界外的行为可能是发散的、毫无物理意义的。如果需要外推应该使用拟合模型并明确说明外推的不确定性极大。实战教训我曾用样条插值补充了月度数据然后下意识地用这个函数去预测了下个月的值结果得到了一个离谱的负数。后来改用ARIMA时间序列模型才解决了问题。拟合中的“异方差性”陷阱最小二乘假设误差是恒定方差的。如果你的数据误差随着x增大而增大异方差普通最小二乘的结果虽然无偏但不是最优的。此时应考虑加权最小二乘给误差小的点更高权重。诊断画残差图残差 vs. 预测值。如果出现漏斗形或扇形就存在异方差。解决scipy.optimize.curve_fit中的sigma参数可以传入各点的误差权重。多项式拟合的“龙格现象”在等距节点上用高阶多项式插值会在区间端点附近产生剧烈的振荡。这就是为什么我们更常用样条分段低阶多项式而不是全局高阶多项式来做插值。在拟合中同样要警惕高阶多项式它会导致过拟合模型在训练集外表现糟糕。克里金插值中的“无味”数据如果你的数据没有明显的空间结构即变差函数不随距离变化克里金会退化为简单的全局平均值。在执行克里金前一定要先做探索性空间数据分析计算并绘制实验变差函数图确认存在空间相关性后再使用。忽略单位与量纲在拟合物理或经济模型时参数通常有物理意义。如果x和y的单位量级相差巨大如x是纳米y是千米直接拟合会导致数值计算问题且参数难以解释。务必先进行标准化或归一化。from sklearn.preprocessing import StandardScaler scaler_x StandardScaler() scaler_y StandardScaler() x_scaled scaler_x.fit_transform(x.reshape(-1, 1)).flatten() y_scaled scaler_y.fit_transform(y.reshape(-1, 1)).flatten() # 在缩放后的数据上拟合 # 得到参数后如果需要解释原始尺度下的参数需进行逆变换对于线性模型较简单非线性模型会复杂。说到底插值和拟合是数学建模者手中的“手术刀”和“听诊器”一个用于精细修补数据一个用于诊断系统规律。没有最好的方法只有最合适的方法。每次动手前多花5分钟画图观察你的数据思考问题的本质往往能省下后面5个小时的调试时间。在竞赛论文里清晰展示你选择方法的思考过程、对比不同方法的结果、并诚实地讨论其局限性这比单纯堆砌一个高R2值要重要得多。
返回列表