Python数据分析之k-近邻算法(kNN)
本文参考于《机器学习实战》
k-近邻算法(k-nearest neighbor,KNN)是一种基本的分类与回归算法。它的工作原理是:存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类的对应关系。输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k是不大于20的整数。最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。本文将基于《机器学习实战》上的约会数据集展开。
分类步骤
准备数据
约会对象分为三类:1.不喜欢的人 2. 魅力一般的人 3. 极具魅力的人 。测试集datingTestSet.txt,每行一个样本,每个样本3个特征和一个类别,数据集维数1000x3 ,三个特征如下:
每年获得的飞行常客里程数
玩视频游戏所消耗时间百分比
每周消费的冰淇淋公升数
将得到的txt文本文件转换成训练样本矩阵和标签向量,代码如下:
import numpy as np
import operator
def file2matrix(filename):
fr = open(filename)
arrayOLines = fr.readlines()
numberOfLines = len(arrayOLines) #得到文件行数
returnMat = np.zeros((numberOfLines,3))
classLabelVector = []
index = 0
for line in arrayOLines:
line = line.strip()
listFromLine = line.split('\t')
returnMat[index,:] = listFromLine[0:3]
if listFromLine[-1] == 'largeDoses': #极具魅力的人记为1
classLabelVector.append(1)
if listFromLine[-1] == 'smallDoses': #魅力一般的人记为2
classLabelVector.append(2)
if listFromLine[-1] == 'didntLike': #不喜欢的人记为3
classLabelVector.append(3)
index += 1
return returnMat,classLabelVector
datingDataMat,datingLabels = file2matrix('datingTestSet.txt')
print('datingDataMat:\n',datingDataMat)
print('datingLabels\n',datingLabels)分析数据
通过数据可视化直观了解特征对分类的影响。代码如下:
import matplotlib
import matplotlib.pyplot as plt
def datavisualization(datingDataMat,datingLabels):
zhfont = matplotlib.font_manager.FontProperties(fname=r'c:\windows\fonts\simsun.ttc')#设置中文字体路径
fig = plt.figure(figsize=(8,13))
ax1 = fig.add_subplot(311)#画布切分成3x1,第一个位置添加子图
ax1.scatter(datingDataMat[:,1], datingDataMat[:,2], 15.0*np.array(datingLabels), 15.0*np.array(datingLabels))
ax1.axis([-2,25,-0.2,2.0])
#利用datingLabel变量,绘制色彩不等,尺寸不同的点;指定坐标轴范围
plt.xlabel('玩视频游戏所消耗时间占百分比',fontproperties=zhfont)#横轴标签
plt.ylabel('每周消费的冰激凌公升数',fontproperties=zhfont) #纵轴标签
ax2 = fig.add_subplot(312)#在画布上添加第二个子图
ax2.scatter(datingDataMat[:,0], datingDataMat[:,1], 15.0*np.array(datingLabels), 15.0*np.array(datingLabels))
ax2.axis([-5000,100000,-2,23])
plt.xlabel('每年获得的飞行常客里程数',fontproperties=zhfont)#横轴标签
plt.ylabel('玩视频游戏所消耗时间占百分比',fontproperties=zhfont)#纵轴标签
ax3 = fig.add_subplot(313)#在画布上添加第三个子图
ax3.scatter(datingDataMat[:,0], datingDataMat[:,2], 15.0*np.array(datingLabels), 15.0*np.array(datingLabels))
ax3.axis([-5000,100000,-0.2,2.0])
plt.xlabel('每年获得的飞行常客里程数',fontproperties=zhfont)#横轴标签
plt.ylabel('每周消费的冰激凌公升数',fontproperties=zhfont)#纵轴标签
plt.show()
datavisualization(datingDataMat,datingLabels)图形如下:
测试数据
首先是数据特征归一化。定义函数如下:
def autoNorm(dataSet):
minVals = dataSet.min(0)
maxVals = dataSet.max(0)
ranges = maxVals - minVals
normDataSet = np.zeros(np.shape(dataSet))
m = dataSet.shape[0]
normDataSet = dataSet - np.tile(minVals, (m,1))
normDataSet = normDataSet/np.tile(ranges, (m,1))
return normDataSet, ranges, minVals我们将已有数据的90%作为训练集,剩下的10%作为测试集。
#kNN算法
def classify0(inX, dataSet, labels, k):
dataSetSize = dataSet.shape[0]
diffMat = np.tile(inX, (dataSetSize,1)) - dataSet
sqDiffMat = diffMat**2
sqDistances = sqDiffMat.sum(axis=1)
distances = sqDistances**0.5
sortedDistIndicies = distances.argsort()
classCount={}
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]]
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
def datingClassTest():
hoRatio = 0.10 #hold out 10%
datingDataMat,datingLabels = file2matrix('datingTestSet2.txt') #load data setfrom file
normMat, ranges, minVals = autoNorm(datingDataMat)
m = normMat.shape[0]
numTestVecs = int(m*hoRatio)
errorCount = 0.0
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 ("the total error rate is: %f" % (errorCount/float(numTestVecs)))
print (errorCount)
datingClassTest() 分类结果如下,错误率为5%,通过调整hoRatio和变量k的值,错误率会相应变化。
使用算法
通过输入三个特征的值,输出是否喜欢该约会对象。
def classifyPerson():
resultList = ['不喜欢','有点喜欢','很喜欢']
#三维特征用户输入
precentTats = float(input("玩视频游戏所耗时间百分比:"))
ffMiles = float(input("每年获得的飞行常客里程数:"))
iceCream = float(input("每周消费的冰激淋公升数:"))
filename = "datingTestSet.txt"
datingDataMat, datingLabels = file2matrix(filename)
normMat, ranges, minVals = autoNorm(datingDataMat)
inArr = np.array([precentTats, ffMiles, iceCream])
norminArr = (inArr - minVals) / ranges
classifierResult = classify0(norminArr, normMat, datingLabels, 3)
print("你可能%s这个人" % (resultList[classifierResult-1]))
classifyPerson()
输入特征值,输出判断结果。
结语
kNN是非常常用的分类算法,除了通过上述方式定义函数外,你也可以直接通过下述语句直接调用。
from sklearn.neighbors import KNeighborsClassifier