1. 程式人生 > >Python3《機器學習實戰》01:k-近鄰演算法(完整程式碼及註釋)

Python3《機器學習實戰》01:k-近鄰演算法(完整程式碼及註釋)

執行平臺: Windows
Python版本: Python3
IDE: Anaconda3

# -*- coding: utf-8 -*-
"""
Created on Sun Apr 29 20:32:03 2018

@author: WangDan

使用k-近鄰演算法改進約會網站的配對效果
"""

import numpy as np
import operator
import matplotlib.pyplot as plt

#準備資料1:從文字檔案中解析資料
def file2matrix(filename):
    #開啟檔案
    fr = open(filename)
    #讀取檔案所有內容,獲得行數
arrayOLines = fr.readlines() numberOfLines = len(arrayOLines) #準備一個本函式最終要返回的資料解析完成的矩陣,numberOfLines行3列 returnMat = np.zeros((numberOfLines,3)) #準備一個本函式最終要返回的分類標籤列表 classLabelVector = [] #行的索引值初始化為0 index = 0 for line in arrayOLines: #s.strip([chars]),當[chars]為空時,預設刪除空白符(包括'\n','\r','\t',' ')
#否則移除字串頭尾指定的字元 line = line.strip() #使用str.split(str="", num=string.count(str))將字串進行切片 #str -- 分隔符,預設為所有的空字元,包括空格、換行(\n)、製表符(\t)等 #num -- 分割次數 listFromLine = line.split('\t') #將第line行分割出來的列表前3列存入特徵矩陣returnMat returnMat[index,:] = listFromLine[0
:3] #將txt檔案中分類標籤轉化為整數形式,1為不喜歡,2為魅力一般,3為極具魅力 if listFromLine[-1] == 'didntLike': classLabelVector.append(1) elif listFromLine[-1] == 'smallDoses': classLabelVector.append(2) elif listFromLine[-1] == 'largeDoses': classLabelVector.append(3) index += 1 return returnMat,classLabelVector #資料視覺化,繪製散點圖 def showdatas(datingDataMat, datingLabels): #建立畫布,設定大小 fig = plt.figure(figsize=(8,5)) ax = fig.add_subplot(311) #spyder預設使用自帶的字型,無法使用系統的其他字型,而自帶字型庫中預設中文字型 #加入下面兩行語句可以解決散點圖中不能輸出中文的問題 plt.rcParams["font.sans-serif"] = ["Microsoft YaHei"] plt.rcParams['axes.unicode_minus'] = False #u表示unicode string,表示使用unicode進行編碼,沒有u表示byte string,型別是str # ax.set_title(u'每年獲得的飛行常客里程數與玩視訊遊戲所消耗時間佔比') ax.scatter(datingDataMat[:,0], datingDataMat[:,1], 3.0*np.array(datingLabels), 3.0*np.array(datingLabels)) bx = fig.add_subplot(312) bx.scatter(datingDataMat[:,0], datingDataMat[:,2], 3.0*np.array(datingLabels), 3.0*np.array(datingLabels)) cx = fig.add_subplot(313) cx.scatter(datingDataMat[:,1], datingDataMat[:,2], 3.0*np.array(datingLabels), 3.0*np.array(datingLabels)) #準備資料2:歸一化特徵值 #有些資料數值大,有些數值小,導致在求歐式距離時,數值大的資料對結果影響大 #為了使所有特徵同等重要,所以要對資料進行歸一化處理 def autoNorm(dataSet): #min(0)表示取出每一列的最小值 #min(1)表示取出每一行的最小值 #minVals和maxVals均為1*3的一維陣列 minVals = dataSet.min(0) maxVals = dataSet.max(0) ranges = maxVals - minVals #shape(np.array([[2,2,2],[3,3,3]]))-->(2, 3) #zeros(5)-->array([0., 0., 0., 0., 0.]) #zeros([2,3])-->array([[0., 0., 0.], [0., 0., 0.]]),注意使用列表作為引數 normDataSet = np.zeros(np.shape(dataSet)) #當shape=0為陣列行數,shape=1為陣列列數 m = dataSet.shape[0] #矩陣資料操作:原始資料減去最小值的差除以最大值與最小值之間的差,得到歸一化結果 #tile([1,2], (3,1))-->array([[1, 2], [1, 2], [1, 2]]),形成3*2的矩陣 normDataSet = dataSet - np.tile(minVals, (m,1))#m*3的矩陣 #矩陣每個數對應相除 normDataSet = normDataSet/np.tile(ranges, (m,1)) return normDataSet, ranges, minVals #k-近鄰演算法實施 def classify0(inX, dataSet, labels, k): #shape[0]為陣列行數,shape[1]為陣列列數 dataSetSize = dataSet.shape[0] #inX點與dataSet每個特徵值對應相減 diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet sqdiffMat = diffMat ** 2 #如c=np.array([[0,2,1], [3,5,6], [0,1,1]]) #c.sum()-->19所有元素相加 #c.sum(axis=0)-->array([3, 8, 8])每一列相加 #c.sum(axis=1)-->array([ 3, 14, 2])每一行相加 sqDistances = sqdiffMat.sum(axis=1) #開方後得到inX到dataSet每個點之間的歐氏距離 distances = sqDistances ** 0.5 #返回陣列值從小到大的索引值,注意是索引值 sortedDistIndicies = distances.argsort() #定義一個字典,用來存放類別次數 classCount = {} for i in range(k): #取出歐氏距離最小的前k個值的標籤 voteIlabel = labels[sortedDistIndicies[i]] #dict.get(key,default=None),字典的get()方法,返回指定鍵voteIlabel的值, #如果值不在字典中返回預設值0,鍵為voteIlabel #從而計算出每個標籤在前k個值中出現的次數 classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1 #python3中用items()替換python2中的iteritems() #key=operator.itemgetter(1)根據字典的值進行排序 #key=operator.itemgetter(0)根據字典的鍵進行排序 #reverse=True降序排序字典 sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse=True) #返回出現次數最多的標籤,也就是inX最大可能對應的標籤 return sortedClassCount[0][0] #測試演算法 def datingClassTest(datingLabels, normMat): #通常隨機選擇提供資料的10%作為測試資料,另外90%作為訓練樣本 hoRatio = 0.10 #shape[0]為陣列行數,shape[1]為陣列列數 m = normMat.shape[0] numTestVecs = int(m*hoRatio) errorCount = 0.0 #k-近鄰演算法實施 for i in range(numTestVecs): classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3) print('the classifier came back with: %d, the real answer is: %d' % (classifierResult, datingLabels[i])) if (classifierResult != datingLabels[i]): errorCount += 1.0 print('\n錯誤率為: %f\n' % (errorCount/float(numTestVecs))) print('錯誤個數為:%d, 總共樣本數為:%d' % (errorCount, numTestVecs)) def classfyPerson(datingLabels): resultList = ['不喜歡', '有點喜歡', '非常喜歡'] #輸入各項資料 percentTats = float(input('玩視訊遊戲所耗時間百分比:')) ffMiles = float(input('每年獲得的飛行常客里程數:')) iceCream = float(input('每週消費的冰激淋公升數:')) normMat, ranges, minVals = autoNorm(datingDataMat) inArr = np.array([ffMiles, percentTats, iceCream]) normInArr = (inArr - minVals) / ranges classifierResult = classify0(normInArr, normDataSet, datingLabels, 3) print('\n你可能%sTa!' % resultList[classifierResult-1]) if __name__ == '__main__': #準備資料:從文字檔案中解析資料 datingDataMat, datingLabels = file2matrix('datingTestSet.txt') #分析資料:使用Matplotlib建立散點圖 showdatas(datingDataMat, datingLabels) #準備資料:歸一化特徵值 normDataSet, ranges, minVals = autoNorm(datingDataMat) #測試演算法: datingClassTest(datingLabels, normDataSet) #使用演算法 classfyPerson(datingLabels)

部分執行結果截圖:
這裡寫圖片描述
這裡寫圖片描述