如何从数据点拟合正确数学函数?高次多项式拟合异常排查
解决高次多项式拟合数据后预测异常的问题
问题背景
给定以下数据点,尝试拟合数学函数,但使用最高次多项式(次数等于数据点数量-1)拟合后,在测试区间内得到剧烈波动的异常结果:
原始数据:
x = [560, 387, 280, 231, 196, 168, 148, 136, 124, 112, 104, 101, 93, 88, 84, 80, 76] y = [10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90]
用户采用的错误拟合代码:
import numpy as np coefficients = np.polyfit(x, y, len(x) - 1) polynomial = np.poly1d(coefficients)
测试代码及异常表现:
import matplotlib.pyplot as plt x1 = [] y1 = [] for i in range(276, 570, 1): x1.append(i) y1.append(polynomial(i)) plt.figure(figsize=(12,6)) plt.plot(x1, y1, 'o') plt.show()
测试结果呈现大幅振荡的异常曲线,完全偏离原始数据的单调趋势。
问题原因
使用最高次插值多项式拟合时,虽然能完全穿过所有给定数据点,但会触发龙格现象:在数据点区间的边缘或外部,多项式会产生极端振荡,导致预测结果完全失真。从原始数据来看,x随y增大单调递减,显然需要更平滑的拟合函数。
正确拟合方法
方法1:低次多项式拟合
选择2次或3次多项式,平衡拟合精度与曲线平滑度:
import numpy as np import matplotlib.pyplot as plt x = [560, 387, 280, 231, 196, 168, 148, 136, 124, 112, 104, 101, 93, 88, 84, 80, 76] y = [10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90] # 用2次多项式拟合 coeffs = np.polyfit(x, y, 2) poly = np.poly1d(coeffs) # 生成测试区间曲线 x1 = np.arange(276, 570, 1) y1 = poly(x1) plt.figure(figsize=(12,6)) plt.plot(x, y, 'ro', label='原始数据点') plt.plot(x1, y1, 'b-', label='2次拟合曲线') plt.legend() plt.show()
方法2:非线性函数拟合
观察数据趋势,x与y呈现反比例或对数相关特征,尝试非线性模型(示例为反比例函数):
from scipy.optimize import curve_fit import numpy as np import matplotlib.pyplot as plt x = np.array([560, 387, 280, 231, 196, 168, 148, 136, 124, 112, 104, 101, 93, 88, 84, 80, 76]) y = np.array([10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90]) # 定义反比例函数模型 def inverse_func(x, a, b): return a / x + b # 拟合参数 params, _ = curve_fit(inverse_func, x, y) a_fit, b_fit = params # 生成拟合曲线 x1 = np.arange(276, 570, 1) y1 = inverse_func(x1, a_fit, b_fit) plt.figure(figsize=(12,6)) plt.plot(x, y, 'ro', label='原始数据点') plt.plot(x1, y1, 'g-', label=f'反比例拟合: y={a_fit:.2f}/x + {b_fit:.2f}') plt.legend() plt.show()
方法3:B样条插值
既保留数据点细节,又避免剧烈振荡:
from scipy.interpolate import make_interp_spline import numpy as np import matplotlib.pyplot as plt x = np.array([560, 387, 280, 231, 196, 168, 148, 136, 124, 112, 104, 101, 93, 88, 84, 80, 76]) y = np.array([10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90]) # 先对x排序(样条插值要求输入有序) sorted_indices = np.argsort(x) x_sorted = x[sorted_indices] y_sorted = y[sorted_indices] # 创建三次B样条插值函数 spl = make_interp_spline(x_sorted, y_sorted, k=3) x1 = np.arange(76, 570, 1) y1 = spl(x1) plt.figure(figsize=(12,6)) plt.plot(x, y, 'ro', label='原始数据点') plt.plot(x1, y1, 'm-', label='三次B样条插值曲线') plt.legend() plt.show()
总结
- 绝对避免用最高次多项式拟合数据,龙格现象会直接导致预测结果异常。
- 根据数据趋势选择低次多项式、非线性函数或样条插值,既能保证拟合精度,又能得到符合预期的平滑曲线。
内容的提问来源于stack exchange,提问作者Aurélien
相关产品推荐
相关产品推荐

