做数据分析的人大概都遇到过这种尴尬:一张散点图摆在眼前,前面一段走直线,后面突然拐了弯,你用 Origin 自带的线性拟合、多项式拟合轮番上阵,结果总是一端压住了、另一端就飞出去。于是自然会想:Origin 到底能不能做分段拟合?答案是能,但它确实没有一个像 Excel 趋势线那样一键出结果的分段拟合按钮,需要你把思路理顺之后自己搭。这篇文章我就把在 Origin 里做分段拟合的完整经验写出来,包括两种主流做法、断点处理思路、连续约束的实现,以及一堆只有实际跑过才会碰到的坑。
1. 先判断:你的数据到底该不该分段
1.1 三种一眼就该分段的曲线形状
不是所有弯弯曲曲的数据都适合分段拟合,但有些数据天生就该分。
最常见的是材料拉伸曲线。应变从零到某个临界点之前,应力随应变线性上升,这是弹性段,斜率就是弹性模量;过了屈服点之后,材料进入塑性硬化阶段,曲线斜率明显变小,甚至二次硬化、颈缩下降都有。两段背后的物理机制完全不同,你不可能用一条直线描述整个过程,硬用高次多项式去追,也只会追出一个没有物理意义的曲线。
第二种典型是吸附动力学或热重分析。初始阶段快速失重、快速吸附,曲线陡峭;经过某个时间点后进入平台期,变化速率骤降。这个转折点往往对应表面活性位点耗尽或反应物浓度下降,分段拟合可以把快反应和慢反应两个子过程分开描述。
第三种是某些响应曲线,比如半导体传感器的电阻-温度关系、催化剂活性-温度曲线,经常存在“低温区一个机制、高温区换了机制”的切换。气体传感器电阻在对数坐标下往往分成两段直线,低温段受表面吸附控制,高温段受晶界势垒控制,拐点就是机制切换的标志。
这类场景的共同特征是:存在明确的拐点或机制切换点,而且每一段都有相对简单的函数形式。如果你的数据只是整体平滑弯曲,那不属于“非分不可”,可能用指数、幂律或对数变换就能全局拟合,没必要引入分段复杂度。
1.2 为什么局部拟合好过硬拉一条高次多项式
有人会问,分段拟合多麻烦,我直接用一个五次多项式把整条曲线撸一遍行不行?行,但结果往往很丑。
高次多项式在拟合“前段直线、后段直线”这类数据时,必须靠多次项在中间制造拐弯。拟合误差可能不大,但曲线会在两端剧烈摆动,外推一点点就离谱,而且系数之间互相抵消,每个系数单独看都没有意义。这在数学上叫多项式端点振荡,是 Runge 现象的老问题。
分段拟合的本质是承认“不同区间由不同规律支配”。每一段用一个简单模型,参数本身就对应物理量,比如弹性模量、硬化系数、吸附速率常数。这样做出来的结果能解释、能外推、能跟别人讨论,不只是“画了一条穿过点的线”。
所以我的建议是:先做一次全局拟合,看残差是否呈现明显的系统性偏差;如果有、又能在数据背后找到机制切换的理由,那就大胆分段。
2. 准备动作:找拐点、定分段数、决定断点处要不要连续
2.1 用一阶微分把拐点从噪声里找出来
很多人找拐点靠肉眼,这在小噪声、大样本时还行,数据稍微乱一点就麻烦——同一个点,上午看成拐点,下午看成噪声。
更可靠的办法是用一阶导数定位。对连续的物理过程,拐点本质上就是斜率发生明显变化的位置。比如弹塑性拉伸曲线,弹性段斜率恒定为弹性模量,进入塑性段后斜率下降,下降发生的位置就是屈服点附近。
在 Origin 里操作分两步。先平滑,菜单路径是 Analysis > Signal Processing > Smooth,选 Savitzky-Golay,窗口和多项式阶数根据噪声水平调节,窗口选 5 到 15 比较常见。然后做微分,Analysis > Mathematics > Differentiate,对平滑后的曲线求一阶导数。
把微分曲线画出来之后,斜率平台切换的位置会非常清楚。我处理过一组带较强噪声的拉伸数据,直接看散点图根本定不准屈服点在 0.048 还是 0.055,但平滑微分之后,一阶导数在 0.051 附近出现一个明显的台阶,这就把拐点位置锁定了。
如果一阶导数还是不够清晰,可以做二阶导数,看极值位置,不过那通常是在数据比较光滑时才用。
2.2 断点的三种处理模式:固定、扫描、参数化
拐点找到之后,断点怎么处理是决定拟合成功与否的关键。有三种模式。
第一种,固定断点。如果断点有理论支撑或行业标准,比如屈服强度规定用 0.2% 残余应变对应的应力,那就直接把断点写死成 0.002。这种情况下拟合最稳定,表达式最简单。
第二种,手动扫描。断点不确定但大概在一个范围内,比如 0.045 到 0.06 之间。那就在这个范围里按步长取若干个值,每个值固定断点做一次拟合,记录残差平方和 SSE,画一条 SSE 随断点变化的曲线,取谷底对应的断点。这个做法本质上是把断点当作超参数来优化,比直接当成拟合参数稳很多。
第三种,把断点写进自定义函数当作拟合参数,让 Origin 自动优化。这个方法理论上最省事,但实际上坑很多,后面 5.1 节会详细讲。先记住结论:断点参与拟合容易不收敛,优先选固定或扫描。
2.3 C0 连续还是允许跳跃:数学模型要贴近物理本质
分段拟合还有一个必须提前做决定的点:两段曲线在断点处是否要求连续。
如果要求两段端点重合,这在数学上是 C0 连续。弹塑性双线性模型就是典型的 C0 连续:弹性段末端应力等于塑性段起始应力,材料不会在屈服点发生应力突变。连续约束会让曲线看起来自然,也能减少一个自由参数。
如果两段是独立拟合出来的,那断点处大概率会有一个台阶,也就是应力跳变。有些场景允许这种跳变,比如昼夜交替条件下某些环境参数的变化,两个状态本来就是突变的。
还有一种情况是要求 C1 连续,也就是断点处不仅位置重合,切线斜率也相同。这个在分段拟合里要慎用,因为如果两段都是斜率为正的直线,斜率相同意味着它们其实是同一条线,分段就没有意义了。C1 连续通常用于分段多项式样条,那是另一种处理思路。
我的原则很简单:连续过程选 C0,突变过程允许跳跃,不要盲目追求高阶光滑。
3. 最稳的做法:拆成两段数据,独立拟合再合图
3.1 拆分工作表的操作和容易出错的地方
如果不想碰自定义函数,最直接的办法就是把数据拆成两段,分别用 Origin 自带的拟合工具跑,然后把结果拼到一张图里。
先说拆分。假设工作表里 A 列是应变,B 列是应力,拐点在 0.05。你可以选中应变小于等于 0.05 的所有行,复制,粘贴到 C 列和 D 列,分别命名 strain_1 和 stress_1;再把应变大于 0.05 的行复制到 E 列和 F 列,命名 strain_2 和 stress_2。这样原数据一分为二。
有一个容易出错的细节:如果数据里恰好有一个点在断点上,这个点该归哪段?我建议把它归入第一段,第二段从下一个数据点开始。一个点同时参与两段拟合会让断点附近的结果被过度加权,反而不好。
拆完之后,建议先分别画一次散点图确认分段位置正确,别急着拟合。我就犯过把两段选反的错,第一段用了后半段数据,拟合出来斜率是负的,整个人都懵了。
3.2 第一段线性拟合与第二段非线性拟合的菜单路径
拆分之后,对第一段进行线性拟合。选中 strain_1 和 stress_1 两列,菜单路径 Analysis > Fitting > Linear Fit,对话框里确认输入数据范围。
这里有一个很实用的选项:如果第一段理论上应该过原点,比如线弹性段的应力-应变关系是应力等于弹性模量乘以应变,没有初始应力,那就在 Linear Fit 对话框里勾选 Fix Intercept at 0,强制截距为 0。否则拟合出的截距可能是个不合理的负数。
第二段如果是直线,直接同样的线性拟合。如果第二段是曲线,用 Analysis > Fitting > Nonlinear Curve Fit,在函数库里面选模型,比如二次多项式、指数、幂函数,甚至自定义。NLFit 的操作和普通线性拟合不太一样,要选函数、填初值、点迭代,这个流程我放到下一章配合自定义函数一起讲。
独立拟合的好处是每段可以使用 Origin 函数库里的任意模型,线性就用 Linear Fit,非线性就用 NLFit,不需要写一行公式。
3.3 把独立拟合曲线合并进原图的三个步骤
独立拟合完成后,Origin 会自动在当前工作表旁边生成拟合结果列,类似 FitLinear1 的 X 列和 Y 列。我们要做的就是把它们加到原始数据那张图里。
操作三步:右键图形的图层图标,选择 Layer Contents,打开图层内容对话框;把 FitLinear1 对应的 Fit 列添加进去;在 Plot Details 里调整线型、颜色,让它和原始数据区分开。
多数情况下,线性拟合输出的 Fit 列只覆盖参与拟合的数据范围,所以图上不会出现超出区间的内容。如果发现某条拟合曲线“蹿”出了区间,多半是在拟合前选中了整列而不是区间数据,回去检查一下输入范围就行。
把两条拟合曲线都加上之后,如果有断点错位的问题,看 3.4 节的处理方法。
3.4 独立拟合的限制:断点处出现台阶怎么办
独立拟合最明显的短板是两段参数互不约束,断点处可能出现台阶。第一段在 0.05 处算出的应力是 102 MPa,第二段从 0.051 开始拟合,外推回 0.05 处可能变成 95 MPa。图形上就是两段直线在断点处错开。
如果物理上要求 C0 连续,可以用一个技巧:对第二段做“以断点为原点”的回归。具体来说,新建列 dx = x - 0.05,dy = y - y0,其中 y0 是第一段在断点处的预测值,然后对 dx 和 dy 做线性拟合,并勾选 Fix Intercept at 0。这样第二段直线必然从断点处接上第一段,连续约束就实现了。
这个方法的好处是完全不依赖自定义函数,用的是 Origin 自带功能,适合不想写表达式的人。缺点是参数在拟合前就要做一系列数据变换,步骤多一点,而且 y0 本身有第一段拟合误差,这个误差没有传递到第二段的统计结果里。要求严格的话,还是推荐下一章的自定义函数方案。
4. 进阶做法:自定义分段函数,一次拟合出带连续约束的结果
4.1 从 Fitting Function Builder 开始:函数类型与表达式写法
如果你希望把两段拟合一次做完,而且断点处严格连续,那就要用到自定义分段函数。
先打开函数构造器,菜单路径 Tools > Fitting Function Builder。选择创建一个新函数,类型选 User Defined,下面的表达式选 Expression。给函数起个名字,比如 PLSeg2,这个名字之后会出现在 NLFit 的函数列表里,起得短一点、不带空格比较好。
接着设置参数。因为我们要做一个连续的双线性分段函数,需要三个自由参数:第一段斜率 a1、第一段截距 b1、第二段斜率 a2。注意这里故意没有设 b2,原因后面马上讲。
函数体可以写成一个分段表达式。Origin 的表达式里支持逻辑判断相乘,也就是当条件成立时返回 1、不成立时返回 0,这是我最推荐的写法。
4.2 用条件判断写分段表达式,两种写法都给你
假设断点固定为 0.05。第一种写法是用 if 函数:
y = if(x < 0.05, a1*x + b1, a2*x + b2);这个写法很直观,前半段是第一条直线,后半段是第二条直线。但需要 b2 作为一个独立参数,四条参数一起拟合,断点处连续与否完全不受约束。
第二种写法是用逻辑值相乘:
y = (x < 0.05) * (a1*x + b1) + (x >= 0.05) * (a2*x + b2);两种表达式在 Origin 里都能跑。就我个人经验,逻辑值相乘的写法在非线性迭代里的解析稳定性更好,尤其是当你之后想在表达式里叠加复杂分支条件时,if 嵌套一旦写多就容易出现解析错误。所以下面的连续版表达式我都用逻辑值相乘来写。
4.3 连续约束用消参实现:a1 x0 + b1 = a2 x0 + b2
如果两段要求在断点处 C0 连续,也就是第一段在 0.05 处的值等于第二段在 0.05 处的值,需要满足:
a1 * 0.05 + b1 = a2 * 0.05 + b2
把这个等式整理一下,b2 就可以用其他三个参数表示:
b2 = a1 * 0.05 + b1 - a2 * 0.05
把 b2 代回第二段表达式,分段函数就变成:
y = (x < 0.05) * (a1*x + b1) + (x >= 0.05) * (a2*(x - 0.05) + a1*0.05 + b1);这个表达式的含义很漂亮:第二段是从断点处出发、斜率为 a2 的一条直线,它在断点的起点高度正好由第一段决定。这样一来,参数从四个减到三个,拟合更稳定,而且每一段参数的物理意义都很清晰。
材料双线性弹塑性模型写出来就是这个形式的。低应变段斜率为弹性模量,高应变段斜率为切线硬化模量,交点处应力严格连续,完全符合固体力学常识。
4.4 在 NLFit 里给初值、跑迭代需要注意什么
自定义函数保存好之后,打开 Analysis > Fitting > Nonlinear Curve Fit,在函数列表里找到 User Defined 下面的 PLSeg2。
NLFit 左侧有若干页面:Data Selection、Function、Parameters、Bounds 等。先确认 Function 选的是 PLSeg2,再回到 Parameters 页面填初值。
初值怎么给?从图上读。对上面的拉伸数据,低应变段斜率目测在 2000 MPa 左右,所以 a1 填 2000;低应变段截距几乎为零,b1 填 0;塑性段斜率明显变缓,目测 400 到 600 之间,a2 填 500。这些初值不需要精确,但量级必须对,否则迭代很容易飞掉。
填好初值之后,点左侧的 Fit 按钮开始迭代。正常情况下可以看出 Chi-Square 从几百降到个位数,参数逐渐收敛。迭代结束后,结果工作表会输出参数值、标准误差、约化卡方、R 平方等指标。
如果点完 Fit 之后参数直接变成 NaN 或者明显不合理,先别慌,绝大多数是初值量级问题,参考第 5 节排查。
5. 拟合失败排查:初值、断点、收敛,三个坑一次性讲透
5.1 断点当成拟合参数为什么容易翻车
这是分段拟合新手最容易踩的坑。为了省事,有人会把断点 x0 也写进自定义函数当自由参数,让 Origin 自己找最优断点,表达式看起来也很简洁:
y = (x < x0) * (a1*x + b1) + (x >= x0) * (a2*x + b2);理论上这是可以的,但实际跑下来多半会翻车。原因在于 Origin 的 NLFit 默认用的是基于梯度的 Levenberg-Marquardt 算法,这个算法要求目标函数对参数是光滑的,至少局部要能求导。而由 if 或逻辑判断产生的分段边界处,目标函数对 x0 的导数根本不连续,梯度一碰到断点就会跳变,迭代极易卡在局部极小甚至来回震荡。
我见过最典型的情况是 x0 在两次迭代之间来回跳:0.049 和 0.052 反复横跳,其他参数的稳定性也一起被带崩。
所以我的建议非常明确:想把断点交给机器找,就老老实实用 2.2 节的扫描法,固定断点反复拟合,画 SSE-断点曲线找谷底。断点扫描得到的残差曲线往往会有一个很宽的谷,取谷底就行了。这种方法虽然笨,但每一步都可控、可解释,尤其适合论文或报告场景。
5.2 初值给量的逻辑:先用图上的直线段估一次
很多时候拟合发散只怪初值太随便。记住一个原则:初值要从图上读,不要从空气里猜。
打开散点图,看第一段直线的斜率大概是多少。如果你在图上看到应变从 0 到 0.05,应力从 0 涨到 100 MPa,那斜率就是 100 除以 0.05 等于 2000,填 2000。截距就是直线延长线在零应变处的值,大约为零就填 0。第二段同理。
另外一个好习惯是先做一个粗略的线性回归作为初值参考。选中第一段数据,快速跑一次 Linear Fit,把拟合出的斜率和截距抄进 NLFit 的参数初值里,这样成功率会高很多。
如果还是发散,还有一个压箱底的办法:先固定部分参数。比如在 Parameters 页面把 a1 固定为 2000,只让 b1 和 a2 参与拟合,跑通之后再解除 a1 的固定,全部参数一起做一次最终的迭代。这个“先固定一部分、再全部释放”的思路,在处理多参数模型时屡试不爽。
5.3 我的三步排查清单,从发散到收敛的实操记录
如果你按照上面的流程还是没跑通,按照下面这个三步清单排查。
第一步,检查模型形状和散点形状是否匹配。表达式选了线性模型,散点却是明显弯曲的,再怎么调参数都不可能收敛。先在 Function 页面里用“预览”功能看一眼函数曲线形状大致是什么走向,方向不对就换模型。
第二步,检查参数的量级是否合理。在 Parameters 页面,把初值跟真实物理量级比一下。如果你拟合的是一个 10 的负 4 次方量级的系数,初值却填了 100,梯度每一步都可能让参数飞出合理范围。这时候可以先把一个参数 Fix 住,拟合其他参数,找到合理值后再全部放开。
第三步,检查表达式中断点使用的区间是否写反。我遇到过把 x < 0.05 写成 x > 0.05 的情况,结果第一段拟合了后半段、第二段拟合了前半段,参数符号全部反过来,看起来像是收敛了,实际上完全错位。验证方法很简单:用拟合出的参数手动算几个点,对比原始数据,一眼就能看出问题。
5.4 怎么判断结果是“拟合出来”而不是“凑出来”
参数收敛后,别急着欢呼。判断拟合质量,要问三个问题。
第一,参数标准误差是否远小于参数值?如果拟合出的 a2 标准误差是 500,而 a2 本身只有 480,这意味着这个参数根本不确定,模型可能被数据撑不起来。第二段如果数据太少或噪声太大,这种情况很常见。结论通常是:别分两段了,或者需要补数据。
第二,残差是不是随 x 呈现系统性变化?在 Origin 里生成残差列之后画残差对 x 的散点图,如果残差呈现 U 形或趋势性爬坡,说明拟合模型在某个区间仍然不合适,要么断点位置不对,要么函数形式不对。残差应该在零线左右随机分布。
第三,分段模型的优越性是否经得起统计检验?一个简单做法是比较简化 AIC。同一起点下,单一线性模型如果残差平方和 SSE_1 = 0.85,分段拟合 SSE_2 = 0.12,样本数 n = 20,单一模型参数个数 m_1 = 2,分段模型 m_2 = 3,那么简化 AIC 差值为 n * ln(0.85/0.12) + 2*(2 - 3) = 20 * 1.957 - 2 = 37.14,明显支持分段模型。如果差值很小甚至为负,分段拟合就是画蛇添足。
最重要的还是那句大白话:参数必须能用来讲道理。分段拟合出来的 a1 如果根本不等于已知弹性模量,你得先怀疑拟合对不对,而不是怀疑弹性模量测错了。
6. 结果评价与出图:让分段拟合经得起审稿人、导师和老板的追问
6.1 光看 R 平方不够,残差和参数误差才说明问题
R 平方在分段拟合场景下非常具有欺骗性。因为分段拟合本来就是为了贴合数据形状,每多分一段 R 平方必然上升。我记得有一次给一个两段直线数据做拟合,单一线性模型的 R 平方只有 0.87,分段之后就变成 0.993,看起来脱胎换骨,但实际上只要参数没有物理意义,这个提升就是镜花水月。
所以我在报告里一定会附上三样东西:拟合参数表、参数标准误或置信区间、残差图。
参数表要包含每一段的斜率和截距,标注断点位置。置信区间如果包含 0,就要说明这个参数可能没有统计显著性。残差图则是用来证明残差随机性的,这在论文里是很有说服力的证据。
6.2 断点竖线、拟合曲线和置信带的出图细节
出图是分段拟合里很容易被忽视但同样重要的环节。
断点位置必须画出来。最直接的方法是在工作表中新建列,X 填两个相同的断点值,比如 0.05 和 0.05,Y 填一组从最小值到最大值之间的等间隔值,然后把这一列作为折线加到图上。这样断点处就有一条竖直参考线,读者一眼就能看到机制切换的位置。
拟合曲线方面,在 NLFit 对话框里通常可以选择是否生成置信带或预测带。如果你的 Origin 版本支持,执行完拟合后回到图里,双击拟合曲线,在 Plot Details 里能找到置信带的显示开关。画出来之后,建议用浅色填充区域表示置信区间,注意不要盖住原始数据点。
图例也需要整理。在 Origin 里右键图例,选择 Properties,可以把图例改成横向排列,避免占太多垂直空间。我一般会在图例里把各条线的名称改成有意义的物理标签,比如 Elastic Region、Plastic Region,而不是默认的 B 列、C 列。这样图面干净,拿出去就能用。
6.3 分段数是机制数,不是自由数:过拟合的边界
最后必须泼一盆冷水。分段拟合最大的风险是过拟合,尤其是当你发现“三段直线拟合比两段直线拟合 R 平方更高”的时候。
分段数应该等于你相信的物理机制数,而不是数据特征数。两段直线背后是两个变形机制,三段直线背后就必须有第三个机制的理由。如果只是为了让曲线更贴合而不断加分段,那你本质上在用折线插值数据点,拟合参数的置信区间会越收越紧,但预测意义基本归零。
判断过拟合还有一个简单信号:对每个分段做外推。把第一段拟合直线外推到断点之后很远的位置,看新预测值是否荒谬。如果外推结果完全不合理,说明这个模型只能描述局部,不能作为整体解释,那就要重新审视分段方案了。
我在实际操作中还有一个习惯:把数据随机分成两部分,用前半部分拟合,用后半部分验证预测误差。如果验证误差远大于拟合误差,多半是过拟合。这个方法在分段拟合这种参数较多的模型里特别能说明问题:模型不是用来记住数据的,是用来预测没见过的数据的。
最后再分享一点个人体会。分段拟合这件事,真正考验人的不是 Origin 的操作,而是你对数据背后机制的理解。断点位置能不能讲清道理,每个参数有没有物理含义,远比 R 平方的小数点后面多一位重要。我现在的流程固定成三步:先靠平滑微分定位拐点,再固定断点做连续分段拟合,最后用残差和参数误差验证结果。你按照这个思路走一遍,会发现 Origin 里看起来麻烦的分段拟合,其实是件很顺手的事。