Python3《機器學習實戰》01:k-近鄰演算法(完整程式碼及註釋)
阿新 • • 發佈:2019-01-08
執行平臺: 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)
部分執行結果截圖: