2.3 插值与样条


文档摘要

2.3 插值与样条 本节摘要:插值是根据已知离散点构造连续函数的技术。scipy.interpolate 的 interp1d 提供阶梯、线性、二次、三次等插值方式,要求曲线精确穿过每个数据点,适合无噪声数据;UnivariateSpline 用平滑参数 s 控制弯曲程度,在噪声数据上做"拟合式"的光滑样条;griddata 处理散点数据的网格插值。本节的核心提醒:精确穿过每个点的插值遇上有噪声的数据会放大噪声,而外推几乎总是危险的。 2.3 插值与样条 上手前先明确 阅读完本节,你应当能够: 说出 interp1d 各插值方式的差别,并为一组数据选出合适的方式; 解释为什么有噪声的数据不能用精确插值,而是用平滑样条;

2.3 插值与样条

本节摘要:插值是根据已知离散点构造连续函数的技术。scipy.interpolate 的 interp1d 提供阶梯、线性、二次、三次等插值方式,要求曲线精确穿过每个数据点,适合无噪声数据;UnivariateSpline 用平滑参数 s 控制弯曲程度,在噪声数据上做"拟合式"的光滑样条;griddata 处理散点数据的网格插值。本节的核心提醒:精确穿过每个点的插值遇上有噪声的数据会放大噪声,而外推几乎总是危险的。

2.3 插值与样条

上手前先明确

阅读完本节,你应当能够:

  1. 说出 interp1d 各插值方式的差别,并为一组数据选出合适的方式;
  2. 解释为什么有噪声的数据不能用精确插值,而是用平滑样条;
  3. 用 UnivariateSpline 调节平滑参数 s,理解过拟合与欠拟合的平衡;
  4. 用 griddata 对散点数据做网格插值,并说明各方法的代价;
  5. 识别外推的适用边界,避免在数据范围外盲信插值结果。

一、问题与直觉

仪器记录温度时偶尔丢包,观测网络总有站点缺测,画图软件要求连续曲线而数据只有几十个点。这些场景的共同点:数据是离散的,但我们需要连续的。插值就是"在已知点之间怎么猜"的学问。

关键在于,插值和上一节的拟合是两种不同的哲学。拟合承认数据有噪声,去找一条"离所有点最近"的曲线,不要求穿过任何一个点;插值假设数据可信,要求构造的曲线精确穿过每个已知点。判断该用哪种,就看一个问题的答案:这些点是精确测量,还是带噪声的读数?选错哲学,结果会从根上坏掉——把噪声数据硬插值,曲线会在每个噪声点上剧烈抖动,比原始数据还难看。

我们先把"动手"的部分做掉。对一维数据,interp1d 是最快上手的人口,它把"用哪段、怎么连"的决策压缩成 kind 一个参数;数据带噪声时换 UnivariateSpline,把"穿不穿过点"的决策压缩成 s 一个参数;散点数据没有规则网格,交给 griddata。三个工具,三种数据形态,一一对应。

二、核心原理

2.1 interp1d:一个参数决定连接方式

import numpy as np from scipy.interpolate import interp1d x = np.linspace(0, 10, 11) y = np.sin(x) + 0.05 * np.random.default_rng(1).normal(size=x.size) f_linear = interp1d(x, y, kind="linear") f_cubic = interp1d(x, y, kind="cubic") print(f_linear(5.5), f_cubic(5.5))

interp1d 返回一个可调用对象,把自变量喂给它就得到插值结果。kind 参数从零阶到三阶各有用处:previous 和 next 输出阶梯形常数,适合"最近时刻的状态"这类语义;linear 是两点的直线连接,简单、稳定、永远不会在两点之间蹦出离谱值;quadratic 与 cubic 用相邻的几个点拟合一簇抛物线或三次曲线,曲线光滑,但代价是可能出现局部过冲——在剧烈变化的数据上,三次插值可能在两点之间拱出比两端都高的尖峰,这叫龙格现象的近亲。

f_step = interp1d(x, y, kind="previous") print(f_step(5.5))

选择建议:数据点密集、变化平缓,linear 足够且最安全;数据点稀疏、需要视觉上的光滑曲线,用 cubic;数值积分或微分需要二阶导数连续,考虑更高阶样条。quadratic 的处境比较尴尬,它比 linear 光滑却不如 cubic 通用,除了特定平滑需求,用得不多。

