KNN分类python实现

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()

在这里插入图片描述


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值