接着之前写的第一章写,这次用python来实现加权最小二乘。
我们先随机取一些数据,这里用python的随机数函数实现。
import numpy as np
import pandas as pd
import statsmodels.api as sm
import matplotlib.pyplot as plt
import seaborn as sns
np.random.seed(1) #随机数的标记,这里编号为1,规定这个之后,随机数就只生成一次,之后就会固定,除非seed的参数改变。
x=np.random.uniform(-5,5,35) #生成从-5到5的随机数记为x,共35个
e=2*np.random.randn(35) #randn返回满足标准正态分布的随机值,我们将这个随机值的二倍看做随机产生的误差,存在e中
y=2*x
plt.plot(x,y,'ro') #画出没有误差的散点图
y=2*x+e
plt.plot(x,y,'bo') #画出有误差的散点图
输出如图所示:

其中红点是准确值(横轴坐标值的二倍),而蓝点是带有误差的值(这些误差是我们刚才生成的)
再去掉红点,直接变为y=2*x的红线,用一般最小二乘拟合一下。
import numpy as np
import pandas as pd
import statsmodels.api as sm
import matplotlib.pyplot as plt
import seaborn as sns
np.random.seed(1)
x=np.random.uniform(-5,5,35)
e=2*np.random.randn(35)
y=2*x
#plt.plot(x,y,'ro')
plt.plot(x,2*x,'r--')
y=2*x+e
plt.plot(x,y,'bo')
sns.regplot(x,y)
输出如下:
蓝色线条是对y=2x加上正态分布的误差之后,再用一般最小二乘法拟合得到的。红色为y=2x,可以看到,二者是接近的。
在学习的过程中,可以在python中自己编写代码用随机数据进行试验,以此加深对知识的理解。
普通最小二乘法认为各个数据是平等的,但事实上常常并非如此,比如不同仪器的测量精度不同,或者我们会倾向于认为离当前时间近的测量值会更加准确。对于这样的数据的不平等性,我们就采用“加权最小二乘法”来提高拟合精度。即给更重要的数据更高的权重。
接着(一)中的数据,我们对其进行一些改动,将第25到35个样本的误差扩大,代码如下:
import numpy as np
import pandas as pd
import statsmodels.api as sm
import matplotlib.pyplot as plt
import seaborn as sns
np.random.seed(1)
x=np.random.uniform(-5,5,35)
e=2*np.random.randn(35)
y=2*x
#plt.plot(x,y,'ro')
plt.plot(x,2*x,'r--')
y=2*x+e
for i in range(25,35): #这个for语句将后面的25到35的样本误差扩大了三倍
y[i]+=3*e[i]
plt.plot(x,y,'bo')
结果如下图所示:
我们发现,一些点相较于上一张图更加偏离了红线。值得注意的是,更加偏离虽然是样本的最后十个点,但它们并不集中在右侧,这是因为x是随机取值,不一定按照从小到大顺序排列。
我们这时对代码稍作修改,在随机生成x后执行语句x.sort(),其它语句不变,即得到下图:
可以直观地看到,图的右边的误差显然更大,此时再调用一般最小二乘拟合语句,会有如下效果:
我们发现拟合曲线与准确值(红线)偏差较大
接下来我们用加权最小二乘法对数据进行拟合,方法如下(方法引自其它博客):
最小二乘法,求解最佳回归系数。
其中 W 为权重矩阵(对角阵):
求解:(详细过程类似于线性回归求解过程)
这三幅公式图为CSDN博主「_zZhe」的原创,在此处这三幅图是转载,原图链接
https://blog.csdn.net/z_feng12489/article/details/80213739
接下来就是根据上面的算法的求解代码:
import numpy as np
import pandas as pd
import statsmodels.api as sm
import matplotlib.pyplot as plt
import seaborn as sns
np.random.seed(1)
x=np.random.uniform(-5,5,35)
e=2*np.random.randn(35)
x.sort()
y=2*x
plt.plot(x,2*x,'r--')
y=2*x+e
for i in range(25,35):
y[i]+=3*e[i]
plt.plot(x,y,'bo')
ans=[[0],[0]] #用于存放结果
lenx=len(x)
#首先我们需要将x由数组转化为矩阵,并给x加上一列1,新的矩阵记为x2
x2=np.array(x).reshape(lenx,1) #先将x转化为单行的矩阵,与之前的区别在于之前x的元素为变量,现在的x2的元素为一维数组,例:
#原来:x=[x1,x2,x3]
#现在:x2=[[x1],[x2],[x3]]
y2=np.array(y).reshape(lenx,1)#对y进行同样操作
#接下来要给x前面增加一列1
#print('???')
add=np.zeros((lenx,1)) #先生成具有lenx个子数组且子数组元素为一个0的二维数组,例:
#[[0],[0],[0]]
for i in range(lenx): #因为上面要求的是一列1,故而把0改成1
add[i][0]=1
#print(add)
x3=np.hstack((add,x2)) #用np.hstack将add和x2进行拼接,例:
#[[1,x1],[1,x2],[1,x3]]
matx=np.mat(x3) #再将x3转化为矩阵以方便进行矩阵运算,当然x3这样的array数组也可以进行矩阵运算(借助dot函数),但是比较麻烦,mat形式的直接用乘号即可)
maty=np.mat(y2)
ans=[[0],[0]]
ans=np.mat(ans) #用来存放答案的矩阵
t=2 #加权参数,对应上面第二张图公式里的分母2t^2的t
w=np.mat(np.eye((lenx))) #生成一个对角阵(这里是 单位矩阵)作为w(也就是权重矩阵)
#for i in range(lenx): #对权重矩阵对角线上的数据进行处理
avey=y.mean()
minn=20
kk=0
for i in range(lenx):
if abs(y[i]-avey)<minn:
minn=abs(y[i]-avey)
kk=i
test_point=kk #这里要选取的是y值距离y平均值最近的样本对应的x值.
#print('???')
#print(kk)
for i in range(lenx):
buf=x3[test_point]-matx[i,:] #这里的中间变量buf是一个lenx行两列的矩阵,每行第一个元素存放测试点的x值,第二个元素存放着测试点x值与x各元素的差
w[i,i]=np.exp(buf*buf.T/-2*t**2) #对权重矩阵的运算,即上面第二幅图的公式,这里有一个技巧,就是用矩阵的乘法来进行了平方运算
#print(buf)
#print('???')
#print(buf*buf.T) #测试数据
ans=(matx.T*(w*matx)).I*(matx.T*(w*maty))#即对上面第三幅图的公式的计算
#print('<><><><><>')
#print(ans)
#print(matx.T) #测试点
ans=ans.getA().tolist() #算出ans之后,将其转化为列表
ansx=[x[0],x[lenx-1]]
ansy=[]
ansy.append(ans[0][0]+ans[1][0]*ansx[0])
ansy.append(ans[0][0]+ans[1][0]*ansx[1])
plt.plot(ansx,ansy,'g-')
Bowen