interp1d 是历史最久的统一入口,新版 SciPy 还提供面向对象的 CubicSpline 与 PchipInterpolator,接口更细、可控性更强。CubicSpline 默认就是三次样条,行为与 interp1d 的 cubic 一致;PchipInterpolator 是保形插值,数据单调时它保证插值结果也单调,不会在两点之间拱出违反物理的尖峰。对浓度随时间下降这类天然单调的数据,保形插值比普通三次更稳,值得记在备选名单里。

2.2 UnivariateSpline:噪声数据的出路

精确穿过每个点,在有噪声的数据上会原样放大噪声。UnivariateSpline 换了个目标:不要求穿过点,只要求曲线"足够接近"且"足够光滑",两者的平衡由平滑参数 s 控制。

from scipy.interpolate import UnivariateSpline x = np.linspace(0, 10, 80) y = np.sin(x) + 0.3 * np.random.default_rng(2).normal(size=x.size) spl_smooth = UnivariateSpline(x, y, s=2.0) spl_tight = UnivariateSpline(x, y, s=0.05) xs = np.linspace(0, 10, 300) print(spl_smooth(xs).round(3)[:5]) print(spl_tight(xs).round(3)[:5])

s 越小,样条越贴近数据点,噪声被原样吸收;s 越大,样条越平直,可能连真实趋势都被抹平。s 取零等价于精确插值,一般不建议在有噪声的数据上这么做。选 s 的实用办法:先跑一次让样条自动估计合适值,再看曲线形态决定加大还是减小。别指望一个万能数值——同样的 s 在不同数据量、不同噪声水平下含义完全不同。

样条对象还提供额外福利:get_knots 返回节点位置,derivative 可以直接求导,integral 可以直接积分,antiderivative 可以求原函数。这意味着"先插值再求导或积分"不再是两段代码的事,而是同一个对象的两个方法。实验数据往往只能给出位置或速度中的一个,另一个要靠求导或积分补出来,这个能力省下的工作量相当可观。

2.3 griddata:散点数据的网格化

一维插值要求 x 有序;二维及以上,数据经常是"一堆散点"——气象站的经纬度与气温、传感器的平面坐标与读数。griddata 把这些散点插到规则网格上,供等值线图或热力图使用:

from scipy.interpolate import griddata rng = np.random.default_rng(3) points = rng.uniform(0, 10, size=(60, 2)) values = np.sin(points[:, 0] / 2) * np.cos(points[:, 1] / 2) grid_x, grid_y = np.mgrid[0:10:50j, 0:10:50j] zi_linear = griddata(points, values, (grid_x, grid_y), method="linear") zi_cubic = griddata(points, values, (grid_x, grid_y), method="cubic") print(np.isnan(zi_linear).sum(), np.isnan(zi_cubic).sum())

method 参数三选一:nearest 用最近邻填充,快、无 NaN,但结果呈块状;linear 用 Delaunay 三角剖分在每个三角形内做线性插值,平滑与保真平衡最好,是默认推荐;cubic 在每个三角形上构造三次多项式,视觉最光滑,但计算慢且可能出现过冲。超出散点凸包的位置会返回 NaN——这不是 bug,是算法在告诉你"这里没有数据支撑,别信我"。

zi_nearest = griddata(points, values, (grid_x, grid_y), method="nearest") print(zi_nearest.shape)

散点覆盖不均匀时,凸包外的 NaN 区域会很大,nearest 是唯一能全图填满的选项,代价是块状边界。另一个现实问题:散点太少(比如只有几十个点却要填几百乘几百的网格),cubic 会在空荡荡的三角形上画出夸张的曲面,这时 linear 反而更诚实。插值方法没有绝对好坏,只有"数据形态配不配得上方法"。

2.4 外推:插值最危险的延伸

插值只在数据范围内可靠,出了范围叫外推。interp1d 默认在范围外抛异常,这是刻意的设计:宁可报错,也不给你一个看着像样的错数。需要程序不报错时,用 fill_value 指定边界外的值,或者明确接受外推:

f_ext = interp1d(x, y, kind="linear", bounds_error=False, fill_value=(y[0], y[-1])) print(f_ext(-1.0), f_ext(12.0))

fill_value 传一个二元组,左边界取首值、右边界取末值,语义是"超出范围就恒定在外端值",至少比三次样条飞出去可控。样条外推尤其危险:三次样条的两端会按最后的弯曲趋势"飞出去",多项式在远处趋于无穷,物理量却往往有界。回顾第 1 章的细菌培养例子,把插值函数延伸到观测时间之外去预测未来,得到的结果几乎没有意义。做预测请回到第 2 节的拟合模型,那里至少带着物理假设,样条外推只是多项式惯性。

图:样条与线性插值对比示意

图:样条与线性插值对比示意

同一组数据点,两种连接方式。三次样条在每个区间用相邻四点构造光滑曲线,连接处导数连续,肉眼看不到折角;线性插值只是把相邻点用直线连起来,数据点处必然出现折角,视觉上"硬",但绝不会在两点之间拱出异常的尖峰。折角不只是美观问题——后续要对曲线求导时,折角处的导数不连续,数值效果差一个档次。

三、工程实践要点

3.1 插值方式速查表

场景 工具与参数 特点与代价
阶梯形变化 interp1d previous 或 next 常数外推语义,不光滑
点密、变化平缓 interp1d linear 最稳,永不振荡
点稀、要光滑曲线 interp1d cubic 光滑但可能局部过冲
有噪声的一维数据 UnivariateSpline 用 s 平衡光滑与贴近
二维散点到规则网格 griddata linear 默认推荐,凸包外为 NaN
视觉最光滑的网格插值 griddata cubic 慢,可能过冲
插值后再求导积分 UnivariateSpline 方法 导数与积分直接可用

3.2 参数选择的取舍

插值的核心取舍只有一个:光滑度换保真度。interp1d 里这个选择在 kind 参数上,UnivariateSpline 里在 s 参数上,griddata 里在 method 参数上——形式不同,本质相同。给噪声数据选参数时,一个屡试不爽的经验:先画数据散点图,再想象"人眼会怎么连",然后让参数往那个方向调。曲线抖得跟心电图一样,就加大 s;曲线平得像地板,就减小 s。数值上没有银弹,眼睛是最快的调试工具。

⚠️ 常见坑:用 interp1d 对带噪声的数据做 cubic 插值,然后把插值曲线当"平滑结果"展示。精确插值不降噪,它只是把噪声点连成更光滑的线,噪声依然在,只是换了身衣服。降噪要靠 UnivariateSpline 或第 4 章的滤波。
💡 关键直觉:记住"插值穿过点,拟合绕过点"。穿过点的工具用于精确数据,绕过点的工具用于噪声数据。这个判断帮你省掉一半的选型纠结,也能解释为什么同一批数据在两个同事手里会得出形状不同的曲线。

3.3 外推的边界管理

程序层面,interp1d 默认 bounds_error 为真,越界直接报错,这是好设计;需要容错时用 fill_value 指定边界外的值。业务层面,外推要回答"物理上允许吗":温度不会无限上升,浓度不会变成负数,凡是违反物理约束的外推结果都要截断或拒绝。如果确实需要外推预测,用第 2 节的拟合模型而不是插值函数——拟合模型带着物理假设,外推至少有个依据;样条外推只是多项式在远处的惯性滑行,没有任何原理支撑。

再往外看一眼工具全景:对规则网格上的多维数据,还有 RegularGridInterpolator 可以用,它要求数据落在网格点上,换来的是稳定与高效;对散点数据,RBFInterpolator 用径向基函数构造曲面,自带平滑参数,适合地形、场量这类连续物理量,但数据量大时它要解一个稠密线性系统,计算代价不低。这两件工具不急着今天学会,知道它们存在,遇到对应问题知道去哪找,就够了。

要点串联

  • 要点一:插值穿过每个已知点,拟合绕过噪声点,选错哲学结果从根上坏掉。
  • 要点二:interp1d 的 kind 从 previous 到 cubic,光滑度递增,过冲风险也递增。
  • 要点三:噪声数据要用 UnivariateSpline,s 小贴数据、s 大抹趋势,取两者之间的平衡。
  • 要点四:griddata 的 linear 是散点插值默认推荐,凸包外的 NaN 是诚实的信号。
  • 要点五:折角意味着导数不连续,后续要微分时优先选样条类插值。
  • 要点六:外推是插值最危险的延伸,样条外推只是多项式惯性,物理约束不可忽略。
  • 要点七:曲线抖就加大平滑,平得像地板就减小平滑,眼睛是最快的调参工具。

下一节把前三节收进一个完整实战:从带噪声的酶促反应数据出发,去噪、拟合、评估参数不确定性,走完"数据到模型"的全程。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U