《機器學習實戰》 in python3.x


機器學習實戰這本書是在python2.x的環境下寫的,而python3.x中好多函數和2.x中的名稱或使用方法都不一樣了,因此對原書中的內容需要校正,下面簡單的記錄一下學習過程中fix的部分

1、print 函數后面需要加括號(程序清單2-4開始出現print函數)

2、raw_input改為input函數,在3.x中python使用input替代了raw_input函數(程序清單2-5)

3、reload(KNN)->import importlib

                             importlib.reload(KNN)

4、程序清單3-6中

    

在python2.7中,找到key所對應的第一個元素為:firstStr = myTree.keys()[0],這在python3.4中運行會報錯:‘dict_keys‘ object does not support indexing,這是因為python3改變了dict.keys,返回的是dict_keys對象,支持iterable 但不支持indexable,我們可以將其明確的轉化成list,則此項功能在python3中應這樣實現:

firstSides = list(myTree.keys()) 
firstStr = firstSides[0]#找到輸入的第一個元素

5、程序清單3-9

pickle對數據進行持久化的時候是采用二進制的方式進行存儲和讀取的,所以寫入文件的時候將

fw=open(filename,'w')---->fw=open(filename,'wb')

將文件以二進制的形式打開,方便pickle的寫入

從文件中讀取數據的時候也要用二進制的方式讀取做以下修改

fr=open(filename)---->fr=open(filename,'rb')

 

 

6、程序清單4-5中

wordList=textParse(open('ch04/email/ham/%d.txt' % i).read())

這一句在運行的時候老是報編碼錯誤,結果是讀取的文件中有亂碼,刪除亂碼后問題解決

錯誤代碼如下:

UnicodeDecodeError: 'gbk' codec can't decode byte 0xae in position 199: illegal multibyte sequence

 

trainingSet=range(50);

del(trainingSet[randIndex])

由於range不能返回List,不支持del操作,所以運行時出現以下錯誤:

TypeError: 'range' object doesn't support item deletion

可以將代碼修改為:

trainingSet = list(range(50));  

7.代碼清單8-5

由於程序運行在python3.x 環境下,而python3.x中用urllib代替了 urllib2函數庫

所以 import urllib2--->import urllib.request

之所以用urllib.request是因為urllib2.open()方法在python3.x中已經升級成為了urllib.request.open(),所以做此改動,可保無失

另外,由於原文提及的Google購物API服務已經關閉,所以只能從作者下載的相關頁面中提取樂高積木的價格了,需要增加以下函數用於分析頁面獲取價格數據:

from BeautifulSoup import BeautifulSoup
# 從頁面讀取數據,生成retX和retY列表
def scrapePage(retX, retY, inFile, yr, numPce, origPrc):
    # 打開並讀取HTML文件
    fr = open(inFile,encoding='utf-8')#規定讀取文件時編碼的格式
    soup = BeautifulSoup(fr.read())
    i=1
    # 根據HTML頁面結構進行解析
    currentRow = soup.findAll('table', r="%d" % i)
    while(len(currentRow)!=0):
        currentRow = soup.findAll('table', r="%d" % i)
        title = currentRow[0].findAll('a')[1].text
        lwrTitle = title.lower()
        # 查找是否有全新標簽
        if (lwrTitle.find('new') > -1) or (lwrTitle.find('nisb') > -1):
            newFlag = 1.0
        else:
            newFlag = 0.0
        # 查找是否已經標志出售,我們只收集已出售的數據
        soldUnicde = currentRow[0].findAll('td')[3].findAll('span')
        if len(soldUnicde)==0:
            print "item #%d did not sell" % i
        else:
            # 解析頁面獲取當前價格
            soldPrice = currentRow[0].findAll('td')[4]
            priceStr = soldPrice.text
            priceStr = priceStr.replace('$','') #strips out $
            priceStr = priceStr.replace(',','') #strips out ,
            if len(soldPrice)>1:
                priceStr = priceStr.replace('Free shipping', '')
            sellingPrice = float(priceStr)
            # 去掉不完整的套裝價格
            if  sellingPrice > origPrc * 0.5:
                    print "%d\t%d\t%d\t%f\t%f" % (yr,numPce,newFlag,origPrc, sellingPrice)
                    retX.append([yr, numPce, newFlag, origPrc])
                    retY.append(sellingPrice)
        i += 1
        currentRow = soup.findAll('table', r="%d" % i)

