在给定一组值(x,f(x))
的情况下,是否有办法找到最适合数据的给定度数的多项式?
我知道polynomial interpolation,用于查找给定n
个数据点的n+1
度的多项式,但这里有大量值,我们希望找到一个低值度多项式(找到最佳线性拟合,最佳二次,最佳立方等)。它可能与least squares ...
更一般地说,我想知道答案,当我们有一个多变量函数 - 像(x,y,f(x,y))
之类的点,比如说 - 并希望找到给定度数的最佳多项式(p(x,y)
)在变量中。 (特别是多项式,而不是样条或傅里叶级数。)
理论和代码/库(最好是Python,但任何语言都没问题)都很有用。
答案 0 :(得分:56)
感谢大家的回复。这是另一种总结它们的尝试。请原谅,如果我说太多“明显”的事情:我之前对最小二乘法一无所知,所以对我来说一切都是新的。
Polynomial interpolation拟合给定n
个数据点的n+1
度的多项式,例如找到一个完全通过四个给定点的立方体。正如在问题中所说,这不是我想要的 - 我有很多要点并且想要一个小程度多项式(只有大约适合,除非我们很幸运) - 但是一些答案坚持谈论它,我应该提到它们:) Lagrange polynomial,Vandermonde matrix等。
“最小二乘法”是多项式拟合“有多好”的特定定义/标准/“度量”。 (还有其他人,但这是最简单的。)假设您正在尝试拟合多项式 p(x,y)= a + bx + cy + dx 2 + ey 2 + fxy 某些给定的数据点(x i ,y i ,Z i )(其中“Z i ”是问题中的“f(x i ,y i )”。对于最小二乘,问题是找到“最佳”系数(a,b,c,d,e,f),使得最小化(保持“最小”)的是“残差平方和”,即< / p>
S =Σ i (a + bx i + cy i + dx i 2 + ey i 2 + fx i y i - Z i ) 2
重要的想法是,如果你将S视为(a,b,c,d,e,f)的函数,则S minimized处于gradient的某一点。 is 0。这意味着例如∂S/∂f= 0,即
Σ i 2(a + ... + fx i y i - Z i )x i y i = 0
和a,b,c,d,e的类似方程式。 请注意,这些只是... f中的线性方程式。因此,我们可以使用Gaussian elimination或任何the usual methods来解决这些问题。
这仍称为“线性最小二乘法”,因为虽然我们想要的函数是二次多项式,但它仍然是参数中的线性(a,b,c,d,e,f )。注意,当我们希望p(x,y)是任意函数f j 的任何“线性组合”时,同样的事情是有效的,而不仅仅是一个多项式(=“单项式的线性组合“)。
对于单变量情况(当只有变量x - f j 是单项式x j )时,有Numpy的polyfit
:
>>> import numpy
>>> xs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> ys = [1.1, 3.9, 11.2, 21.5, 34.8, 51, 70.2, 92.3, 117.4, 145.5]
>>> p = numpy.poly1d(numpy.polyfit(xs, ys, deg=2))
>>> print p
2
1.517 x + 2.483 x + 0.4927
对于多变量情况,或一般的线性最小二乘,有SciPy。 As explained in its documentation,它采用值f j ( x i )的矩阵A. (理论上它找到了A的Moore-Penrose pseudoinverse。)上面的例子涉及(x i ,y i ,Z i ),拟合多项式意味着f j 是单项式x () y ()。以下查找最佳二次方(或任何其他度数的最佳多项式,如果更改“degree = 2”行):
from scipy import linalg
import random
n = 20
x = [100*random.random() for i in range(n)]
y = [100*random.random() for i in range(n)]
Z = [(x[i]+y[i])**2 + 0.01*random.random() for i in range(n)]
degree = 2
A = []
for i in range(n):
A.append([])
for xd in range(degree+1):
for yd in range(degree+1-xd):
A[i].append((x[i]**xd)*(y[i]**yd)) #f_j(x_i)
c,_,_,_ = linalg.lstsq(A,Z)
j = 0
for xd in range(0,degree+1):
for yd in range(0,degree+1-xd):
print " + (%.2f)x^%dy^%d" % (c[j], xd, yd),
j += 1
打印
+ (0.01)x^0y^0 + (-0.00)x^0y^1 + (1.00)x^0y^2 + (-0.00)x^1y^0 + (2.00)x^1y^1 + (1.00)x^2y^0
因此它发现多项式是x 2 + 2xy + y 2 +0.01。 [最后一个术语有时是-0.01,有时是0,由于我们添加的随机噪声,这是预期的。]
Python + Numpy / Scipy的替代品是R和计算机代数系统:Sage,Mathematica,Matlab,Maple。甚至Excel也许能够做到这一点。 Numerical Recipes讨论了自己实现它的方法(在C,Fortran中)。
x=y=range(20)
而不是随机点时,它总是产生1.33x 2 + 1.33xy + 1.33y 2 ,这令人费解......直到我意识到因为我总是x[i]=y[i]
,所以多项式是相同的:x 2 + 2xy + y 2 = 4x 2 = (4/3)(X 2 + XY + Y 2 )。因此,道德是仔细选择要点以获得“正确的”多项式是很重要的。 (如果可以选择,则应选择Chebyshev nodes进行多项式插值;不确定最小二乘方是否也是如此。)degree
更改为3或4或5,它仍然主要识别相同的二次多项式(高阶项的系数为0),但对于更大的度数,它开始拟合更高次多项式。但即使是6度,采用更大的n(更多的数据点而不是20,比如200)仍然适合二次多项式。因此,道德是避免过度拟合,为此可能有助于尽可能多地获取数据点。答案 1 :(得分:7)
是的,通常这样做的方法是使用最小二乘法。还有其他方法可以指定多项式的拟合程度,但对于最小二乘法,理论最简单。一般理论称为线性回归。
最好的选择可能是从Numerical Recipes开始。
R是免费的,可以做你想做的一切,但是它有很大的学习曲线。
如果您可以访问Mathematica,则可以使用“拟合”功能进行最小二乘拟合。我想Matlab和它的开源对手Octave有类似的功能。
答案 2 :(得分:5)
对于(x,f(x))案例:
import numpy
x = numpy.arange(10)
y = x**2
coeffs = numpy.polyfit(x, y, deg=2)
poly = numpy.poly1d(coeffs)
print poly
yp = numpy.polyval(poly, x)
print (yp-y)
答案 3 :(得分:4)
请记住,更高程度的多项式总是更适合数据。更高程度的多项式通常导致非常不可能的函数(参见Occam's Razor),但是(过度拟合)。您希望在简单性(多项式次数)和拟合(例如最小平方误差)之间找到平衡点。从数量上来说,有Akaike Information Criterion或Bayesian Information Criterion的测试。这些测试给出了一个优先选择模型的分数。
答案 4 :(得分:2)
如果要将(xi,f(xi))拟合到度 n 的多项式,那么您将设置数据的线性最小二乘问题(1,xi,xi,xi ^ 2,...,xi ^ n,f(xi))。这将返回一组系数(c0,c1,... ,cn)以便最佳拟合多项式是* y = c0 + c1 * x + c2 * x ^ 2 + ... + cn * x ^ n。*
通过在问题中包含 y 以及 x 和 y 的组合,可以概括这两个因变量。< / p>
答案 5 :(得分:2)
拉格朗日多项式(如张贴@j w)可以精确拟合您指定的点,但是如果多项式的度数大于5或6,则可能会遇到数值不稳定。
最小二乘法为您提供“最佳拟合”多项式,其误差定义为各个误差的平方和。 (取你所拥有的点之间的y轴距离和得到的函数,将它们平方,然后求它们)MATLAB polyfit
函数执行此操作,并使用多个返回参数,您可以自动获取它处理缩放/偏移问题(例如,如果你在x = 312.1和312.3之间有100个点,并且你想要一个6度多项式,你将要计算u =(x-312.2)/0.1所以你-values分布在-1和+ =之间。
注意,x轴值的分布会影响最小二乘拟合的结果强烈。如果x值的间距相等,那么最终会得到更大的误差。如果您有一个案例,您可以选择 x值并且您关心与已知函数和插值多项式的最大偏差,那么Chebyshev polynomials的使用将为您提供一些东西。接近完美的minimax多项式(很难计算)。在Numerical Recipes中对此进行了详细讨论。
编辑:从我收集的内容来看,这一切都适用于一个变量的功能。对于多变量函数,如果学位超过2,那么可能要困难得多。我确实找到reference on Google Books。
答案 6 :(得分:2)
在大学时我们有这本书,我仍然觉得非常有用:Conte,de Boor;初等数值分析; Mc Grow Hill。相关段落为6.2:数据拟合 示例代码来自FORTRAN,并且列表也不是非常易读,但同时解释深刻而清晰。你最终会理解你在做什么,而不仅仅是这样做(就像我对数字食谱的体验一样) 我通常从数字食谱开始,但对于这样的事情,我很快就要抓住Conte-de Boor。
也许最好发布一些代码...它有点被剥离,但最相关的部分就在那里。它显然依赖于numpy!
def Tn(n, x):
if n==0:
return 1.0
elif n==1:
return float(x)
else:
return (2.0 * x * Tn(n - 1, x)) - Tn(n - 2, x)
class ChebyshevFit:
def __init__(self):
self.Tn = Memoize(Tn)
def fit(self, data, degree=None):
"""fit the data by a 'minimal squares' linear combination of chebyshev polinomials.
cfr: Conte, de Boor; elementary numerical analysis; Mc Grow Hill (6.2: Data Fitting)
"""
if degree is None:
degree = 5
data = sorted(data)
self.range = start, end = (min(data)[0], max(data)[0])
self.halfwidth = (end - start) / 2.0
vec_x = [(x - start - self.halfwidth)/self.halfwidth for (x, y) in data]
vec_f = [y for (x, y) in data]
mat_phi = [numpy.array([self.Tn(i, x) for x in vec_x]) for i in range(degree+1)]
mat_A = numpy.inner(mat_phi, mat_phi)
vec_b = numpy.inner(vec_f, mat_phi)
self.coefficients = numpy.linalg.solve(mat_A, vec_b)
self.degree = degree
def evaluate(self, x):
"""use Clenshaw algorithm
http://en.wikipedia.org/wiki/Clenshaw_algorithm
"""
x = (x-self.range[0]-self.halfwidth) / self.halfwidth
b_2 = float(self.coefficients[self.degree])
b_1 = 2 * x * b_2 + float(self.coefficients[self.degree - 1])
for i in range(2, self.degree):
b_1, b_2 = 2.0 * x * b_1 + self.coefficients[self.degree - i] - b_2, b_1
else:
b_0 = x*b_1 + self.coefficients[0] - b_2
return b_0
答案 7 :(得分:0)
请记住,近似多项式与找到完全之间存在很大差异。
例如,如果我给你4分,你可以
请务必选择适合您的方法!
答案 8 :(得分:0)
如果你知道如何将最小二乘问题表示为线性代数问题,那么使用Excel的矩阵函数来快速拟合是相当容易的。 (这取决于您认为Excel作为线性代数求解器的可靠性。)
答案 9 :(得分:-1)
lagrange polynomial在某种意义上是适合给定数据点集的“最简单”插值多项式。
有时会出现问题,因为数据点之间的差异很大。