KNN演算法專案實戰——改進約會網站的配對效果
阿新 • • 發佈:2018-12-30
1 #-*- coding:utf-8 -*- 2 3 import matplotlib.lines as mlines 4 import matplotlib.pyplot as plt 5 import numpy as np 6 import matplotlib as mpl 7 import operator 8 9 ''' 10 #準備資料,從文字檔案中解析資料 11 ''' 12 def file2matrix(filename): 13 #開啟檔案 14 with open(filename,'r') as fr:15 # 讀取檔案所有內容 16 arrayOLines = fr.readlines() 17 # 得到檔案行數 18 numberOfLines = len(arrayOLines) 19 # 返回的NumPy矩陣,解析完成的資料:numberOfLines行,3列 20 returnMat = np.zeros((numberOfLines, 3)) 21 # 返回的分類標籤向量 22 classLabelVector = [] 23 #行的索引值 24 index = 0 25 for line in arrayOLines: 26 # s.strip(rm),當rm空時,預設刪除空白符(包括'\n','\r','\t',' ') 27 line = line.strip() 28 # 使用s.split(str="",num=string,cout(str))將字串根據'\t'分隔符進行切片。 29 listFromLine = line.split('\t') 30 #將資料前三列提取出來,存放到returnMat的NumPy矩陣中,也就是特徵矩陣 31 returnMat[index, :] = listFromLine[0:3] 32 # 根據文字中標記的喜歡的程度進行分類,1代表不喜歡,2代表魅力一般,3代表極具魅力 33 if listFromLine[-1] == 'didntLike': 34 classLabelVector.append(1) 35 elif listFromLine[-1] == 'smallDoses': 36 classLabelVector.append(2) 37 elif listFromLine[-1] == 'largeDoses': 38 classLabelVector.append(3) 39 index += 1 40 return returnMat, classLabelVector 41 42 43 44 ''' 45 #分析資料,資料視覺化,使用Matplotlib建立散點圖 46 ''' 47 def showdatas(datingDataMat, datingLabels): 48 #設定漢字格式 49 # sans-serif就是無襯線字型,是一種通用字型族。 50 # 常見的無襯線字型有 Trebuchet MS, Tahoma, Verdana, Arial, Helvetica, 中文的幼圓、隸書等等 51 mpl.rcParams['font.sans-serif'] = ['SimHei'] # 指定預設字型 SimHei為黑體 52 mpl.rcParams['axes.unicode_minus'] = False # 用來正常顯示負號 53 #將fig畫布分隔成2行2列,不共享x軸和y軸,fig畫布的大小為(13,8) 54 #當nrow=2,nclos=2時,代表fig畫布被分為四個區域,axs[0][0]表示第一行第一個區域 55 fig, axs = plt.subplots(nrows=2, ncols=2,sharex=False, sharey=False, figsize=(13,9)) 56 57 LabelsColors = [] 58 for i in datingLabels: 59 if i == 1: 60 LabelsColors.append('black') 61 if i == 2: 62 LabelsColors.append('orange') 63 if i == 3: 64 LabelsColors.append('red') 65 66 #畫出散點圖,以datingDataMat矩陣的第一(飛行常客例程)、第二列(玩遊戲)資料畫散點資料,散點大小為15,透明度為0.5 67 axs[0][0].scatter(x=datingDataMat[:,0], y=datingDataMat[:,1], color=LabelsColors,s=15, alpha=.5) 68 #設定標題,x軸label,y軸label 69 axs0_title_text = axs[0][0].set_title('每年獲得的飛行常客里程數與玩視訊遊戲所消耗時間佔比') 70 axs0_xlabel_text = axs[0][0].set_xlabel('每年獲得的飛行常客里程數') 71 axs0_ylabel_text = axs[0][0].set_ylabel('玩視訊遊戲所消耗時間佔') 72 plt.setp(axs0_title_text, size=12, weight='bold', color='red') 73 plt.setp(axs0_xlabel_text, size=10, weight='bold', color='black') 74 plt.setp(axs0_ylabel_text, size=10, weight='bold', color='black') 75 76 #畫出散點圖,以datingDataMat矩陣的第一(飛行常客例程)、第三列(冰激凌)資料畫散點資料,散點大小為15,透明度為0.5 77 axs[0][1].scatter(x=datingDataMat[:,0], y=datingDataMat[:,2], color=LabelsColors,s=15, alpha=.5) 78 #設定標題,x軸label,y軸label 79 axs1_title_text = axs[0][1].set_title('每年獲得的飛行常客里程數與每週消費的冰激淋公升數',) 80 axs1_xlabel_text = axs[0][1].set_xlabel('每年獲得的飛行常客里程數') 81 axs1_ylabel_text = axs[0][1].set_ylabel('每週消費的冰激淋公升數') 82 plt.setp(axs1_title_text, size=12, weight='bold', color='red') 83 plt.setp(axs1_xlabel_text, size=10, weight='bold', color='black') 84 plt.setp(axs1_ylabel_text, size=10, weight='bold', color='black') 85 86 #畫出散點圖,以datingDataMat矩陣的第二(玩遊戲)、第三列(冰激凌)資料畫散點資料,散點大小為15,透明度為0.5 87 axs[1][0].scatter(x=datingDataMat[:,1], y=datingDataMat[:,2], color=LabelsColors,s=15, alpha=.5) 88 #設定標題,x軸label,y軸label 89 axs2_title_text = axs[1][0].set_title('玩視訊遊戲所消耗時間佔比與每週消費的冰激淋公升數') 90 axs2_xlabel_text = axs[1][0].set_xlabel('玩視訊遊戲所消耗時間佔比') 91 axs2_ylabel_text = axs[1][0].set_ylabel('每週消費的冰激淋公升數') 92 plt.setp(axs2_title_text, size=12, weight='bold', color='red') 93 plt.setp(axs2_xlabel_text, size=10, weight='bold', color='black') 94 plt.setp(axs2_ylabel_text, size=10, weight='bold', color='black') 95 96 #設定圖例 97 didntLike = mlines.Line2D([], [], color='black', marker='.', markersize=6, label='不喜歡') 98 smallDoses = mlines.Line2D([], [], color='orange', marker='.',markersize=6, label='魅力一般') 99 largeDoses = mlines.Line2D([], [], color='red', marker='.',markersize=6, label='極具魅力') 100 #新增圖例 101 axs[0][0].legend(handles=[didntLike,smallDoses,largeDoses]) 102 axs[0][1].legend(handles=[didntLike,smallDoses,largeDoses]) 103 axs[1][0].legend(handles=[didntLike,smallDoses,largeDoses]) 104 #顯示圖片 105 plt.show() 106 107 108 109 ''' 110 #準備資料,資料歸一化處理 111 ''' 112 def autoNorm(dataSet): 113 #獲得每列資料的最小值和最大值 114 minVals = dataSet.min(0) 115 maxVals = dataSet.max(0) 116 #最大值和最小值的範圍 117 ranges = maxVals - minVals 118 #shape(dataSet)返回dataSet的矩陣行列數 119 #normDataSet = np.zeros(np.shape(dataSet)) 120 #返回dataSet的行數 121 m = dataSet.shape[0] 122 #原始值減去最小值 123 normDataSet = dataSet - np.tile(minVals, (m, 1)) 124 #除以最大和最小值的差,得到歸一化資料 125 normDataSet = normDataSet / np.tile(ranges, (m, 1)) 126 #返回歸一化資料結果,資料範圍,最小值 127 return normDataSet, ranges, minVals 128 129 130 131 ''' 132 KNN演算法分類器 133 # inX - 用於分類的資料(測試集) 134 # dataSet - 用於訓練的資料(訓練集) 135 # labes - 訓練資料的分類標籤 136 # k - kNN演算法引數,選擇距離最小的k個點 137 # sortedClassCount[0][0] - 分類結果 138 ''' 139 def classify0(inX, dataSet, labels, k): 140 #numpy函式shape[0]返回dataSet的行數 141 dataSetSize = dataSet.shape[0] 142 #在列向量方向上重複inX共1次(橫向),行向量方向上重複inX共dataSetSize次(縱向) 143 diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet 144 #二維特徵相減後平方 145 sqDiffMat = diffMat**2 146 #sum()所有元素相加,sum(0)列相加,sum(1)行相加 147 sqDistances = sqDiffMat.sum(axis=1) 148 #開方,計算出距離 149 distances = sqDistances**0.5 150 #返回distances中元素從小到大排序後的索引值 151 sortedDistIndices = distances.argsort() 152 #定一個記錄類別次數的字典 153 classCount = {} 154 for i in range(k): 155 #取出前k個元素的類別 156 voteIlabel = labels[sortedDistIndices[i]] 157 #dict.get(key,default=None),字典的get()方法,返回指定鍵的值,如果值不在字典中返回預設值。 158 #計算類別次數 159 classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 160 #python3中用items()替換python2中的iteritems() 161 #key=operator.itemgetter(1)根據字典的值進行排序 162 #key=operator.itemgetter(0)根據字典的鍵進行排序 163 #reverse降序排序字典 164 sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True) 165 #返回次數最多的類別,即所要分類的類別 166 return sortedClassCount[0][0] 167 168 169 170 ''' 171 #測試演算法,計算分類器的準確率,驗證分類器 172 ''' 173 def datingClassTest(): 174 #開啟的檔名 175 filename = "datingTestSet.txt" 176 #將返回的特徵矩陣和分類向量分別儲存到datingDataMat和datingLabels中 177 datingDataMat, datingLabels = file2matrix(filename) 178 #取所有資料的百分之十 179 hoRatio = 0.10 180 #資料歸一化,返回歸一化後的矩陣,資料範圍,資料最小值 181 normMat, ranges, minVals = autoNorm(datingDataMat) 182 #獲得normMat的行數 183 m = normMat.shape[0] 184 #百分之十的測試資料的個數 185 numTestVecs = int(m * hoRatio) 186 #分類錯誤計數 187 errorCount = 0.0 188 189 for i in range(numTestVecs): 190 #前numTestVecs個數據作為測試集,後m-numTestVecs個數據作為訓練集 191 classifierResult = classify0(normMat[i,:], normMat[numTestVecs:m,:],datingLabels[numTestVecs:m], 4) 192 print("分類結果:%d\t真實類別:%d" % (classifierResult, datingLabels[i])) 193 if classifierResult != datingLabels[i]: 194 errorCount += 1.0 195 print("錯誤率:%f%%" %(errorCount/float(numTestVecs)*100)) 196 197 198 199 ''' 200 #使用演算法,構建完整可用系統 201 ''' 202 def classifyPerson(): 203 #輸出結果 204 resultList = ['不喜歡','有些喜歡','非常喜歡'] 205 #三維特徵使用者輸入 206 ffMiles = float(input("每年獲得的飛行常客里程數:")) 207 precentTats = float(input("玩視訊遊戲所耗時間百分比:")) 208 iceCream = float(input("每週消費的冰激淋公升數:")) 209 #開啟的檔名 210 filename = "datingTestSet.txt" 211 #開啟並處理數 據 212 datingDataMat, datingLabels = file2matrix(filename) 213 #訓練集歸一化 214 normMat, ranges, minVals = autoNorm(datingDataMat) 215 #生成NumPy陣列,測試集 216 inArr = np.array([ffMiles,precentTats, iceCream]) 217 #測試集歸一化 218 norminArr = (inArr - minVals) / ranges 219 #返回分類結果 220 classifierResult = classify0(norminArr, normMat, datingLabels, 3) 221 #列印結果 222 print("你可能%s這個人" % (resultList[classifierResult-1])) 223 224 225 226 ''' 227 #主函式,測試以上各個步驟,並輸出各個步驟的結果 228 ''' 229 if __name__ == '__main__': 230 #開啟的檔名 231 filename = "datingTestSet.txt" 232 #開啟並處理資料 233 datingDataMat, datingLabels = file2matrix(filename) 234 #資料視覺化 235 showdatas(datingDataMat, datingLabels) 236 #驗證分類器 237 datingClassTest() 238 #使用分類器 239 classifyPerson()