一、构造数据
#根据rating和age评估价格def wineprice(rating,age):peak_age=rating-50price=rating/2if age>peak_age:price=price*(5-(age-peak_age))#过了峰值年,价值降低else:price=price*(5*(age+1)/peak_age)#临近峰值年,价值增高if price<0:price=0return price#构造数据集合:输入——rating,age;输出——pricedef wineset1():rows=[]for i in range(300):rating=random()*50+50age=random()*50price=wineprice(rating,age)price*=(random()*0.4+0.8)rows.append({'input':(rating,age),'result':price})return rows#构造数据集合:输入——rating,age,aisle,bottlesize;输出——pricedef wineset2():rows=[]for i in range(300):rating=random()*50+50age=random()*50aisle=float(randint(1,20))bottlesize=[375.0,750.0,1500.0,3000.0][randint(0,3)]price=wineprice(rating,age)price*=(bottlesize/750)price*=(random()*0.9+0.2)rows.append({'input':(rating,age,aisle,bottlesize),'result':price})return rows
二、距离评估
#为了使距离度量更有说服力,将数据根据不同维度的影响力进行收缩或者扩张处理def rescale(data,scale):scaleddata=[]for row in data:scaled=[scale[i]*row['input'][i] for i in range(len(scale))]scaleddata.append({'input':scaled,'result':row['result']})return scaleddata#向量的欧氏距离def enclidean(v1,v2):d=0.0for i in range(len(v1)):d+=(v1[i]-v2[i])**2return sqrt(d)#数据集data中每条记录按照和vec1的欧氏距离从小到大排序def getdistances(data,vec1):distancelist=[]for i in range(len(data)):vec2=data[i]['input']distancelist.append((enclidean(vec1,vec2),i))distancelist.sort()return distancelist
三、K近邻预测
#k近邻预测价格def knnestimate(data,vec1,k=5):dlist=getdistances(data,vec1)avg=0.0for i in range(k):idx=dlist[i][1]avg+=data[idx]['result']avg=avg/kreturn avg#三种方法为k个近邻项的影响设置权值,保证距离被预测的对象越远,影响越小#反函数,对距离很近和很远比较敏感,尤其是对很近的"噪音数据"def inverseweight(dist,num=1.0,const=0.1):return num/(dist+const)#递减的一次函数,容易造成影响全为0def subtractweight(dist,const=1.0):if dist>const:return 0else:return const-dist#膜拜高斯大神!!!!!!def gaussian(dist,sigma=10.0):return math.e**(-dist**2/(2*sigma**2))#加入距离影响权值的k近邻预测算法def weightedknn(data,vec1,k=5,weightf=gaussian):dlist=getdistances(data,vec1)avg=0.0totalweight=0.0for i in range(k):dist=dlist[i][0]idx=dlist[i][1]weight=weightf(dist)avg+=weight*data[idx]['result']totalweight+=weightavg=avg/totalweightreturn avg
四、交叉验证
#划分数据集为测试数据集合和训练数据集合def dividedata(data,test=0.05):trainset=[]testset=[]for row in data:if random()<test:testset.append(row)else:trainset.append(row)return trainset,testset#用训练集合训练预测,用测试集合计算偏差def testalgorithm(algf,trainset,testset):error=0.0for row in testset:guess=algf(trainset,row['input'])error+=(row['result']-guess)**2return error/len(testset)#交叉验证,随机验证N次求取平均值def crossvalidate(algf,data,trials=100,test=0.05):error=0.0for i in range(trials):trainset,testset=dividedata(data,test)error+=testalgorithm(algf,trainset,testset)return error/trials#k=3或者1的k近邻预测算法def knn3(d,v):return knnestimate(d,v,k=3)def knn1(d,v):return knnestimate(d,v,k=1)def wknn3(d,v):return weightedknn(d,v,k=3)def wknn1(d,v):return weightedknn(d,v,k=1)
五、测试
data=wineset2()data=rescale(data,[10,10,0,0.5])print(crossvalidate(weightedknn,data))print(crossvalidate(wknn3,data))print(crossvalidate(wknn1,data))
输出
7553.27239664540056079.2719622073967672.686618173495
可以看出k过大、过小的预测结果偏差都比较大,因为k较小容易出现过拟合,k较大容易受无关数据影响。
六、优化
回来补上
如果觉得《K近邻算法应用——价格预测》对你有帮助,请点赞、收藏,并留下你的观点哦!