科研人员必看:Python 处理分段函数的 5 种姿势(从入门到绘图)

发布时间:2026/7/1 17:53:09
科研人员必看:Python 处理分段函数的 5 种姿势(从入门到绘图) 科研人员必看Python 处理分段函数的 5 种姿势从入门到绘图摘要分段函数piecewise function是科研、工程、经济建模里最常见的函数形式之一——从范德瓦尔斯方程、滞回曲线、激活函数 ReLU/Sigmoid 近似到分段线性拟合都离不开它。本文以一道经典分段函数f(x) 2√x (0≤x≤1); 1x (x1)为例手把手带你用 Python 实现 4 种函数表达 1 种可视化方案并附带可直接运行的完整代码。文末还有避坑指南建议收藏。一、什么是分段函数为什么科研要用它分段函数是指在定义域的不同子区间上用不同表达式定义的函数。数学上写作f ( x ) { 2 x , 0 ≤ x ≤ 1 1 x , x 1 f(x) \begin{cases} 2\sqrt{x}, 0 \le x \le 1 \\ 1 x, x 1 \end{cases}f(x){2x​,1x,​0≤x≤1x1​在科研中你会大量遇到它领域典型分段函数物理化学范德瓦尔斯方程修正分段形式p k / V ( V ≥ V 0 ) p k/V \;(V \ge V_0)pk/V(V≥V0​)γ / ( V − β ) − α / V 2 ( β V V 0 ) \gamma/(V-\beta) - \alpha/V^2 \;(\beta V V_0)γ/(V−β)−α/V2(βVV0​)机器学习ReLU、LeakyReLU、Hard Sigmoid、Piecewise Linear材料力学弹塑性应力-应变曲线、滞回环经济学累进税率、分段计费、凹/凸效用函数信号处理饱和放大器、量化器、削波器分段函数的特点是逻辑清晰但实现繁琐。下面我们用 Python 把同一道题写出 4 种风格从最朴素的if一直到numpy向量化。二、4 种 Python 实现方式完整代码请见文末附录或直接复制以下 4 个代码块独立运行。方式 1最朴素的if / elif / else⭐ 推荐初学importmathdeff_if(x):if0x1:return2*math.sqrt(x)elifx1:return1xelse:raiseValueError(fx 必须在 [0, ∞)当前 x {x})优点逻辑最清晰调试最方便分支多时也不乱。缺点单点调用不能直接吃 numpy 数组要循环。方式 2lambda一行流importmath f_lambdalambdax:2*math.sqrt(x)if0x1else(1xifx1elseNone)优点一行写完适合简单的两段函数。缺点嵌套太深会变成lambda 地狱3 段以上就别用了。越界时如 x 0静默返回None需注意与f_if抛出ValueError的行为差异。方式 3dict lambda配置化⭐ 段数 ≥ 3 推荐importmath PIECES[(lambdax:0x1,lambdax:2*math.sqrt(x)),(lambdax:x1,lambdax:1x),]deff_dict(x):forcond,exprinPIECES:ifcond(x):returnexpr(x)raiseValueError(fx 必须在 [0, ∞)当前 x {x})优点把条件 表达式做成表加一段只动PIECES维护性极好。缺点单点调用仍不能向量化。方式 4numpy.where向量化⭐ 画图/批量计算必备importnumpyasnpdeff_vec(x):xnp.asarray(x)ynp.where(x1,2*np.sqrt(x),1x)returny优点能直接吃 list、tuple、numpy 数组批量计算 画图的速度飞起。缺点分段点处依赖np.where的广播行为需要小心处理断点。4 种方式对比方式性能单点性能批量可读性可扩展适用场景if/elif慢Python 循环极慢⭐⭐⭐⭐⭐⭐⭐⭐调试、单元测试lambda慢极慢⭐⭐⭐⭐⭐短小函数、回调dict中慢⭐⭐⭐⭐⭐⭐⭐⭐⭐配置化、多段函数np.where快⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐数值计算、画图实战建议业务代码用dict lambda计算/画图用np.where两者结合最香。三、绘制分段函数图像matplotlib 实战光算不算出函数值得画出来。很多科研论文、报告里都靠这张图说话。3.1 正确做法分段点两侧分别采样 分别绘制本例中 f(x) 在 x1 处连续用单一np.linspace画也不会出错。但分段绘图是通用最佳实践——当函数不连续时分别采样能避免垂直跳变线也让不同段能独立设置图例和颜色。推荐做法在分段点两侧分别采样 分别 plot。importnumpyasnpimportmatplotlib.pyplotasplt# 中文字体回退链macOS 推荐Windows/Linux 见文末踩坑plt.rcParams[font.sans-serif][PingFang SC,Heiti SC,STHeiti,SimHei,DejaVu Sans]plt.rcParams[axes.unicode_minus]False# 1) 分别采样x_leftnp.linspace(0,1,200,endpointTrue)x_rightnp.linspace(1,5,200,endpointTrue)y_left2*np.sqrt(x_left)y_right1x_right# 2) 分别画fig,axplt.subplots(figsize(7,5),dpi110)ax.plot(x_left,y_left,colorcrimson,linewidth2,labelr$y2\sqrt{x}$)ax.plot(x_right,y_right,colorcrimson,linewidth2,labelr$y1x$)# 3) 强调分段点 (1, 2)ax.scatter([1],[2],s70,facecolorswhite,edgecolorscrimson,linewidths2,zorder5)ax.axvline(x1,colorsteelblue,linestyle--,linewidth1,alpha0.7)ax.axhline(y2,colorsteelblue,linestyle--,linewidth1,alpha0.7)ax.annotate((1, 2),xy(1,2),xytext(1.15,2.3),fontsize11,colorsteelblue)# 4) 参考点forxv,yv,txtin[(0.5,2*np.sqrt(0.5),(1/2, √2)),(3,4,(3, 4))]:ax.scatter([xv],[yv],s30,colorblack,zorder4)ax.annotate(txt,xy(xv,yv),xytext(xv0.15,yv0.2),fontsize10,colorblack)# 5) 美化ax.set_xlim(-0.2,5)ax.set_ylim(-0.3,6)ax.set_xlabel(x,fontsize12)ax.set_ylabel(y f(x),fontsize12)ax.set_title(例 9 分段函数图像,fontsize14)ax.grid(True,linestyle:,alpha0.6)ax.legend(locupper left,fontsize11)ax.spines[top].set_visible(False)ax.spines[right].set_visible(False)fig.tight_layout()fig.savefig(piecewise_function.png,dpi150,bbox_inchestight)plt.show()3.2 效果展示运行后得到下图红色实线 函数曲线空心圆 分段点(1, 2)蓝色虚线 辅助读数黑点 题目里要求的两个参考点。四、避坑指南血泪总结坑 1养成在分段点两侧分别绘图的好习惯本例中 f(x) 在 x1 处连续两段取值均为 2用单一np.linspacenp.where画出来是平滑曲线不会出错。但并非所有分段函数都在分段点连续——当左右极限不等时np.where确实会在断点画出垂直跳变线。对策无论函数是否连续养成在分段点两侧分别linspace 分别plot的习惯——既保证通用性也便于为不同段设置独立的图例、颜色或线型。坑 2matplotlib mathtext 不支持\tfrac现象ax.annotate(r$(\tfrac{1}{2},\sqrt{2})$)报错Unknown symbol: \tfrac。对策matplotlib 3.x 已支持\tfrac保险起见用\frac兼容所有版本或直接用普通 Unicode 文本(1/2, √2)。坑 3中文字体乱码现象标题里的分段函数变成方块。对策显式设置font.sans-serif回退链。系统推荐字体回退链macOSPingFang SC→Heiti SC→STHeitiWindowsSimHei→Microsoft YaHeiLinuxWenQuanYi Zen Hei→Noto Sans CJK SC终极方案装mplfonts一键解决pip install mplfonts mplfonts init。坑 4定义域越界没有兜底现象传x -1给f_if静默返回None或1x 0后续计算/画图全错。对策用raise ValueError(...)显式报错或者在np.where之外用np.where((x 0), ..., np.nan)让越界点不出现在图上。坑 5浮点比较x 1翻车现象计算出来x 0.999999999落进1x分支。对策边界用x 1或x 1 1e-9永远别写x 1。五、进阶玩法科研实战向5.1 拟合 ReLU / LeakyReLUdefrelu(x):returnnp.where(x0,x,0.0)defleaky_relu(x,alpha0.01):returnnp.where(x0,x,alpha*x)神经网络里的激活函数就是分段函数np.where完美对应其前向传播。5.2 范德瓦尔斯方程defvdw(V,k,gamma,alpha,beta,V0):returnnp.where(VV0,k/V,gamma/(V-beta)-alpha/V**2)截图下半部分那道题就是它直接把公式搬进来就能用。5.3 分段线性拟合pwlf库对于数据驱动的分段拟合推荐pwlfpiecewise linear fitimportpwlf my_pwlfpwlf.PiecewiseLinFit(x_data,y_data)break_pointsmy_pwlf.fit(3)# 3 段y_hatmy_pwlf.predict(x_data)六、附录完整可运行代码把以下内容保存为piecewise_function.py直接python piecewise_function.py即可运行 例 9 分段函数 y f(x) { 2√x, 0 ≤ x ≤ 1 { 1 x, x 1 完整示例4 种函数表达 1 种可视化 importmathimportnumpyasnp# ---------- 方式 1if/elif/else ----------deff_if(x):if0x1:return2*math.sqrt(x)elifx1:return1xelse:raiseValueError(fx 必须在 [0, ∞)当前 x {x})# ---------- 方式 2lambda ----------f_lambdalambdax:2*math.sqrt(x)if0x1else(1xifx1elseNone)# ---------- 方式 3dict lambda ----------PIECES[(lambdax:0x1,lambdax:2*math.sqrt(x)),(lambdax:x1,lambdax:1x),]deff_dict(x):forcond,exprinPIECES:ifcond(x):returnexpr(x)raiseValueError(fx 必须在 [0, ∞)当前 x {x})# ---------- 方式 4numpy 向量化 ----------deff_vec(x):xnp.asarray(x)ynp.where(x1,2*np.sqrt(x),1x)returny# ---------- 方式 5matplotlib 绘图 ----------defplot_f(save_pathNone,showTrue):importmatplotlib.pyplotasplt plt.rcParams[font.sans-serif][PingFang SC,Heiti SC,STHeiti,SimHei,DejaVu Sans]plt.rcParams[axes.unicode_minus]Falsex_leftnp.linspace(0,1,200,endpointTrue)x_rightnp.linspace(1,5,200,endpointTrue)y_left2*np.sqrt(x_left)y_right1x_right fig,axplt.subplots(figsize(7,5),dpi110)ax.plot(x_left,y_left,colorcrimson,linewidth2,labelr$y2\sqrt{x}$)ax.plot(x_right,y_right,colorcrimson,linewidth2,labelr$y1x$)ax.scatter([1],[2],s70,facecolorswhite,edgecolorscrimson,linewidths2,zorder5)ax.axvline(x1,colorsteelblue,linestyle--,linewidth1,alpha0.7)ax.axhline(y2,colorsteelblue,linestyle--,linewidth1,alpha0.7)ax.annotate((1, 2),xy(1,2),xytext(1.15,2.3),fontsize11,colorsteelblue)forxv,yv,txtin[(0.5,2*np.sqrt(0.5),(1/2, √2)),(3,4,(3, 4))]:ax.scatter([xv],[yv],s30,colorblack,zorder4)ax.annotate(txt,xy(xv,yv),xytext(xv0.15,yv0.2),fontsize10,colorblack)ax.set_xlim(-0.2,5);ax.set_ylim(-0.3,6)ax.set_xlabel(x,fontsize12);ax.set_ylabel(y f(x),fontsize12)ax.set_title(例 9 分段函数图像,fontsize14)ax.grid(True,linestyle:,alpha0.6)ax.legend(locupper left,fontsize11)ax.spines[top].set_visible(False)ax.spines[right].set_visible(False)fig.tight_layout()ifsave_path:fig.savefig(save_path,dpi150,bbox_inchestight)print(f图像已保存到:{save_path})ifshow:plt.show()returnfig,ax# ---------- 自测 ----------if__name____main__:samples[0,0.5,1,1.5,3,10]print(f{x:6}|{if:10}|{lambda:10}|{dict:10}|{vec:10})print(-*60)forxinsamples:print(f{x:6}|{f_if(x):10.4f}|{f_lambda(x):10.4f}| f{f_dict(x):10.4f}|{f_vec(x):10.4f})plot_f(save_pathpiecewise_function.png,showFalse)运行结果x | if | lambda | dict | vec ------------------------------------------------------------ 0 | 0.0000 | 0.0000 | 0.0000 | 0.0000 0.5 | 1.4142 | 1.4142 | 1.4142 | 1.4142 1 | 2.0000 | 2.0000 | 2.0000 | 2.0000 1.5 | 2.5000 | 2.5000 | 2.5000 | 2.5000 3 | 4.0000 | 4.0000 | 4.0000 | 4.0000 10 | 11.0000 | 11.0000 | 11.0000 | 11.0000 图像已保存到: piecewise_function.png七、写在最后分段函数表面上是分情况讨论这么简单但在科研里它是连接理论与代码的桥梁。掌握本文的 4 种写法 1 种绘图方案你就能在 90% 的场景下优雅地处理它。如果觉得本文有帮助点赞、收藏、关注三连支持一下评论区欢迎交流你遇到过的奇葩分段函数。作者Marst.Zhang 标签#Python #分段函数 #matplotlib #科研绘图 #数值计算