同時,SetDATaCollect函數也要做相應的修改:

# 依次讀取六種樂高套裝的數據,並生成數據矩陣        
def setDataCollect(retX, retY):
    scrapePage(retX, retY, '/setHtml/lego8288.html', 2006, 800, 49.99)
    scrapePage(retX, retY, '/setHtml/lego10030.html', 2002, 3096, 269.99)
    scrapePage(retX, retY, '/setHtml/lego10179.html', 2007, 5195, 499.99)
    scrapePage(retX, retY, '/setHtml/lego10181.html', 2007, 3428, 199.99)
    scrapePage(retX, retY, '/setHtml/lego10189.html', 2008, 5922, 299.99)
    scrapePage(retX, retY, '/setHtml/lego10196.html', 2009, 3263, 249.99)

8、代碼清單9-1

在函數loadDataSet中

fltLine = list(map(float, curLine))#將每行映射成浮點數,python3 map返回值改變,所以需要添加list()函數

 

 在函數binSplitDataSet

    #mat0 = dataSet[nonzero(dataSet[:, feature] > value)[0], :][0]
    #mat1 = dataSet[nonzero(dataSet[:, feature] <= value)[0], :][0]

 

改為:

mat0 = dataSet[nonzero(dataSet[:, feature] > value)[0], :]
mat1 = dataSet[nonzero(dataSet[:, feature] <= value)[0], :]

邏輯上mat0,mat1返回的是划分后的新矩陣,而不是矩陣的第一行

9、程序清單9-2

在chooseBestSplit函數中,由於Matrxi類型不能被Hash(featIndex是隨機數)

將代碼

for splitVal in set(dataSet[:,featIndex]): 

改為:

for splitVal in set((dataSet[:,featIndex].T.A.tolist())[0]): 

 

 

 

 10、9.7.1節用Tkinter創建GUI

#windows下python3.2版本之后是自動安裝tkinter的,python3.3的引入方式為:
>>> import _tkinter
>>> import tkinter
>>> tkinter._test() #彈出測試窗口
>>>

#第一個小測試
>>> root=Tk() #創建一個空的tk窗口,注意彈出后不要關閉,然后繼續輸入下一行 >>> myLabel=Label(root,text="hello,Tkinter!") >>> myLabel=grid()#輸入以上兩行,框內顯示文字 >>> #使程序完整: >>> root.mainloop()

 11、treeExplorer.py代碼修改如下:(標紅的地方為修改的地方)

from numpy import *
from tkinter import *
import regTrees
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg


def reDraw(tolS,tolN):
    reDraw.f.clf()#清空畫布
    reDraw.a=reDraw.f.add_subplot(111)#添加新的子圖
    if chkBtnVar.get():
        #檢查復選框是否被選中
        if tolN<2:tolN=2
        myTree=regTrees.createTree(reDraw.rawdata, regTrees.modelLeaf, regTrees.modelErr,(tolS,tolN))
        yHat=regTrees.createForeCast(myTree,reDraw.testdata,regTrees.modelTreeEval)
    else:
        myTree=regTrees.createTree(reDraw.rawdata,ops=(tolS,tolN))
        yHat=regTrees.createForeCast(myTree,reDraw.testdata)
    reDraw.a.scatter(reDraw.rawdata[:,0].tolist(),reDraw.rawdata[:,1].tolist(),s=5)#繪出真實值,散點圖
    reDraw.a.plot(reDraw.testdata,yHat,linewidth=2.0)#繪出測試值,曲線圖
    reDraw.canvas.show()
    
