import numpy as np
import pandas as pd
KNN算法过程
- 从训练集中选择离预测样本最近的K个样本。
- 根据这K个样本计算这个样本的值(属于哪个类别或具体数值)
对数据集的操作
#读取鸢尾花数据集,header参数来指定标题的行,默认为0,如果没有标题,则使用None
data=pd.read_csv(r"F:\数据集\Iris数据集\iris.csv",header=0)
#data 默认显示全部行 中间用省略号
#data.head()只显示头5行(默认)
#data.head(n)显示头n行
#data.tail()默认显示后5行
#data.tail(n)显示后n行
#data.sample()默认随机抽取一行显示
#data.sample(n)随机抽取n条样本
data.sample(10)
#将Species这一列的各种值映射为相应的数值
data["Species"]=data["Species"].map({"versicolor":0,"setosa":1,"virginica":2})
#data
#删除不需要的 Unnamed: 0列 axis=1表示删除列 默认为0,表示删除行
#data=data.drop("Unnamed",axis=1)与下面这行等价
data.drop("Unnamed: 0",axis=1,inplace=True)
#判断数据集中是否有重复的值, 只要有一个重复则返回true,否则返回false
data.duplicated().any()
#输出原有鸢尾花记录数:150条
len(data)
#删除重复记录,加上inplace=True使其在原有的数据集上操作,而不是在其副本上操作
data.drop_duplicates(inplace=True)
#删除重复记录后:149条
len(data)
#查看各个类别的鸢尾花具有多少条记录
data["Species"].value_counts()
1 50
0 50
2 49
Name: Species, dtype: int64
编写KNN的类
class KNN:
""" 使用python语言实现K近邻算法。(实现分类)"""
#定义初始化方法(init两边是两杠)
def __init__(self,k):
"""
初始化方法
paramters
--------
k:int
邻居的个数.
"""
self.k=k
#定义fit(训练)方法
def fit(self,X,y):
"""训练方法
Parameters
-------
X:类数组类型,形状为:[样本数量,特征数量]
待训练的样本特征
y:类数组类型,形状为:[样本数量]
每个样本的目标值(标签)
"""
#为了统一处理list和dataFrame,将X转化为ndarray数组形式,若X本身就是ndarray类型则什么都不做
self.X=np.asarray(X)
#同理转换y
self.y=np.asarray(y)
#定义预测方法 参数X是测试集的样本,没有考虑权重
def predict(self,X):
"""根据参数传递的样本,对样本数据进行预测
Parameters
---------
X:类数组类型,形状为:[样本数量,特征数量]
待训练的样本特征
Return:
--------
result:数组类型
预测的结果
"""
X=np.asarray(X)
result=[]
#对ndarray数组进行遍历,每次取数组中的一行(一个样本)。
# self.X是训练集的样本 这里用欧式距离
#注意,x是一行,self.X是多行,在做减法是先会对x进行扩展,使其行数和self.X 一样,再对应元素想减
for x in X:
#axis=1 指定按行的方式求和,否则是将数组所有的值求和成一个数值
#对于测试集中的每一个样本,一次与训练集中的所有样本求距离
dis=np.sqrt(np.sum((x-self.X)**2,axis=1))
#返回数组排序后每个元素在原数组中的索引(原数组:排序之前的数组)
index=dis.argsort()
#进行截断,只取前k个元素【取距离最近的k个元素的索引】
index=index[:self.k]
#找到距离最近的k个节点的标签 index不论是list还是ndarray都行(都返回对应位置的标签)
#self.y[index]
#返回数组中每个元素(0、1、2.。。数据最大值 等出现的次数)出现的次数,但元素必须是非负的整数
count=np.bincount(self.y[index])
#返回ndarray数组中,值最大的元素对应的索引,就是出现次数最多的元素的索引,该索引就是我们判定的类别
#count.argmax()
#将判定的类别加到结果中
result.append(count.argmax())
#写成np.array(result)也行(用result创建ndarray数组)
#这里写成np.asarray(result)是将result转化为ndarray数组
return np.asarray(result)
#定义预测方法2 参数X是测试集的样本,考虑权重
def predict2(self,X):
"""根据参数传递的样本,对样本数据进行预测(考虑权重,使用距离的倒数使用权重)
Parameters
---------
X:类数组类型,形状为:[样本数量,特征数量]
待训练的样本特征
Return:
--------
result:数组类型
预测的结果
"""
X=np.asarray(X)
result=[]
#对ndarray数组进行遍历,每次取数组中的一行(一个样本)。
# self.X是训练集的样本 这里用欧式距离
#注意,x是一行,self.X是多行,在做减法是先会对x进行扩展,使其行数和self.X 一样,再对应元素想减
for x in X:
#axis=1 指定按行的方式求和,否则是将数组所有的值求和成一个数值
#对于测试集中的每一个样本,一次与训练集中的所有样本求距离
dis=np.sqrt(np.sum((x-self.X)**2,axis=1))
#返回数组排序后每个元素在原数组中的索引(原数组:排序之前的数组)
index=dis.argsort()
#进行截断,只取前k个元素【取距离最近的k个元素的索引】
index=index[:self.k]
#找到距离最近的k个节点的标签 index不论是list还是ndarray都行(都返回对应位置的标签)
#self.y[index]
#返回数组中每个元素(0、1、2.。。数据最大值 等出现的次数)出现的次数,但元素必须是非负的整数
#[使用weights考虑权重,权重为距离的倒数]
count=np.bincount(self.y[index],weights=1/dis[index])
#返回ndarray数组中,值最大的元素对应的索引,就是出现次数最多的元素的索引,该索引就是我们判定的类别
#count.argmax()
#将判定的类别加到结果中
result.append(count.argmax())
#写成np.array(result)也行(用result创建ndarray数组)
#这里写成np.asarray(result)是将result转化为ndarray数组
return np.asarray(result)
训练与测试:
#提取出每个类别的鸢尾花数据
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)
#构建训练集和测试集 axix=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=KNN(k=3)
#进行训练
knn.fit(train_X,train_y)
#进行测试,获得测试的结果
#result=knn.predict1(test_X)
result=knn.predict2(test_X)
#display(result)
#display(test_y)
display(np.sum(result==test_y))
#display(len(result))
display((np.sum(result==test_y))/len(result))
28
0.9655172413793104
KNN可视化
import matplotlib as mpl
import matplotlib.pyplot as plt
绘制数据
#默认情况下matplotlib不支持中文显示,我们需要设置一下,使其支持
#设置字体为黑体,以支持中文显示
mpl.rcParams["font.family"]="SimHei"
#设置在中文字体是,能正常显示负号(-)
mpl.rcParams["axes.unicode_minus"]=False
#{"versicolor":0,"setosa":1,"virginica":2}
# 设置画布的大小
plt.figure(figsize=(8,8))
#绘制训练集数据散点图 选择花瓣、花萼长度这两个维度 注意:不要将label写成lable了
plt.scatter(x=t0["Sepal.Length"][:40],y=t0["Petal.Length"][:40],color="r",label="versicolor")
plt.scatter(x=t1["Sepal.Length"][:40],y=t1["Petal.Length"][:40],color="g",label="setosa")
plt.scatter(x=t2["Sepal.Length"][:40],y=t2["Petal.Length"][:40],color="b",label="virginica")
#绘制测试集数据 返回数组给right 和 wrong
right=test_X[result==test_y]
wrong=test_X[result!=test_y]
plt.scatter(x=right["Sepal.Length"],y=right["Petal.Length"],color="c",marker="x",label="right")
plt.scatter(x=wrong["Sepal.Length"],y=wrong["Petal.Length"],color="m",marker=">",label="wrong")
plt.xlabel("花萼长度")
plt.ylabel("花瓣长度")
plt.title("KNN分类结果显示")
# 设置图例显示位置 默认best 最合适的地方
plt.legend(loc="best")
#显示图形
plt.show()