Python中如何将拟合后的折线平滑化并避免1D插值后曲率变大

代码如下

import matplotlib.pyplot as plt
import numpy as np

x = [1,5,30,200] y = [27,12,7,5] x = np.array(x) y = np.array(y)

x = np.arange(1, 17, 1)

y = np.array([4.00, 6.40, 8.00, 8.80, 9.22, 9.50, 9.70, 9.86, 10.00, 10.20, 10.32, 10.42, 10.50, 10.55, 10.58, 10.60])

z1 = np.polyfit(x, y, 3)#用 3 次多项式拟合 p1 = np.poly1d(z1) print(p1) #在屏幕上打印拟合多项式 yvals=p1(x)#也可以使用 yvals=np.polyval(z1,x) plot1=plt.plot(x, y, ‘*’,label=‘original values’) plot2=plt.plot(x, yvals, ‘r’,label=‘polyfit values’) plt.xlabel(‘x axis’) plt.ylabel(‘y axis’) plt.legend(loc=1)#指定 legend 的位置,读者可以自己 help 它的用法 plt.title(‘polyfitting’) plt.show()

使用插值拟合后,产生一个波峰一个波谷,我只想要个平滑的曲线,不知道有啥好办法

fq1 = interp1d(x, y1, kind='quadratic')
fq2 = interp1d(x, y2, kind='zero')

下面代码是一个粘度曲线测试程序,请参考程序运行结果


import numpy as np
from scipy.interpolate import interp1d

#创建待插值的数据

x = np.linspace(0, 10*np.pi, 20)

y = np.cos(x)

x = [1,5,30,200]

x = [1,100,150,200]

y = [27,12,7,5] x = np.array(x) y = np.array([27,12,7,5])

分别用 linear 和 quadratic 插值

fl = interp1d(x, y, kind=‘linear’) fq = interp1d(x, y, kind=‘quadratic’)

#设置 x 的最大值和最小值以防止插值数据越界 xint = np.linspace(x.max(), x.min(), 200) y1 = np.array([21.350,11,6.62,5.05]) y2 = np.array([15.630,8.31,5.18,3.53])

x1 = np.array([50,150,200])

xint1 = np.linspace(x1.min(), x1.max(), 110)

yintl = fl(xint) yintq = fq(xint) fq1 = interp1d(x, y1, kind=‘quadratic’) fq2 = interp1d(x, y2, kind=‘zero’) import pylab as pl

fig, ax = pl.subplots()

make ticks and tick labels

xticks = range(min(x), max(x) + 1, 3)

xticks = range(1, 200 + 50,30)

xticklabels = [1,3,10,"",30,100,200]

#设置网格样式

ax.set_xticks(xticks)

ax.set_xticklabels(xticklabels, rotation=0) ax.grid(True, linestyle=’-.’,color=“b”) yfq2=fq2(xint) yfq1=fq1(xint)

pl.plot(xint,fl(xint), color=“green”, label = “Linear”)

pl.plot(xint,fq(xint), color=“red”, label =“Quadratic”) pl.plot(xint,yfq1, “b–”, label =“QuadraticL”) pl.plot(xint,yfq2, color=“green”, label =“QuadraticH”) x3= np.array([1,5,30,200]) y3 = np.array([27,12,7,5]) pl.plot(x3,y3,“b-”, label =“QuadraticZ”) pl.legend(loc = “best”)

pl.ylim(1,50,10) pl.xlim(0,200,70) pl.show()


Python中如何将拟合后的折线平滑化并避免1D插值后曲率变大

5 回复

没人知道?写个函数计算是不是可以实现


这个问题很常见,尤其是在处理传感器数据或拟合结果时。核心思路是:不要直接对拟合后的离散点进行插值,而是用更高阶或更平滑的模型重新拟合原始数据,或者在拟合阶段就加入平滑约束。

直接对拟合折线进行插值(尤其是线性插值后接样条)肯定会引入虚假的曲率,因为插值器会严格通过每个点,包括噪声点。

推荐方案:使用样条平滑或更鲁棒的拟合

对于一维数据,scipy.interpolate.UnivariateSpline 的平滑样条是首选。它通过 s 参数控制平滑度,而不是强制通过每个点。

import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import UnivariateSpline

# 假设这是你的原始数据
x_raw = np.linspace(0, 10, 30)
y_raw = np.sin(x_raw) + np.random.normal(0, 0.1, x_raw.shape)  # 带噪声的原始数据

# 方案1:直接对原始数据使用平滑样条 (推荐)
# 关键参数 s:平滑因子。s 越大,曲线越平滑。设为数据点数时接近插值。
spline = UnivariateSpline(x_raw, y_raw, s=len(x_raw)*0.1)  # s 需要根据你的数据噪声水平调整
x_smooth = np.linspace(x_raw.min(), x_raw.max(), 300)  # 生成更密的点用于绘制平滑曲线
y_smooth_spline = spline(x_smooth)

# 方案2:如果你已经有了一条“拟合折线”,想平滑它,最好回到原始数据。
# 但如果必须处理这条折线,可以将其视为新“数据”,用很小的 s 值再做一次平滑样条。
# 假设 (x_fit, y_fit) 是你的拟合折线点
# spline_on_fit = UnivariateSpline(x_fit, y_fit, s=0.5*len(x_fit)) # s 要非常小
# y_smoothed_fit = spline_on_fit(x_smooth)

# 绘图对比
plt.figure(figsize=(10, 6))
plt.scatter(x_raw, y_raw, label='原始数据 (带噪声)', alpha=0.6)
plt.plot(x_smooth, y_smooth_spline, 'r-', linewidth=2, label='平滑样条拟合 (直接对原始数据)')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend()
plt.title('使用 UnivariateSpline 进行平滑拟合')
plt.grid(True, alpha=0.3)
plt.show()

关键点:

  1. 忘掉“先拟合再插值平滑”:这个流程是问题的根源。
  2. 回到源头:用 UnivariateSplineSavitzky-Golay 滤波器 (scipy.signal.savgol_filter),或增加多项式拟合的阶数,直接处理原始数据
  3. 调整参数UnivariateSplines 参数是平滑因子。你需要根据数据噪声情况手动调整。s=0 是插值(曲率会大),s 越大曲线越平滑。可以尝试 s = len(x)*m,其中 m 是一个介于 0.01 到 10 之间的因子,通过绘图选择视觉效果最好的。

一句话总结:用平滑样条直接拟合原始数据,避免对拟合结果进行插值。

python<br>import matplotlib.pyplot as plt<br>import numpy as np<br><br>x = np.array([1,5,30,200])<br>y = np.array([27,12,7,5])<br>plot1=plt.plot(x, y, 'x',label='original values')<br><br># calculate polynomial<br>z = np.polyfit(x, y, 2)<br>f = np.poly1d(z)<br>x_new = np.linspace(x[0], x[-1], 200)<br>y_new = f(x_new)<br>plot2=plt.plot(x_new, y_new, 'r',label='polyfit values')<br><br>plt.show()<br>
都拿到 3 元函数了, 那就是画图的问题, 多取几个点就好了.

但是拟合的曲线会有 overfitting 的问题, 不知道怎么解决.
![]( http://p24a7yp7l.bkt.clouddn.com/dd345a805f1425d17d29aea471690113.png)



感谢回复,可能我方向不对,我画的是黏度曲线,不可能出现负数,上面画出来的都有负数,
[![PE2kbF.md.png]( https://s1.ax1x.com/2018/07/03/PE2kbF.md.png)]( https://imgchr.com/i/PE2kbF)
[img]https://s1.ax1x.com/2018/07/03/PE2kbF.png[/img]

回到顶部