def getInputs():
    try:tolN=int(tolNentry.get())
    except:
        tolN=10
        print("enter Integer for tolN")
        #❷(以下兩行) 清除錯誤的輸入並用默認值替換
        tolNentry.delete(0, END)
        tolNentry.insert(0,'10')
    try:tolS=float(tolSentry.get())
    except:
        tolS=1.0
        print("enter Float for tolS")
        tolSentry.delete(0,END)
        tolSentry.insert(0,'1.0')
    return tolN,tolS
    

def drawNewTree():
    tolN,tolS=getInputs()
    reDraw(tolS, tolN)

root=Tk()
#Label(root,text="plot place Holder").grid(row=0,columnspan=3)
#利用TkAgg創建畫布
reDraw.f=Figure(figsize=(5,4),dpi=100)
reDraw.canvas=FigureCanvasTkAgg(reDraw.f,master=root)
reDraw.canvas.show()
reDraw.canvas.get_tk_widget().grid(row=0,columnspan=3)#widget:部件

Label(root,text="tolN").grid(row=1,column=0)
tolNentry=Entry(root)#Entry:文本輸入框
tolNentry.grid(row=1,column=1)
tolNentry.insert(0,'10')
Label(root,text="tolS").grid(row=2,column=0)
tolSentry=Entry(root)#文本輸入框
tolSentry.grid(row=2,column=1)
tolSentry.insert(0,'1.0')
Button(root,text="ReDraw",command=drawNewTree).grid(row=1,column=2,rowspan=3)
chkBtnVar=IntVar()#按鈕整數值ֵ
chkBtn=Checkbutton(root,text="Model Tree",variable=chkBtnVar)#復選框
chkBtn.grid(row=3,column=0,columnspan=2)
reDraw.rawdata=mat(regTrees.loadDataSet('sine.txt'))
reDraw.testdata=arange(min(reDraw.rawdata[:,0]),max(reDraw.rawdata[:,0]),0.01)
reDraw(1.0,10)
root.mainloop()

 12、使用K均值聚類,並使用該算法計算波蘭的酒吧聚集地及可以到各個酒吧的最佳位置。由於國內使用YahooAPi的地圖功能,故使用百度地圖提供的API代替,整個代碼現改如下:

from numpy import *
from matplotlib.markers import MarkerStyle
def loadDataSet(filename):
    dataMat=[]
    fr=open(filename)
    for line in fr.readlines():
        curLine=line.strip().split('\t')
        fltLine=list(map(float,curLine))
        dataMat.append(fltLine)
    return dataMat

#計算歐幾里德距離
def distEclud(vecA,vecB):
    return sqrt(sum(power(vecA-vecB,2)))

#生成隨機中心質點
def randCent(dataSet,k):
    n=shape(dataSet)[1]
    centroids=mat(zeros((k,n)))
    #構建簇質心
    for j in range(n):
        minJ=min(dataSet[:,j])
        rangeJ=float(max(dataSet[:,j])-minJ)
        centroids[:,j]=minJ+rangeJ*random.rand(k,1)#random.rand生成k行1列的數組,其中元素值均分布在(0,1)范圍內,實際上是每列對應向量的計算
    return centroids

#k-均值聚類算法
def kMeans(dataSet,k,distMeas=distEclud,createCent=randCent):
    m=shape(dataSet)[0]
    clusterAssment=mat(zeros((m,2)))#簇分配結果矩陣(該表是質心變化記錄表,m代表元素個數,第一列存放的是距離該點最近的質心,第二列出存放的距該質心的距離的平方)
    centroids=createCent(dataSet,k)
    clusterChanged=True
    while clusterChanged:
        clusterChanged=False
        for i in range(m):
            minDist=inf;minIndex=-1
            for j in range(k):
                #❶(以下三行) 尋找最近的質心
                distJI=distMeas(centroids[j,:],dataSet[i,:])#尋找距離i點最近的質心
                if distJI<minDist:
                    minDist=distJI;minIndex=j
            if clusterAssment[i,0]!=minIndex:clusterChanged=True
            clusterAssment[i,:]=minIndex,minDist**2
        #❷(以下四行) 更新質心的位置
        print(centroids)
        #獲取新的K個點作為新的質心
        for cent in range(k):
            ptsInClust=dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]#返回該質點對應的所有DataSet中的點(所有距離該質心距離最短的點)
            centroids[cent,:]=mean(ptsInClust,axis=0)#按列(壓縮行)返回均值(返回每列的平均值),產生新的質心
    return centroids,clusterAssment

