简体   繁体   English

如何使用固定点进行多项式拟合

[英]How to do a polynomial fit with fixed points

I have been doing some fitting in python using numpy (which uses least squares). 我一直在使用numpy(使用最小二乘法)在python中做一些拟合。

I was wondering if there was a way of getting it to fit data while forcing it through some fixed points? 我想知道是否有办法让它适应数据同时迫使它通过一些固定点? If not is there another library in python (or another language i can link to - eg c)? 如果不是python中有另一个库(或者我可以链接到的另一种语言 - 例如c)?

NOTE I know it's possible to force through one fixed point by moving it to the origin and forcing the constant term to zero, as is noted here, but was wondering more generally for 2 or more fixed points : 注意我知道可以通过将其移动到原点强制通过一个固定点并将常数项强制为零,如此处所述,但更通常地想知道2个或更多固定点:

http://www.physicsforums.com/showthread.php?t=523360 http://www.physicsforums.com/showthread.php?t=523360

The mathematically correct way of doing a fit with fixed points is to use Lagrange multipliers . 使用固定点进行拟合的数学上正确的方法是使用拉格朗日乘数 Basically, you modify the objective function you want to minimize, which is normally the sum of squares of the residuals, adding an extra parameter for every fixed point. 基本上,您修改要最小化的目标函数,通常是残差的平方和,为每个固定点添加额外参数。 I have not succeeded in feeding a modified objective function to one of scipy's minimizers. 我没有成功地将改进的目标函数提供给scipy的最小化器之一。 But for a polynomial fit, you can figure out the details with pen and paper and convert your problem into the solution of a linear system of equations: 但是对于多项式拟合,您可以用笔和纸来计算细节,并将您的问题转换为线性方程组的解:

def polyfit_with_fixed_points(n, x, y, xf, yf) :
    mat = np.empty((n + 1 + len(xf),) * 2)
    vec = np.empty((n + 1 + len(xf),))
    x_n = x**np.arange(2 * n + 1)[:, None]
    yx_n = np.sum(x_n[:n + 1] * y, axis=1)
    x_n = np.sum(x_n, axis=1)
    idx = np.arange(n + 1) + np.arange(n + 1)[:, None]
    mat[:n + 1, :n + 1] = np.take(x_n, idx)
    xf_n = xf**np.arange(n + 1)[:, None]
    mat[:n + 1, n + 1:] = xf_n / 2
    mat[n + 1:, :n + 1] = xf_n.T
    mat[n + 1:, n + 1:] = 0
    vec[:n + 1] = yx_n
    vec[n + 1:] = yf
    params = np.linalg.solve(mat, vec)
    return params[:n + 1]

To test that it works, try the following, where n is the number of points, d the degree of the polynomial and f the number of fixed points: 要测试它是否有效,请尝试以下方法,其中n是点数, d是多项式的次数, f是固定点的数量:

n, d, f = 50, 8, 3
x = np.random.rand(n)
xf = np.random.rand(f)
poly = np.polynomial.Polynomial(np.random.rand(d + 1))
y = poly(x) + np.random.rand(n) - 0.5
yf = np.random.uniform(np.min(y), np.max(y), size=(f,))
params = polyfit_with_fixed_points(d, x , y, xf, yf)
poly = np.polynomial.Polynomial(params)
xx = np.linspace(0, 1, 1000)
plt.plot(x, y, 'bo')
plt.plot(xf, yf, 'ro')
plt.plot(xx, poly(xx), '-')
plt.show()

在此输入图像描述

And of course the fitted polynomial goes exactly through the points: 当然,拟合多项式完全符合要点:

>>> yf
array([ 1.03101335,  2.94879161,  2.87288739])
>>> poly(xf)
array([ 1.03101335,  2.94879161,  2.87288739])

If you use curve_fit() , you can use sigma argument to give every point a weight. 如果使用curve_fit() ,则可以使用sigma参数为每个点赋予权重。 The following example gives the first , middle, last point very small sigma, so the fitting result will be very close to these three points: 以下示例给出了第一个,中间的,最后一个非常小的西格玛点,因此拟合结果将非常接近这三个点:

