k近邻法(KNN)的Python代码:
import numpy as np #数值计算包
import pandas as pd #数据分析包
import matplotlib.pyplot as plt #画图工具包
from sklearn.datasets import load_iris #导入load_iris数据集
from sklearn.model_selection import train_test_split #划分数据的训练集与测试集包
from collections import Counter #计数
iris=load_iris() #导入数据
df = pd.DataFrame(iris.data, columns=iris.feature_names)
#将iris中的四个特征数据放入df矩阵中
#DataFrame介绍:https://www.jianshu.com/p/8024ceef4fe2
df['label']=iris.target
#在df矩阵的最后插入一列label即样本类别
df.columns=['sepal length','sepal width','petal length','petal width','label']
#输入print(df)即可查看
plt.figure(figsize=(15, 8))
#figsize:指定figure的宽和高
#plt.figure()的使用:https://blog.youkuaiyun.com/m0_37362454/article/details/81511427
plt.subplot(121)
#subplot绘制多个子图介绍:https://www.cnblogs.com/xiaoboge/p/9683056.html
plt.scatter(df[:50]['sepal length'], df[:50]['sepal width'], label='0')
#画散点图
plt.scatter(df[50:100]['sepal length'], df[50:100]['sepal width'], label='1')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.title('original data')
plt.legend() #给图像加上图例
#legend介绍:https://blog.youkuaiyun.com/lanluyug/article/details/80002273
data = np.array(df.iloc[:100, [0, 1, -1]])
#读取df的前100行,第一、二、五列('sepal length','sepal width','label')
#array:计算机为数组分配一段连续的内存,从而支持对数组随机访问
#要选取连续多列就该使用df.iloc
#iloc的用法示例:https://blog.youkuaiyun.com/qq_39697564/article/details/87855167
X, y = data[:,:-1], data[:,-1]
#令X等于'sepal length','sepal width'的100个二维数据(不包括写的那个索引值),令y等于100个类别(只有0,1)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
#train_test_split介绍:https://blog.youkuaiyun.com/qq_36523839/article/details/80280771
#随机数种子的含义:其取值不变时划分得到的结果一模一样,其值改变时,划分得到的结果不同
'''
下面KNN类中predict函数的基本思想:
因为是需要在训练集中找出k个距离测试点最近的点,此处采用先求出k个距离作为基准,
将后面的距离(相比之下较小的距离)不断替代前面的最大的距离,这样就会得出几个最近的距离
(此处还可以先将所有的欧式距离计算出来,然后将每一个欧式距离与对应的标签分类进行绑定,
通过将欧式距离从小到大排序,然后选取前k个,即可得到最近的k个点)
'''
class KNN:
def __init__(self,X_train,y_train,n_neighbors=3,p=2):
self.n = n_neighbors
self.p = p
self.X_train = X_train
self.y_train = y_train
#n_neighbors表示临近点的个数,p表示距离度量
#__init__左右两边为两个下划线
#函数定义时不能将没有默认值的参数放在有默认值的参数的后面
def predict(self,X):
knn_list = []
for i in range(self.n):
dist = np.linalg.norm(X - self.X_train[i],ord = self.p)
knn_list.append((dist,self.y_train[i]))
#np.linalg.norm的介绍:https://blog.youkuaiyun.com/weixin_37763870/article/details/103434397
#append用于在列表末尾添加新的对象,range不包括后面的索引值
#二范数和欧式距离:https://www.cnblogs.com/cainiaoxuexi2017-ZYA/p/12683444.html
#Python 中矩阵或者数组相减的法则:https://blog.youkuaiyun.com/dake13/article/details/80917932
for i in range(self.n,len(self.X_train)):
max_index = knn_list.index(max(knn_list,key = lambda x:x[0]))
dist = np.linalg.norm(X - self.X_train[i],ord = self.p)
if knn_list[max_index][0] > dist:
knn_list[max_index] = (dist,self.y_train[i])
#knn_list为测试点到k个欧式距离最小的训练集的列表,第一列为欧氏距离,第二列为对应的标签分类
#max()函数中key参数介绍:https://blog.youkuaiyun.com/u011675334/article/details/104838083
#lambda用于简化函数定义的书写形式
knn = [k[-1] for k in knn_list]
count_pairs = Counter(knn)
max_count = sorted(count_pairs.items(),key=lambda x:x[1])[-1][0]
return max_count
#sorted介绍:https://www.runoob.com/python3/python3-func-sorted.html
#sorted与key连用https://www.cnblogs.com/yiyaxuan/p/12991842.html
#items() 函数以列表返回可遍历的(键, 值) 元组数组
def score(self, X_test, y_test):
right_count = 0
n = 10
for X, y in zip(X_test, y_test):
label = self.predict(X)
if label == y:
right_count += 1
return right_count / len(X_test)
clf = KNN(X_train, y_train)
print('Test_score: {}'.format(clf.score(X_test, y_test)))
test_point = [6.0, 3.0]
print('Test Point: {}'.format(clf.predict(test_point)))
plt.subplot(122)
plt.scatter(df[:50]['sepal length'], df[:50]['sepal width'], label='0')
plt.scatter(df[50:100]['sepal length'], df[50:100]['sepal width'], label='1')
plt.plot(test_point[0], test_point[1], 'bo', label='test_point')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()
plt.title('classification by KNN(n=3, p=2)')
plt.subplots_adjust(top=0.92, bottom=0.08, left=0.10, right=0.95, hspace=0.25,wspace=0.35)
plt.savefig("demo.jpg")
plt.show()
KNN中predict最后部分计算k个最近邻点中标签分类个数最多的那个标签的代码部分目前没理解,代码参考第3章 k近邻法(KNearestNeighbors)代码实现