kNN英文全称k Nearest Neighbor,即k近邻算法。
首先需要了解欧氏距离,很简单,就是平面上的两点之间的距离。
例:点A(x1, y1)与点B(x2, y2)之间的距离为
样本编号 | X | Y | label |
1 | 1.0 | 1.1 | A |
2 | 1.0 | 1.0 | A |
3 | 0 | 0 | B |
4 | 0 | 0.1 | B |
编号 | X | Y | label |
1 | 0.1 | 0.1 | 待分类 |
从直观层面上就可以看出测试数据距离B类比较近,通过欧氏距离计算也是如此。下面给出kNN算法以及上面小例子的代码。
import numpy as np
import operator
import matplotlib.pyplot as plt
def kNN(inX, dataSet, labels, k):
'''
kNN算法
:param inX: 需要分类的数据
:param dataSet: 样本数据集
:param labels: 样本数据的标签
:param k: 需要取出的前k个
:return: 最有可能的分类
'''
# 获取样本数据的个数
dataSetSize = dataSet.shape[0]
# np.tile函数将待分类的数据inX“广播”成和样本数据dataSet一样的形状
# 获取待分类数据inX与样本数据集中的每个数据之间的差
diffMat = np.tile(inX, (dataSetSize,1)) - dataSet
# 平方
sqDiffMat = diffMat**2
# 按行累加
sqDistances = sqDiffMat.sum(axis=1)
# 开方,得出距离(欧氏距离)
distances = sqDistances**0.5
# 从小到大排序,得到的是数据的index
sortedDistIndicies = distances.argsort()
classCount={}
# 取出前k个距离最小的
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 createDataSet():
group = np.array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
labels = ['A','A','B','B']
return group, labels
if __name__ == '__main__':
group, labels = createDataSet()
predict = kNN([0.1,0.1], group, labels, 3)
print(predict) # 预测结果为B
数据集介绍:
总共10000条,包含三个特征(每年获得的飞行常客里程数,玩视频游戏所耗时间百分比,每周消费的冰淇淋公升数),而标签类型为(1代表“不喜欢的人”,2代表“魅力一般的人”,3代表“极具魅力的人”)
部分数据展示如下:(需要下载数据集的见文末)
40920 8.326976 0.953952 3 14488 7.153469 1.673904 2 26052 1.441871 0.805124 1 75136 13.147394 0.428964 1
def show():
n = 1000 # number of points to create
xcord1 = [];
ycord1 = []
xcord2 = [];
ycord2 = []
xcord3 = [];
ycord3 = []
markers = []
colors = []
fw = open('datingTestSet2.txt', 'w')
for i in range(n):
[r0, r1] = np.random.standard_normal(2)
myClass = np.random.uniform(0, 1)
if (myClass <= 0.16):
fFlyer = np.random.uniform(22000, 60000)
tats = 3 + 1.6 * r1
markers.append(20)
colors.append(2.1)
classLabel = 1 # 'didntLike'
xcord1.append(fFlyer);
ycord1.append(tats)
elif ((myClass > 0.16) and (myClass <= 0.33)):
fFlyer = 6000 * r0 + 70000
tats = 10 + 3 * r1 + 2 * r0
markers.append(20)
colors.append(1.1)
classLabel = 1 # 'didntLike'
if (tats < 0): tats = 0
if (fFlyer < 0): fFlyer = 0
xcord1.append(fFlyer);
ycord1.append(tats)
elif ((myClass > 0.33) and (myClass <= 0.66)):
fFlyer = 5000 * r0 + 10000
tats = 3 + 2.8 * r1
markers.append(30)
colors.append(1.1)
classLabel = 2 # 'smallDoses'
if (tats < 0): tats = 0
if (fFlyer < 0): fFlyer = 0
xcord2.append(fFlyer);
ycord2.append(tats)
else:
fFlyer = 10000 * r0 + 35000
tats = 10 + 2.0 * r1
markers.append(50)
colors.append(0.1)
classLabel = 3 # 'largeDoses'
if (tats < 0): tats = 0
if (fFlyer < 0): fFlyer = 0
xcord3.append(fFlyer);
ycord3.append(tats)
fw.close()
fig = plt.figure()
ax = fig.add_subplot(111)
# ax.scatter(xcord,ycord, c=colors, s=markers)
type1 = ax.scatter(xcord1, ycord1, s=20, c='red')
type2 = ax.scatter(xcord2, ycord2, s=30, c='green')
type3 = ax.scatter(xcord3, ycord3, s=50, c='blue')
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
ax.legend([type1, type2, type3], ["不喜欢", "魅力一般", "极具魅力"], loc=2)
ax.axis([-5000, 100000, -2, 25])
plt.xlabel('每年获取的飞行常客里程数')
plt.ylabel('玩游戏所耗时间百分比')
plt.show()
# 使用kNN算法改进约会网站的配对效果
import numpy as np
import pandas as pd
import operator
import matplotlib.pyplot as plt
def file_Matrix(filename):
'''
将源数据规整为所需要的数据形状
:param filename: 源数据
:return: returnMat:规整好的数据;classLabelVector:数据类标签
'''
data = pd.read_csv(filename, sep='\t').as_matrix()
returnMat = data[:,0:3]
classLabelVector = data[:,-1]
return returnMat, classLabelVector
def autoNorm(dataSet):
'''
数据归一化
:param dataSet:
:return:
'''
# 每列的最小值
minVals = dataSet.min(0)
# 每列的最大值
maxVals = dataSet.max(0)
ranges = maxVals - minVals
# normDataSet = np.zeros(np.shape(dataSet))
m = dataSet.shape[0]
# np.tile(minVals,(m,1))的意思是将一行数据广播成m行
normDataSet = (dataSet - np.tile(minVals, (m,1))) / np.tile(ranges, (m,1))
# print(normDataSet)
return normDataSet, ranges, minVals
def kNN(inX, dataSet, labels, k):
'''
kNN算法
:param inX: 需要分类的数据
:param dataSet: 样本数据集
:param labels: 样本数据的标签
:param k: 需要取出的前k个
:return: 最有可能的分类
'''
# 获取样本数据的个数
dataSetSize = dataSet.shape[0]
# np.tile函数将待分类的数据inX“广播”成和样本数据dataSet一样的形状
# 获取待分类数据inX与样本数据集中的每个数据之间的差
diffMat = np.tile(inX, (dataSetSize,1)) - dataSet
# 平方
sqDiffMat = diffMat**2
# 按行累加
sqDistances = sqDiffMat.sum(axis=1)
# 开方,得出距离(欧氏距离)
distances = sqDistances**0.5
# 从小到大排序,得到的是数据的index
sortedDistIndicies = distances.argsort()
classCount={}
# 取出前k个距离最小的
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 = file_Matrix('datingTestSet2.txt') #load data setfrom file
normMat, ranges, minVals = autoNorm(datingDataMat)
# print(normMat)
m = normMat.shape[0]
numTestVecs = int(m * hoRatio)
errorCount = 0.0
for i in range(numTestVecs):
classifierResult = kNN(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],4)
print("the classifier came back with: %d, the real answer is: %d" % (classifierResult, datingLabels[i]))
if (classifierResult != datingLabels[i]): errorCount += 1.0
print("测试集个数为%d ,分类错误的个数为%d"%(numTestVecs, errorCount))
print("the total error rate is: %f" % (errorCount/float(numTestVecs)))
print("accuracy: %f" % ((1-errorCount/float(numTestVecs))*100))
if __name__ == '__main__':
datingClassTest()
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 1, the real answer is: 1
... ...
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 2, the real answer is: 2
测试集个数为99 ,分类错误的个数为4
the total error rate is: 0.040404
accuracy: 95.959596
测试的结果是错误率为4.04%,可以接受。不过还可以自主调节hoRatio和变量k的值,以便让错误率降低。
数据集下载:(百度网盘)
链接:https://pan.baidu.com/s/1UErV8D4Hrw557tjj9fyjlQ
提取码:o80d
参考文献:《Machine Learning in Action》