一、 题目
原生python实现knn分类算法
二、 算法设计
k 近邻(k-Nearset Neighbor,简称 kNN)学习是一种常用的监督学习方法,其工作机制非常简单:给定测试样本,基于某种距离度量找出训练集中与其最靠近的 k 个训练样本,然后基于这 k 个“邻居”的信息来进行观测。通常,在 分类任务 中可使用“投票法”,即将这 k 个样本点中出现最多的类别标记作为预测结果;在 回归任务 中可使用“平均法”,将这 k 个样本的实值输出标记的平均值作为预测结果;还可以基于距离远近进行加权平均或加权投票,距离越近的样本权重越大。
程序流图:
Knn 算法:
将每个样本视作一个点
-
载入数据集,对数据进行必要的预处理
-
设置参数K,K最好选择奇数,因为后续进行归类的策略是少数服从多数,设置K为奇数的话总会有结果。
-
计算待预测点与已知点之间的关系,这里的关系可以有多种方式来体现,常用如下:
①欧式距离(应用较广,其他及其算法也有广泛应用),其计算方法:
②曼哈顿距离
③…… -
之前确定了参数K,计算了待预测点与已知点之间的距离衡量,将计算的结果进行从小到大排序,取前K个点
-
将待预测点归类为多数的那一个类别,这便是对于未知点的类别预测结果了。
三、源代码
import numpy as np
import pandas as pd
# 读取数据集,header参数来指定参数标题的行,默认为0,第一行,如果没有标题使用None
data = pd.read_csv('iris.csv', header=0)
# 对文本进行处理,将Species列的文本映射成数值类型
data['Species'] = data['Species'].map({'virginica': 0, 'setosa': 1, 'versicolor': 2})
data.sample(10)
# 删除不需要的列
data.drop("id", axis=1, inplace=True)
# 重复值检查,any(),一旦有重复值,就返回True
data.duplicated().any()
# 删除重复的数据
data.drop_duplicates(inplace=True)
# 查看各类别的数据条数
data['Species'].value_counts()
#显示读取的数据
print(data)
# 编写KNN类
class KNN:
def __init__(self, k):
"""
k:int
邻居的个数
"""
self.k = k
def fit(self, X, y):
"""
X:类似数组类型,list,ndarray……形状:[样本的数量,特征的数量]
y:类似数组类型,形状为[样本数量]
每个样本的目标值,也是就是标签
"""
# 将X转换成ndarray类型,如果X已经是ndarray则不进行转换
self.X = np.asarray(X)
self.y = np.asarray(y)
def predict(self, X):
"""根据参数传递的样本,对样本数据进行预测,返回预测之后的结果
----
X:类似数组类型,list,ndarray……形状:[样本的数量,特征的数量]
Return
----
result:数类型,预测的结果。
"""
# 将测试的X转换为ndarray结构
X = np.asarray(X)
result = []
for x in X:
# ndarray相减为对应元素相减,测试的X的每一行与self.X 相减
# 求欧氏距离:每个元素都取平方值
dis = np.sqrt(np.sum((x - self.X) ** 2, axis=1))
# 求最近的k个点的距离,sort()排序不适用,因为排序后打乱了顺序
# argsort(),返回每个元素在排序之前原数组的索引
index = dis.argsort()
# 取前k个元素,距离最近的k的元素
index = index[:self.k]
# 返回数组中每个元素出现的次数,元素必须是非负整数
count = np.bincount(self.y[index])
# 返回ndarray之最大的元素的索引,该索引就是我们判定的类别
result.append(count.argmax())
return np.asarray(result)
def predict2(self, X):
"""根据参数传递的样本,对样本数据进行预测(考虑权重,使用距离的倒数作为权重),返回预测之后的结果
Parameters
----
X:类似数组类型,list,ndarray……形状:[样本的数量,特征的数量]
Return
----
result:数类型,预测的结果。
"""
# 将测试的X转换为ndarray结构
X = np.asarray(X)
result = []
for x in X:
# ndarray相减为对应元素相减,测试的X的每一行与self.X 相减
# 求欧氏距离:每个元素都取平方值
dis = np.sqrt(np.sum((x - self.X) ** 2, axis=1))
# 求最近的k个点的距离,sort()排序不适用,因为排序后打乱了顺序
# argsort(),返回每个元素在排序之前原数组的索引
index = dis.argsort()
# 取前k个元素,距离最近的k的元素
index = index[:self.k]
# 返回数组中每个元素出现的次数,元素必须是非负整数,【使用weight考虑权重,权重为距离的倒数】
count = np.bincount(self.y[index], weights=1 / dis[index])
# 返回ndarray之最大的元素的索引,该索引就是我们判定的类别
result.append(count.argmax())
return np.asarray(result)
#数据集拆分成训练集和测试集
#1、提取每个类别鸢尾花的数量
t0 = data[data['Species']==0]
t1 = data[data['Species']==1]
t2 = data[data['Species']==2]
#打乱顺序,random_state ,记住打乱的顺序
t0 = t0.sample(len(t0),random_state=0)
t1 = t1.sample(len(t1),random_state=0)
t2 = t2.sample(len(t2),random_state=0)
train_X = pd.concat([t0.iloc[:40,:-1],t1.iloc[:40,:-1],t2.iloc[:40,:-1]],axis=0)
train_Y = pd.concat([t0.iloc[:40,-1],t1.iloc[:40,-1],t2.iloc[:40,-1]],axis=0)
test_X = pd.concat([t0.iloc[40:,:-1],t1.iloc[40:,:-1],t2.iloc[40:,:-1]],axis=0)
test_Y = pd.concat([t0.iloc[40:,-1],t1.iloc[40:,-1],t2.iloc[40:,-1]],axis=0)
#进行训练与测试
knn = KNN(k=3)
#进行训练
knn.fit(train_X,train_Y)
#进行测试
result = knn.predict(test_X)
# display(result)
# display(test_Y)
#查看预测结果
print(np.mean(result == test_Y))