利用k近邻模型进行鸢尾花分类-Python实现
数据集简介https://www.cnblogs.com/mandy-study/p/7941365.html
K近邻算法
参考书:李航的《统计学习方法》
书上的内容写得比我好得多的多!这里不重复叙述,以下给出算法步骤:
- step1:计算待预测向量 x t e s t x_{test} xtest与数据集中各个 x i x_{i} xi的"距离" D D D
- step2:从 D D D中选择 k k k个最小"距离"的数据集中的向量,记 N K ( x ) N_{K}(x) NK(x)
- step3:对
N
K
(
x
)
N_{K}(x)
NK(x)中向量的标签计数,如果标签"apple"计数最多,则
待测向量 x t e s t x_{test} xtest的标签即为"apple"
距离度量公式
Python实现(部分截图+代码解释)
最终版代码在最后面给出
- 从sklearn.datasets模块中加载鸢尾花的数据集。
- 用 df 可以以 dataframe 的结构观察数据,也可以用df.values 换成array结构。
- 先写一个距离度量公式
def f1(x1,x2,p):
#两向量之间的距离度量
return sum(np.power(abs(x1-x2),p))**(1/p)
- 将任务拆解成3个部分、用4个def完成
- 完成算法step1内容
计算1个向量 x 1 x_{1} x1与数据集中所有向量的距离;返回键为:数据集中向量的索引,值为:待测向量 x 1 x_{1} x1与数据集中对应索引向量的距离的字典
def distance_dict(x1,Training_set,p):
return {inx:f1(x1,val,p) for inx,val in enumerate(Training_set)}
上述代码中的enumerate()方法可以查阅相关文章。
- 完成算法step2内容
- 对字典的值进行排序,取出k个最小的值;
返回键为:索引值 值为:距离值 的字典
def sort_dict(x1,Training_set,p,k):
#排序dict_distance中最小的k个数据,
Distance_dict = distance_dict(x1,Training_set,p)
Sort_Distance_dict = sorted(Distance_dict.items(),key=lambda x:x[1]) #元组形式
return dict([Sort_Distance_dict[i] for i in range(k)])
上述代码中的第4行可以查阅字典排序的相关文章。
- 完成算法step3内容
- 对上面函数得到的字典进行处理,sort_dict(x1,Training_set,p,k) 得到的结果是字典,而字典的键是对应于Training_set中的索引值。
我们知道的是在Training_set和Training_lable 中array和list数据结构,定位查找的复杂度是常数级的。
下面的函数 count_dict(sort_dict,Training_lable) 的目的是利用索引值构造与待测向量 x t e s t x_{test} xtest最接近 k k k个数据向量 的标签计数的字典结构。(通俗点就是对 N K ( x ) N_{K}(x) NK(x)中向量的标签行计数)
#sort_k_dict参数是字典的数据结构
def count_dict1(sort_dict,Training_lable):
counter = {}
for i in sort_dict.keys(): #i是该字典的键,即为索引值!
if Training_lable[i] not in counter:
counter[Training_lable[i]] = 1
else:
counter[Training_lable[i]] += 1
return counter
关于计数功能,这里给出第二个方法:
#通过collection Counter类 实现相同效果
from collections import Counter
def count_dict2(sort_dict,Training_lable):
counter = Counter([Training_lable[i] for i in sort_dict.keys()])
return counter
- 在字典中找出 值最大所对应的键,即k个标签中占比最高的那个!(通俗点就是如果标签"apple"计数最多,则待测向量 x t e s t x_{test} xtest的标签即为"apple")
#count_dict 参数是字典,键是标签、值是k个标签中对应于键的个数;
def print_count_dict(count_dict):
max_num = max(count_dict.values())
key_list = [key for key,value in count_dict.items() if value == max_num]
if len(key_list) == 1: #如果k个标签中能确定只有1个标签对应的值最大
return key_list[0] #返回该标签
else:
return tuple(key_list) #否则以元组的形式返回多个标签
最后
到目前为止,算法的分解步骤已经完成!只要将上述def进行整理用个def封装起来(class我不会…)。最终版代码如下:(建议用jupyter)
import pandas as pd
import numpy as np
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['label'] = iris.target
#df.values 查看df的数据
#定义一个KNN函数,将部分中间过程的函数整理一下;直接对 向量x1是属于数据集的哪个标签进行判断,返回标签值;
def f1(x1,x2,p):
#两向量之间的距离
return sum(np.power(abs(x1-x2),p))**(1/p)
def KNN(x1,Training_set,Training_lable,p,k):
def distance_dict(x1,Training_set,p):
return {inx:f1(x1,val,p) for inx,val in enumerate(Training_set)}
def sort_dict(x1,Training_set,p,k):
#排序dict_distance中最小的k个数据,
Distance_dict = distance_dict(x1,Training_set,p)
Sort_Distance_dict = sorted(Distance_dict.items(),key=lambda x:x[1]) #元组形式
return dict([Sort_Distance_dict[i] for i in range(k)])
def count_dict1(sort_dict,Training_lable):
counter = {}
for i in sort_dict.keys(): #i是该字典的键,即为索引值!
if Training_lable[i] not in counter:
counter[Training_lable[i]] = 1
else:
counter[Training_lable[i]] += 1
return counter
def print_count_dict(count_dict):
max_num = max(count_dict.values())
key_list = [key for key,value in count_dict.items() if value == max_num]
if len(key_list) == 1: #如果k个标签中能确定只有1个标签对应的值最大
return key_list[0] #返回该标签
else:
return tuple(key_list) #否则以元组的形式返回多个标签
Sort_dict = sort_dict(x1,Training_set,p,k) #计算x1与数据集Trainin_set中所有向量的距离,并返回k个最小的索引,距离值的字典
Count_dict = count_dict1(Sort_dict,Training_lable) #将排序好的Sort_dict进行 数据集标签 的计数,返回字典形式
Print_count_dict = print_count_dict(Count_dict) #输出标签计数字典中值最大的键,如果最大值有多个,则以元组的形式返回多个标签
return Print_count_dict
思路
本次实践将0、1、2类标签划分为训练集和测试集两个数据集,其中训练集占比80%(0、1、2类标签数据各40条),测试集占比20%(0、1、2类标签数据各10条)
#处理数据
dataAll = df.values
dataAll_x = np.array([np.delete(i,-1) for i in dataAll])
dataAll_label = np.array([i[-1] for i in dataAll])
这里因为提前知道了鸢尾花数据,所以才这么划分!
#数据集和测试集用np.array的数据结构,而数据标签用列表的数据结构
training_set = np.array(list(dataAll_x[0:40])+list(dataAll_x[50:90])+list(dataAll_x[100:140]))
training_set_lable = list(dataAll_label[0:40])+list(dataAll_label[50:90])+list(dataAll_label[100:140])
test_set = np.array(list(dataAll_x[40:50])+list(dataAll_x[90:100])+list(dataAll_x[140:150]))
test_set_lable = list(dataAll_label[40:50])+list(dataAll_label[90:100])+list(dataAll_label[140:150])
现在判断测试集中的向量是属于哪些类别;
然后把遍历KNN函数得到的结果用列表的结果装起来,将此列表与测试集已知的标签列表进行对比,才有准确率的概念
#KNN算法中的参数:p取2 k取7
KNN_lable_list = [KNN(i,training_set,training_set_lable,2,7)for i in test_set]
KNN_lable_list
num1 = 0
for i,j in zip(KNN_lable_list,test_set_lable):
if i != j:
num1 += 1
print("KNN算法判断的类别与测试标签类别有误\n"+"判断的类别为:"+str(i)+"\n原测试集类别为:"+str(j))
accuracy = (len(test_set_lable)-num1)/len(test_set_lable)
print("准确率:"+str(accuracy))
总结
本人非计算机专业,对于代码的优化、数据结构的应用上做的并不好!而且上面代码写完后发现标签的单词打错了…label打成了lable!
建议:
在参考此份代码的同时,一定要把函数def调出来,自己多带几个参数进去玩,同时也要去参考李航《统计学习方法》中的K近邻。