#二分K-均值聚類算法
def biKmeans(dataSet,k,distMeas=distEclud):
    m=shape(dataSet)[0]
    clusterAssment=mat(zeros((m,2)))
    #❶(以下兩行) 創建一個初始簇
    centroid0=mean(dataSet,axis=0).tolist()[0]#所有點的平均值:絕對中心位置
    centList=[centroid0]
    for j in range(m):
        clusterAssment[j,1]=distMeas(mat(centroid0),dataSet[j,:])**2#計算所有點距離中心點的距離平方
    while(len(centList)<k):#質點不足K個
        lowestSSE=Inf#SSE:距離質點的距離平方和
        for i in range(len(centList)):
            #❷(以下兩行) 嘗試划分每一簇
            ptsInCurrCluster=dataSet[nonzero(clusterAssment[:,0].A==i)[0],:]
            centroidMat,splitClusAss=kMeans(ptsInCurrCluster,2,distMeas)
            sseSplit=sum(splitClusAss[:,1])#新划分的簇質點距離平方和
            sseNotSplit=sum(clusterAssment[nonzero(clusterAssment[:,0].A!=i)[0],1])#老簇除了第i個質點外距離平方和
            print("sseSplit,and notSplit",sseSplit,sseNotSplit)
            if(sseSplit+sseNotSplit)<lowestSSE:#新的總和<老的總和
                bestCentToSplit=i#最佳划分質點
                bestNewCents=centroidMat#最佳新質點集
                bestClustAss=splitClusAss.copy()#最佳簇分配矩陣
                lowestSSE=sseSplit+sseNotSplit#刷新最小SSE
        bestClustAss[nonzero(bestClustAss[:,0].A==1)[0],0]=len(centList)#更新質點序號
        bestClustAss[nonzero(bestClustAss[:,0].A==0)[0],0]=bestCentToSplit#以方便並入總質點集
        print('the bestCentToSplit is:',bestCentToSplit)
        print('the len of bestClusAss is:',len(bestClustAss))
        #(以下兩行)將老的一個質點用兩個最佳的新質點替換
        centList[bestCentToSplit] = bestNewCents[0,:].tolist()[0]
        centList.append(bestNewCents[1,:].tolist()[0])
        clusterAssment[nonzero(clusterAssment[:,0].A==bestCentToSplit)[0],:]=bestClustAss#更新簇分配矩陣(將最佳划分點相關的部分全部用新的划分點和距離替代)
    return mat(centList),clusterAssment
                
import urllib
import urllib.request
import json

def geoGrab(stAddress,city):
    apiStem="http://api.map.baidu.com/geocoder/v2/?"
    params={}
    #❶ 將返回類型設為JSON
    params['address']='%s%s'%(stAddress,city)
    params['ak']='您自己申請的百度API的key'
    params['output']='json'
    url_params=urllib.parse.urlencode(params)
    baiduApi=apiStem+url_params
    print(baiduApi)
    #❷ 打印輸出的的URL
    c=urllib.request.urlopen(baiduApi)
    return json.loads(c.read().decode('utf-8'))#指定編碼,否則默認為字節不是字符串