N = 20
x = np.linspace(0, 2, N)
np.random.seed(1)
noise = np.random.randn(N)*0.2
sigma =np.ones(N)
sigma[[0, N//2, -1]] = 0.01
pr = (-2, 3, 0, 1)
y = 1+3.0*x**2-2*x**3+0.3*x**4 + noise

def f(x, *p):
    return np.poly1d(p)(x)

p1, _ = optimize.curve_fit(f, x, y, (0, 0, 0, 0, 0), sigma=sigma)
p2, _ = optimize.curve_fit(f, x, y, (0, 0, 0, 0, 0))

x2 = np.linspace(0, 2, 100)
y2 = np.poly1d(p)(x2)
plot(x, y, "o")
plot(x2, f(x2, *p1), "r", label=u"fix three points")
plot(x2, f(x2, *p2), "b", label=u"no fix")
legend(loc="best")

在此输入图像描述

One simple and straightforward way is to utilize constrained least squares where constraints are weighted with a largish number M, like: 一种简单直接的方法是利用约束最小二乘法,其中约束用大数M加权,如:

from numpy import dot
from numpy.linalg import solve
from numpy.polynomial.polynomial import Polynomial as P, polyvander as V

def clsq(A, b, C, d, M= 1e5):
    """A simple constrained least squared solution of Ax= b, s.t. Cx= d,
    based on the idea of weighting constraints with a largish number M."""
    return solve(dot(A.T, A)+ M* dot(C.T, C), dot(A.T, b)+ M* dot(C.T, d))

def cpf(x, y, x_c, y_c, n, M= 1e5):
    """Constrained polynomial fit based on clsq solution."""
    return P(clsq(V(x, n), y, V(x_c, n), y_c, M))

Obviously this is not really a all inclusive silver bullet solution, but apparently it seems to work reasonable well with an simple example ( for M in [0, 4, 24, 124, 624, 3124] ): 显然,这并不是一个包罗万象的银弹解决方案,但显然它似乎在一个简单的例子中很合理( for M in [0, 4, 24, 124, 624, 3124] ):

In []: x= linspace(-6, 6, 23)
In []: y= sin(x)+ 4e-1* rand(len(x))- 2e-1
In []: x_f, y_f= linspace(-(3./ 2)* pi, (3./ 2)* pi, 4), array([1, -1, 1, -1])
In []: n, x_s= 5, linspace(-6, 6, 123)    

In []: plot(x, y, 'bo', x_f, y_f, 'bs', x_s, sin(x_s), 'b--')
Out[]: <snip>

In []: for M in 5** (arange(6))- 1:
   ....:     plot(x_s, cpf(x, y, x_f, y_f, n, M)(x_s))
   ....: 
Out[]: <snip>

In []: ylim([-1.5, 1.5])
Out[]: <snip>
In []: show()

and producing output like: 并产生如下输出: 适合渐进的大M

Edit: Added 'exact' solution: 编辑:添加'确切'解决方案:

from numpy import dot
from numpy.linalg import solve
from numpy.polynomial.polynomial import Polynomial as P, polyvander as V
from scipy.linalg import qr 

def solve_ns(A, b): return solve(dot(A.T, A), dot(A.T, b))

def clsq(A, b, C, d):
    """An 'exact' constrained least squared solution of Ax= b, s.t. Cx= d"""
    p= C.shape[0]
    Q, R= qr(C.T)
    xr, AQ= solve(R[:p].T, d), dot(A, Q)
    xaq= solve_ns(AQ[:, p:], b- dot(AQ[:, :p], xr))
    return dot(Q[:, :p], xr)+ dot(Q[:, p:], xaq)

def cpf(x, y, x_c, y_c, n):
    """Constrained polynomial fit based on clsq solution."""
    return P(clsq(V(x, n), y, V(x_c, n), y_c))

and testing the fit: 并测试适合度:

In []: x= linspace(-6, 6, 23)
In []: y= sin(x)+ 4e-1* rand(len(x))- 2e-1
In []: x_f, y_f= linspace(-(3./ 2)* pi, (3./ 2)* pi, 4), array([1, -1, 1, -1])
In []: n, x_s= 5, linspace(-6, 6, 123)
In []: p= cpf(x, y, x_f, y_f, n)
In []: p(x_f)
Out[]: array([ 1., -1.,  1., -1.])

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM