在有些数据分布中,使用一条曲线比直线能更好拟合数据,这就需要用到多项式拟合。如下图所示分布:
多项式的一般形式:
y = p 0 x n + p 1 x n − 1 + p 2 x n − 2 + p 3 x n − 3 + . . . + p n y=p_{0}x^n + p_{1}x^{n-1} + p_{2}x^{n-2} + p_{3}x^{n-3} +...+p_{n} y=p0xn+p1xn−1+p2xn−2+p3xn−3+...+pn
多项式拟合的目的是为了找到一组 p 0 , p 1 , . . . , p n p_0, p_1, ..., p_n p0,p1,...,pn,使得拟合方程尽可能的与实际样本数据相符合。
假设拟合得到的多项式如下:
f ( x ) = p 0 x n + p 1 x n − 1 + p 2 x n − 2 + p 3 x n − 3 + . . . + p n f(x)=p_{0}x^n + p_{1}x^{n-1} + p_{2}x^{n-2} + p_{3}x^{n-3} +...+p_{n} f(x)=p0xn+p1xn−1+p2xn−2+p3xn−3+...+pn
则拟合函数与真实结果的差方如下:
l o s s = ( y 1 − f ( x 1 ) ) 2 + ( y 2 − f ( x 2 ) ) 2 + . . . + ( y n − f ( x n ) ) 2 loss = (y_1-f(x_1))^2 + (y_2-f(x_2))^2 + ... + (y_n-f(x_n))^2 loss=(y1−f(x1))2+(y2−f(x2))2+...+(yn−f(xn))2
那么多项式拟合的过程即为求取一组 p 0 , p 1 , . . . , p n p_0, p_1, ..., p_n p0,p1,...,pn, 使得loss的值最小。在程序中,多项式可以表示为一个数组,格式如下:
f = [-6, 3, 8, 1]
表示多项式为:
y = − 6 x 3 + 3 x 2 + 8 x + 1 y=-6x^3 + 3x^2 + 8x + 1 y=−6x3+3x2+8x+1
Python中, 可以用 numpy.polyfit() 函数进行多项式拟合
X = [x1, x2, ..., xn]
Y = [y1, y2, ..., yn]
# 根据一组样本,并给出最高次幂,求出拟合系数
np.polyfit(X, Y, 最高次幂) # 得到的是一个一维数组
其他多项式运算相关函数
# 根据拟合系数与自变量求出拟合值, 由此可得拟合曲线坐标样本数据 [X, Y']
np.polyval(P, X)->Y'
# 多项式函数求导,根据拟合系数求出多项式函数导函数的系数
np.polyder(P)->Q
# 已知多项式系数Q 求多项式函数的根(与x轴交点的横坐标, 即波峰波谷)
xs = np.roots(Q)
# 两个多项式函数的差函数(对应系数相减)的系数(可以通过差函数的根求取两个曲线的交点)
Q = np.polysub(P1, P2)
打开文件, 查看苹果股票的日期与收盘价格
import pandas as pd
import numpy as np
data = pd.read_csv('aapl.csv', # 导入文件
header=None,
names=['dates','close']) # 日期 & 当日收市股价
print(data)
"""
dates close
0 2011-01-28 336.10
1 2011-01-31 339.32
2 2011-02-01 345.03
3 2011-02-02 344.32
4 2011-02-03 343.44
... ... ...
27 2011-03-09 352.47
28 2011-03-10 346.67
29 2011-03-11 351.99
"""
为了方便进行数学运算, 需要把日期改为数字格式, 可以用2011-01-01为0, 计算每个日期距离2011-01-01的天数为日期值 delta
data['delta'] = data['dates'] - pd.to_datetime('2011-01-01')
data['delta'] = data['delta'].dt.days
print(data)
"""
dates close delta
0 2011-01-28 336.10 27
1 2011-01-31 339.32 30
2 2011-02-01 345.03 31
3 2011-02-02 344.32 32
... ... ... ...
"""
画出close和delta的图表
data.plot(x='delta', y='close')
针对股票价格, 用polyfit()函数进行多项式拟合
# polyfit函数, 需要(x数据, y数据, deg=项数)三个必要参数,
p = np.polyfit(data['delta'], data['close'], 5)
print(p)
# array([-1.97919911e-05, 4.66447608e-03, -4.27249275e-01, 1.89446747e+01, -4.05027302e+02, 3.67749979e+03])
# 得到的是多项式函数的系数
根据多项式函数, 可以用polyval()函数计算拟合值
我们可以计算200个拟合值, 画出曲线图 (折线图的点变多了, 就变成曲线图了~)
# 将delta从最小值到最大值之间平均分割200个数, 得到一个一维数组
xs = np.linspace(data['delta'].min(), data['delta'].max(), 200)
# linspace函数是创建数值序列的工具.
# 需要指定间隔起始点、终止端,以及指定分隔值总数(包括起始点和终止点);最终函数返回间隔类均匀分布的数值序列
# polyval函数, 根据拟合系数与自变量求出拟合值, 由此可得拟合曲线坐标样本数据
# 根据函数系数p(用polyfit函数计算得到), 以及x的值序列xs(用linspace函数计算得到), 求出y值序列ys
ys = np.polyval(p, xs)
# 画出源数据图
plt.plot(data['delta'], data['close'])
# 画出多项式拟合图
plt.plot(xs, ys)
欠拟合:
过拟合:
调整多项式的最高次幂来调整拟合度
p = np.polyfit(data['delta'], data['close'], 20) # 将最高次幂调整为20
xs = np.linspace(data['delta'].min(), data['delta'].max(), 200)
ys = np.polyval(p, xs)
plt.plot(data['delta'], data['close'])
plt.plot(xs, ys)
根据下图可以看得出来, 多项式函数可以很好的拟合股价波动了, 但是在一些位置出现了过拟合
继续调整多项式的最高次幂来找到最佳的拟合函数
p = np.polyfit(data['delta'], data['close'], 9) # 将最高次幂调整为9
xs = np.linspace(data['delta'].min(), data['delta'].max(), 200)
ys = np.polyval(p, xs)
plt.plot(data['delta'], data['close'])
plt.plot(xs, ys)
得到某一时刻股价的趋势 (涨/跌)
polyder函数可以根据拟合系数求出多项式函数导函数的系数
# polyder()函数用于多项式求导
q = np.polyder(p)
# 当x=46时, 判断是涨还是跌
# 将46放入导数函数
k = np.polyval(q, 46)
print(k) # -1.9249425322705065
# k为负数表示, 当x=46时, 股价的趋势是下降的
如何求出波峰波谷? ==> 函数切线斜率为0的位置 ==> 即导数函数的根
xs = np.roots(q)
print(xs)
# array([64.81228477, 54.26409333, 41.25554414, 28.20802013])
# 求出的是x轴的值
线性函数或多项式函数只是对于已经发生的数据继续拟合, 无法用于预测未发生的