SciPy插值得到意外曲线,与LibreOffice Calc三次趋势线结果不符
核心差异说明
你遇到的结果不一致是因为两类方法的底层逻辑完全不同:
- LibreOffice Calc的「3次多项式趋势线」是全局三次多项式最小二乘拟合:会基于所有样本点拟合一个唯一的三次多项式
y = a·x³ + b·x² + c·x + d,拟合目标是最小化所有样本点的预测值和真实值的平方误差,不需要严格穿过每一个原始样本点。 scipy.interpolate.interp1d(kind='cubic')是分段三次样条插值:会在每两个相邻样本点之间分别生成一个独立的三次多项式,要求所有分段在连接点处值、一阶导数、二阶导数全部连续,且必须严格经过每一个原始样本点,天然就会出现局部弯曲的形态。
实现和Calc一致效果的方法
直接使用numpy.polyfit做全局三次多项式拟合即可,和Calc的趋势线计算逻辑完全一致,示例代码如下:
import numpy as np import matplotlib.pyplot as plt # 原始样本数据 x = np.array([1, 4, 16, 64, 256]) y = np.array([80, 40, 20, 10, 5]) # 三次多项式拟合,deg=3表示三次,返回系数按高次到低次排列:[a, b, c, d] cubic_coeffs = np.polyfit(x, y, deg=3) # 生成可直接调用的多项式函数 cubic_predict = np.poly1d(cubic_coeffs) # 生成平滑曲线用于绘图 x_smooth = np.linspace(x.min(), x.max(), 500) y_smooth = cubic_predict(x_smooth) # 可视化验证 plt.scatter(x, y, c="blue", label="原始样本") plt.plot(x_smooth, y_smooth, c="red", label="全局三次多项式拟合(同Calc结果)") plt.xlabel("x") plt.ylabel("y") plt.legend() plt.show()
之前测试方法无效的原因
- 样条插值本身和全局多项式拟合就是两类完全不同的方法,无论怎么调整参数都不可能得到和Calc趋势线一致的结果。
- 如果你之前用
scipy.optimize.curve_fit没有得到预期结果,大概率是没有明确指定三次多项式的函数形式,只要传入正确的三次函数形式就能得到和polyfit完全一致的结果:
from scipy.optimize import curve_fit # 明确指定三次多项式的函数形式 def cubic_func(x, a, b, c, d): return a * x**3 + b * x**2 + c * x + d # 拟合得到的参数popt和np.polyfit输出的系数完全相同 popt, _ = curve_fit(cubic_func, x, y)
补充说明
你的原始样本x取值是指数级增长的,且天然符合y = 80 / √x的幂律关系,三次多项式拟合只是对该分布的近似,如果你的使用场景允许换拟合模型,幂律拟合的效果会比三次多项式好很多。
内容的提问来源于stack exchange,提问作者lambdakappatheta
相关产品推荐
相关产品推荐

