用回归法预测乐高套装价格的基本步骤:
1、收集数据:用Google Shopping的API收集到的数据
2、准备数据:从返回的JSON数据中抽取价格
3、分析算法:可视化并观察数据
4、训练算法:构建不同的模型,采用逐步线性回归和直接的线性回归模型
5、测试算法:使用交叉验证来测试不同的模型,分析哪个效果更好
6、使用算法:目标就是生成数据模型
收集数据
Google为用户提供了一套购物的API来抓取价格,API将以JSON格式返回所需的产品信息。Python提供了JSON解析模块,我们可以从返回的JSON格式里整理出所需数据。
具体代码:
import json
from numpy import *
from time import sleep
import urllib.requestdef searchForSet(retX,retY,setNum,yr,numPce,origPrc):sleep(5)myAPIstr='AIzaSyD2cR2KFyx12hXu6PFU-wrWot3NXvko8vY'searchURL='https://www.googleapis.com/shopping/search/v1/public/products?key=%s&country=US&q=lego+%d&alt=json'%(myAPIstr,setNum)pg=urllib.request.urlopen(searchURL)retDict=json.loads(pg.read())for i in range(len(retDict['items'])):try:currItem=retDict['item'][i]if currItem['product']['condition']=='new':newFlag=1else:newFlag=0listOfInv=currItem['product']['inventories']for item in listOfInv:sellingPrice=item['price']if sellingPrice>origPrc*0.5:print(yr,numPce,newFlag,origPrc,sellingPrice)retX.append([yr,numPce,newFlag,origPrc])retY.append(sellingPrice)except:print('problem with item %d' % i)def setDataCollect(retX,retY):searchForSet(retX,retY,8288,2006,800,49.99)searchForSet(retX,retY,10030,2002,3096,269.99)searchForSet(retX,retY,10179,2007,5195,499.99)searchForSet(retX,retY,10181,2007,3428,199.99)searchForSet(retX,retY,10189,2008,5922,299.99)searchForSet(retX,retY,10196,2009,3263,249.99)
上述代码中,第一个函数是searchForSet(),它调用Google购物API并保证数据抽取的正确性。这里需要导入的模块是time.sleep()、json、urllib3。接下来,拼接查询的URL字符串,添加API的key和待查询的套装信息,打开和解析操作通过json.loads()方法实现。完成后将得到一部字典,下面需要做的就是从中找出价格和其他信息。
部分返回结果的是一个产品的数组,我们将在这些产品上循环迭代,判断该产品是否是新产品并抽取它的价格。解析成功后的套装将在屏幕上显示出来并保存在list对象setX和retY中。
最后一个函数是setDataCollect(),它负责多次调用searchForSet()。函数searchForSet()的其他参数是从某网站收集来的,它们也一并输出到文件中。
执行结果:
lgX=[]
lgY=[]
setDataCollect(lgX,lgY)
训练算法:建立模型
上面收集了一些真实数据,下面依据这些数据构建模型。构建出的模型可以对售价做出预测,并帮助我们理解现有数据。
首先需要添加对应常数项的特征X0(X0=1),为此创建一个全1的矩阵,然后将原数据矩阵lgX复制到新数据矩阵lgX1的第一列到第5列:
lgX1=mat(ones((58,5)))
lgX1[:,1:5]=mat(lgX)
最后,在这个新数据集上进行回归处理:
ws=standRegres(lgX1,lgY)
下面使用缩减法的一种,即岭回归在进行一次试验:
def crossValidation(xArr,yArr,numVal=10):m=len(yArr)indexList=range(m)errorMat=zeros((numVal,30))for i in range(numVal):trainX=[]trainY=[]testX=[]testY=[]random.shuffle(indexList)for j in range(m):if j<m*0.9:trainX.append(xArr[indexList[j]])trainY.append(yArr[indexList[j]])else:testX.append(xArr[indexList[j]])testY.append(yArr[indexList[j]])wMat=ridgeTest(trainX,trainY)for k in range(30):#用训练时的参数将测试数据标准化matTestX=mat(testX)matTrainX=mat(trainX)meanTrain=mean(matTrainX,0)varTrain=var(matTrainX,0)matTestX=(matTestX-meanTrain)/varTrainyEst=matTestX*mat(wMat[k,:]+mean(trainY))errorMat[i,k]=rssError(yEst.T.A,array(testY))meanErrors=mean(errorMat,0)minMean=float(min(meanErrors))bestWeights=wMat[nonzero(meanErrors==minMean)]xMat=mat(xArr)yMat=mat(yArr).TmeanX=mean(xMat,0)varX=var(xMat,0)unReg=bestWeights/varXprint('岭回归下最好的模型是:',unReg)print(-1*sum(multiply(meanX,unReg))+mean(yMat))
上述代码中,函数crossValidation()有三个参数,前两个参数lgX和lgY存有数据集中的X值和Y值的list对象,默认lgX和lgY具有相同的长度。第三个参数numVal是算法中交叉验证的次数,如果该值没有指定,就取默认值10。函数crossValidation()首先计算数据点的个数m。创建好了训练集和测试集容器,之后创建了一个list并使用NumPy提供的random.shuffle()函数对其中的元素进行混洗,因此可以实现训练集或测试集的随机选取。将数据集的90%分割成训练集,其余10%为测试集,并将二者分别放入对应容器中。
一旦对数据点进行混洗之后,就建立了一个新的矩阵wMat来保存岭回归中的所有回归系数。接下来我们在上述测试集上用30组回归系数来循环测试回归效果。岭回归需要使用标准化后的数据,因此测试数据也需要与测试集相同的参数来执行标准化。之后用rssError()计算误差,并将结果草存在errorMat中。在所有交叉验证完成后,errorMat保存了ridgeTest()里每个对应的多个误差值。为了将得出的回归系数与standRegres()作对比,需要计算这些误差估计值的均值。有一点需要注意的是:岭回归使用了数据标准化,而standRegres()则没有,因此为了将上述比较可视化,还需要将数据还原。