文章目录
- 前言
- 一、K是什么?
- 二、算法步骤
- 三、缺点
- 四、代码实现
- 五、测试结果
前言
邻近算法,或者说K最邻近(KNN,K-NearestNeighbor)分类算法是数据挖掘分类技术中最简单的方法之一。
该方法的思路非常简单直观:根据其最近的K个样本的分类确定它自身类别的分类算法。
一般来说在计算距离时,可以使用他们之间的直线距离即欧氏距离,或坐标绝对值之和,即曼哈顿距离。
一、K是什么?
所谓K最近邻,就是K个最近的邻居的意思,说的是每个样本都可以用它最接近的K个邻近值来代表。
K的取值非常重要。
K太小:受到个例影响严重,波动较大。
K太大:受到距离较远数据影响,分类模糊。
K的取值受数据集大小影响。一般需要反复尝试,根据经验调整或者使用均方根误差来选取。
二、算法步骤
- 计算新样本和所有样本之间的距离。
- 按照由小到大排序。
- 根据K值选取取前K个样本。
- 加权平均。(计算总距离D以及目标到这个样本的距离d,
三、缺点
- 数据越多计算量越大,效率越低。
- 难以运用到较大的数据集中。
四、代码实现
- 导入必要的库:
import numpy as np
- KNN实现:
class KNN(object):
def __init__(self, data, targets, k, distance='E'):
# 'E' 欧氏距离
# 'M' 曼哈顿距离
self.targets = targets
self.data = data
# data = [[x1,x2,x3,x4,y]]
self.k = k
self.result = []
self.maxDistance = []
distance = distance.upper()
if distance == 'E' or distance == 'M':
self.way = distance
else:
raise Exception("未定义的距离公式")
def __distance(self, target):
distances = []
if self.way == 'E':
for i in self.data:
diff = i[:-1] - target
distance = np.sum(np.power(diff, 2))
distances.append((distance, i))
else:
for i in self.data:
diff = i[:-1] ** 2 ** 0.5 - target ** 2 ** 0.5
distance = np.sum(diff)
distances.append((distance, i))
return distances
def classify(self):
for target in self.targets:
distances = self.__distance(target)
neighbors = sorted(distances, key=lambda distances: distances[0])[:self.k]
total_distance = 0
for distance in neighbors:
total_distance += distance[0]
weights = {}
for item in neighbors:
if item[1][-1] not in weights:
weights[item[1][-1]] = 1 - (item[0] / total_distance)
else:
weights[item[1][-1]] += 1 - (item[0] / total_distance)
maxValue = 0
res = None
for key in weights.keys():
if weights.get(key) > maxValue:
res = key
maxValue = weights.get(key)
self.result.append(res)
self.maxDistance.append(neighbors[self.k - 1][0] ** 0.5)
- 绘制分类图(二维情况下):
def plotKNN(classifier):
colors = ['r', 'g', 'b', 'c', 'm', 'y', 'k']
figure, axes = plt.figure(), plt.gca()
for i in range(len(classifier.data)):
x = np.array([classifier.data[i][0]])
y = np.array([classifier.data[i][1]])
plt.plot(x, y,
color=colors[int(classifier.data[i][-1])],
marker='o', # 点的形状为圆点
ms=7,
linestyle='') # 线型为空,也即点与点之间不用线连接
for i in range(len(classifier.targets)):
x = np.array([classifier.targets[i][0]])
y = np.array([classifier.targets[i][1]])
plt.plot(x, y,
color=colors[int(classifier.result[i])],
marker='*', # 点的形状为星形
ms=20,
linestyle='') # 线型为空,也即点与点之间不用线连接
circle = plt.Circle((x[0], y[0]), classifier.maxDistance[i], fill=False,
color=colors[int(classifier.result[i])])
axes.add_patch(circle)
plt.axis('equal')
plt.grid(True)
plt.title("KNN")
plt.show()
- 测试及评估:
import random
import numpy as np
from KNN import KNN
import csv
if __name__ == '__main__':
data = []
classification = {'M': 1, 'B': 0}
with open('E:\\ML\\dataset\\prostate-cancer\\Prostate_Cancer.csv', 'r') as f:
f_csv = csv.reader(f)
header = next(f_csv)
for row in f_csv:
row.append(classification[row[1]])
del row[1]
del row[0]
data.append(np.array(row, dtype='float64'))
random.shuffle(data)
n = len(data) // 3
test_data = data[:n]
test_data_labels = []
for i in range(len(test_data)):
test_data_labels.append(test_data[i][-1])
test_data[i] = test_data[i][:-1]
train_data = data[n:]
# 测试
correct = 0
incorrect = 0
classifier = KNN(train_data, test_data, 5, 'E')
classifier.classify()
result = classifier.result
# 62个M,38个B
for i in range(len(test_data)):
if test_data_labels[i] == result[i]:
correct += 1
else:
incorrect += 1
print("Correct: " + str(correct))
print("Incorrect: " + str(incorrect))
print("AC Rate: {:.2f}%".format(100 * (correct/(correct + incorrect))))
五、测试结果