KNN算法項目實戰——改進約會網站的配對效果


KNN項目實戰——改進約會網站的配對效果
1、項目背景:
海倫女士一直使用在線約會網站尋找適合自己的約會對象。盡管約會網站會推薦不同的人選,但她並不是喜歡每一個人。經過一番總結,她發現自己交往過的人可以進行如下分類:
  • 不喜歡的人
  • 魅力一般的人
  • 極具魅力的人
2、項目數據
 海倫收集約會數據已經有了一段時間,她把這些數據存放在文本文件datingTestSet.txt中,每個樣本數據占據一行,總共有1000行。
 海倫收集的樣本數據主要包含以下3種特征:
  • 每年獲得的飛行常客里程數
  • 玩視頻游戲所消耗時間百分比
  • 每周消費的冰淇淋公升數
數據格式如下:
 
3、K-近鄰算法的一般流程
 
(1)收集數據:提供文本文件。
(2)准備數據:使用Python解析文本文件。
(3)分析數據:使用Matplotlib畫二維擴散圖。
(4)測試算法:使用文本文件的部分數據作為測試樣本,計算錯誤率。
(5)使用算法:錯誤率在可接受范圍內,就可以運行k-近鄰算法進行分類。
 
 
4、項目步驟及代碼實現
  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()

 

5、項目結果
(1)數據可視化結果
 
(2)驗證分類器計算錯誤率結果
 
(3)使用分類器根據輸入數據獲得預測結果
 
 


免責聲明!

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



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