概念
最小二乘法多项式曲线拟合,根据给定的m个点,并不要求这条曲线精确地经过这些点,而是曲线y=f(x)的近似曲线y= φ(x)。
原理
给定数据点pi(xi,yi),其中i=1,2,…,m。求近似曲线y= φ(x)。并且使得近似曲线与y=f(x)的偏差最小。近似曲线在点pi处的偏差δi= φ(xi)-y,i=1,2,...,m。
常见的曲线拟合方法:
1.使偏差绝对值之和最小
2.使偏差绝对值最大的最小
3.使偏差平方和最小
按偏差平方和最小的原则选取拟合曲线,并且采取二项式方程为拟合曲线的方法,称为最小二乘法。喎�"/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPjwvcD4NCjxoMSBpZD0="推导过程">推导过程:
设拟合多项式为:
各点到这条曲线的距离之和,即偏差平方和如下:
为了求得符合条件的a值,对等式右边求ai偏导数:
注意每个变量仍有下标i,且[]外的x也有被∑累加(sum)
.......
将等式左边进行一下化简,然后应该可以得到下面的等式:
.......
把这些等式表示成矩阵的形式,就可以得到下面的矩阵:
将这个范德蒙得矩阵化简后可得到:
也就是说X*A=Y,那么A = (X’*X)-1*X’*Y,便得到了系数矩阵A,同时,我们也就得到了拟合曲线
数据集:征兵抽签1-366号(y),366个不同的人抽(x)。结果表明生日靠后的人易抽到小号
# coding=utf-8
'''
多项式曲线拟合算法
'''
import matplotlib.pyplot as plt
from math import *
import numpy
import random
fig = plt.figure()
ax = fig.add_subplot(111)
'阶数为9阶 n=9=k'
order=9
'被拟合的点'
xa = []
ya = []
'画图时,点的取值范围和点的密度。根据源数据点进行推断'
start = 1 # -1
end = 367 # 1
step = (start + end)/200.0
# 实验得步长不影响图形的弯曲程度
# 此概念与KDE核密度的宽度不同
# 因为系数a0,a1,a2...已经得到,此处步长只影响画图质量,可随意设置
#生成样例曲线上的各个点 100个
x = numpy.arange(start, end, step)
# y = [((a*a-1)*(a*a-1)*(a*a-1)+0.5)*numpy.sin(a*2) for a in x]
y = [((a*a-1)**3 + 0.5)*sin(2*a) for a in x]
#print x
#print y
# print z
# 生成的原曲线
#ax.plot(x,y,color='r',linestyle='-',marker='.')
#,label="(a*a-1)*(a*a-1)*(a*a-1)+0.5"
#生成的曲线上的各个点随机偏移一下,并放入到xa,ya中去
i = 0
##############################
#生成xa ya的源数据点
##############################
'''
for xx in x:
yy = y[i]
# 偏移宽度(-40%,+40%)
d = float(random.randint(60,140))/100
#ax.plot([xx*d],[yy*d],color='m',linestyle='',marker='.')
i += 1
xa.append(xx*d)
ya.append(yy*d)
'''
d = []
ya = [i for i in range(start, end)]
random.shuffle(ya)
xa = range(start, end)
#d = tuple(zip(xa, ya))
for i in range(len(xa)):
d.append([xa[i],ya[i]])
# print d
nd = numpy.array(d)
ax.plot(nd[:, 0], nd[:, 1], 'o', color="white", markersize=7, linewidth=3)
# 偏移之后的点图
#ax.plot(xa,ya,color='m',linestyle='',marker='.')
#进行曲线拟合
matA=[] #整个多项式矩阵
for i in range(0,order+1):
matA1=[] # 每一行
for j in range(0,order+1):
tx=0.0 # 每一列
for k in range(0,len(xa)):
dx = 1.0 # 表示初始
for l in range(0,j+i):
# l为次数,对应一行的不同次的变量
# 本区域(重复)运行次数越多次数越高
# 第一次不运行此区域 表示n=dx
# 最后一次运行 2order次 即为x^2k(或n^2次)
dx = dx*xa[k]
tx += dx
# 运行n次(len(xa)次)后,tx为sum(x[i]^2k) 或 n (dx=1运行n次)
matA1.append(tx)
matA.append(matA1)
#print(len(xa))
#print(matA[0][0])
# 转化为ndarray
matA = numpy.array(matA)
matB = []
for k in range(0,order+1):
ty = 0.0
# 加和n个
for i in range(0,len(xa)):
dy = 1.0
# 对于从i=1->n 求 (x[i])^(k-1)
for l in range(0, k):
dy = dy * xa[i] #dy即为公式中 (x[i])^k
ty += ya[i] * dy # 先乘完再加和
matB.append(ty)
matB = numpy.array(matB)
# 多元一次 x^k为系数 a为未知数 求线性的a
matAA = numpy.linalg.solve(matA,matB)
#得到系数矩阵
# 下面画出拟合后的曲线
# a0 + a1*x + a2*x^2 +...+ ak*x^k
#print(matAA)
xxa = numpy.arange(start, end, step)
yya = []
for i in range(0,len(xxa)):
yy = 0.0
for j in range(0,order+1):
dy = 1.0
for k in range(0,j):
dy *= xxa[i] # x^k
# ak*x^k
dy *= matAA[j] # matAA[j]即为系数a
yy += dy
yya.append(yy)
ax.plot(xxa,yya,color='g',linestyle='-',marker='')
ax.legend()
plt.show()
遇到的问题:
将一维数组变成二维
zip返回列表内的元组:[(a,b), (c,d)]
x = range(25)
y = zip(*[iter(x)]*5)
print y
[(0,1,2,3,4),
(5,6,7,8,9),
(10,11,12,13,14),
(15,16,17,18,19),
(20,21,22,23,24)
]
最小二乘法多项式曲线拟合原理
在线拟合工具
利用python搞机器学习——最小二乘法
leastsq-wiki
leastsq函数
最小二乘法以及leastsq函数