from time import sleep
#批量獲取經緯度並將“地址+經緯度”寫入place.txt
def massPlaceFind(fileName):
    fw=open('places.txt','w')
    for line in open(fileName).readlines():
        line=line.strip()
        lineArr=line.split('\t')
        #print(lineArr)
        retDict=geoGrab(lineArr[1],lineArr[2])#1和2分別是詳細地址(精確到門牌號)和城市名稱
        if retDict['status']==0:#正常返回
            lat=float(retDict['result']['location']['lat'])#緯度
            lng=float(retDict['result']['location']['lng'])#經度
            print("%s\t%f\t%f"%(lineArr[0],lat,lng))
            fw.write('%s\t%f\t%f\n'%(line,lat,lng))#將經緯度添加到原來對應的行上
        else:
            print("error fetching")
        sleep(1)
    fw.close()
    
#使用余弦定理計算兩點之間距離
def distSLC(vecA,vecB):
    a=sin(vecA[0,1]*pi/180)*sin(vecB[0,1]*pi/180)
    b=cos(vecA[0,1]*pi/180)*cos(vecB[0,1]*pi/180)*cos(pi*(vecB[0,0]-vecA[0,0])/180)
    return arccos(a+b)*6371.0

import matplotlib
import matplotlib.pyplot as plt
def clusterClubs(numClust=5):
    datList=[]
    for line in open('places.txt').readlines():
        lineArr=line.split('\t')
        datList.append([float(lineArr[4]),float(lineArr[3])])#基於進度和緯度創建矩陣
    datMat=mat(datList)
    myCentroids,clustAssing=biKmeans(datMat,numClust,distMeas=distSLC)
    fig=plt.figure()
    rect=[0.1,0.1,0.8,0.8]#決定繪制圖的哪一部分的矩陣
    scatterMarkers=['s','o','^','8','p','d','v','h','>','<']#形狀標記
    axprops=dict(xticks=[],yticks=[])
    ax0=fig.add_axes(rect,label='ax0',**axprops)
    imgP=plt.imread('Portland.png')
    #❶ 基於圖像創建矩陣
    ax0.imshow(imgP)
    ax1=fig.add_axes(rect,label='ax1',frameon=False)#使用ax1在原圖像上繪制新圖
    for i in range(numClust):
        ptsInCurrCluster=datMat[nonzero(clustAssing[:,0]==i)[0],:]
        markerStyle=scatterMarkers[i%len(scatterMarkers)]#循環使用標記
        ax1.scatter(ptsInCurrCluster[:,0].flatten().A[0],ptsInCurrCluster[:,1].flatten().A[0],marker=markerStyle,s=90)
    ax1.scatter(myCentroids[:,0].flatten().A[0],myCentroids[:,1].flatten().A[0],marker='+',s=300)#使用+標記中心位置
    plt.show()
        
                
    

        
    
    
            
    
    

 13、apriori算法修改如下

def loadDataSet():
    return [[1,3,4],[2,3,5],[1,2,3,5],[2,5]]

def createC1(dataSet):
    C1=[]#大小為1的所有候選項集合
    for transaction in dataSet:
        for item in transaction:
            if not [item] in C1:
                C1.append([item])
    C1.sort()
    #對C1中每個項構建一個不變集合
    return list(map(frozenset,C1))#frozenset:用戶不可改的集合

#D:數據集 
#Ck:候選項集列表
#minSupport:最小支持度
#生成頻繁項集
def scanD(D,Ck,minSupport):
    ssCnt={}#ssCnt:{key:value},key為項,value為其出現的次數
    for tid in D:
        for can in Ck:
            if can.issubset(tid):#can是tid的子集
                if not can in ssCnt:ssCnt[can]=1
                else:ssCnt[can]+=1
    numItems=float(len(D))
    retList=[]
    supportData={}
    for key in ssCnt:
        #計算所有項集的支持度
        support=ssCnt[key]/numItems
        if support>=minSupport:
            retList.insert(0,key)#在列表頭部插入
        supportData[key]=support#最頻繁項集及其支持度 key:value
    return retList,supportData

變化的部分都用紅色標出,以后也會采取類似的做法

 

to be continued……


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM