文章目录

  • K近邻算法
  • K近邻算法(KNN)概念
  • KNN原理
  • KNN标准化
  • Skearn库K近邻API
  • K近邻算法实战-预测入住位置
  • 问题背景
  • 问题分析
  • 解题步骤
  • 完整代码
  • 总结
  • 计算步骤
  • 优缺点


K近邻算法

K近邻算法(KNN)概念

KNN最早是由Cover和Hart提出的一种分类算法。其定义为:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别,其中K通常是不大于20的整数。KNN算法中,所选择的邻居都是已经正确分类的对象。该方法在定类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。

举个例子

下图中需要判断绿色圆圈的类别。

KNN算法练习题 knn算法简单例题_机器学习

原始数据有两个类别,分别为蓝色方框和红色三角。下面用KNN法对绿色圆圈进行分类。

  • K=3,即在特征空间中选取3个最相近的特征,见上图的实线圈,此时红色三角形所占比例为2/3,蓝色方块占1/3,由KNN原理可知绿色圆圈的类别属于大多数属于的一个类别。所以绿色圆将被赋予红色三角形那个类。
  • K=5,同理,由于蓝色四方形比例为3/5,因此绿色圆被赋予蓝色四方形类。

在举一个实际点的例子,使用k-近邻算法分类一个电影是爱情片还是动作片。数据集中有两个特征,分别为打斗镜头和接吻镜头,并给出了相应的电影类型。我们的目的是通过两个镜头的次数来判断电影的类型。

KNN算法练习题 knn算法简单例题_K近邻_02


这个例子,凭我们的感觉也能知道,打斗镜头多的是动作片,接吻镜头多的是爱情片,但是K近邻算法是怎么判断的呢,它是通过距离来度量的,这就需要先介绍一下KNN的原理。

KNN原理

简单地说,距离的度量可以用欧式距离,对没错就是欧式距离!给出一个点A(x1,y1),给出一个点B(x2,y2),那么两点之间的距离为

KNN算法练习题 knn算法简单例题_K近邻_03


回到上面的例子,计算未知电影和第一个电影的距离,即计算点(18,90)到(3,104)间的距离。

KNN算法练习题 knn算法简单例题_KNN算法练习题_04


同理可计算出每个已知电影和未知电影的距离

KNN算法练习题 knn算法简单例题_KNN算法练习题_05


如果距离相近,那么类型也相近。

根据KNN定义:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。

先设置K=1,那么则寻找与未知电影距离最近的电影为第二个电影,类型为爱情片。则认为未知电影为爱情片。

设置K=5,那么则寻找与未知电影距离最近的五个电影。

KNN算法练习题 knn算法简单例题_Sklearn_06


距离最近的五个电影中,3/5为爱情片,2/5为动作片,样本中大多数属于爱情片,所以认为未知电影为爱情片。

K的取值决定着最后的结果

KNN标准化

KNN算法是需要对数据进行标准化的
因为计算距离时,是取差的平方并累计,并取累计距离技术的平方根,如果作差的两个数相差较大,那么势必会产生较大的误差。所以需要进行标准化,这样有助于防止具有较大初始值域的属性比具有较小初始值域的属性的权重过大。后面会有具体实例说明。

Skearn库K近邻API

K近邻算法实战-预测入住位置

问题背景

根据位置特征、时间特征等预测入住的位置。见下表

KNN算法练习题 knn算法简单例题_Sklearn_07


数据集已统计了不同时刻、不同位置的入住位置。根据此数据集进行预测。

  • row_id:等级的id:这个因素意义不大,可直接剔除
  • x,y:坐标:定位的位置。
  • accuracy:定位的准确性
  • time:时间戳,不同时间的入住不同,所以时间影响较大。
  • place_id:位置:目标值,最后的入住位置。

问题分析

原数据量有100多万,为了增快运算效率,先进行一些简单的数据处理(如果电脑性能好,可直接用原数据) 。
为缩小数据集进行以下处理:

  • 设置1<x<1.25,2.5<y<2.75。范围之外的数据剔除。
  • 将时间戳进行转换,转化为年月日周时分秒等。转化为这个格式的目的是增加特征,比如加入日、周、时等特征。当然这样处理效果也不一定好,根据实际情况随机应变。
  • 对目标进行缩减,place_id有几万个,目标太多。将入住次数少的place_id剔除。

将处理后的数据进行标准化操作,输入到knn中进行预测。

解题步骤

1、调库

import pandas as pd
import numpy as np
from sklearn.neighbors import KNeighborsClassifier#K近邻算法

2、读取数据

data = pd.read_csv("train.csv")
data.head()

3、缩减数据量

设置1<x<1.25,2.5<y<2.75。范围之外的数据剔除。

data = data.query("x > 1.0 & x < 1.25 & y > 2.5 & y < 2.75")

KNN算法练习题 knn算法简单例题_K近邻_08

pd.query()函数基于DataFrame列的计算代数式,对于按照某列的规则进行过滤的操作

对时间戳进行处理

time_value = pd.to_datetime(data['time'],unit='s')

pd.to_datetime()即为时间戳处理函数,完整表达式为

pd.to_datetime(arg , errors=“raise” , dayfirst=False , yearfirst=False , utc=None , box=True , format=None , exact=True , unit=None , infer_datetime_format=False , origin=“unix” , cache=False)

如果想要了解每个参数的具体意义,见官网 它可以将时间戳转化为时间。
举个例子,将2490195806转化为标准的年月日

pd.to_datetime(2490195806, unit='s')#unit默认为ns,这是只让其显示到s

运行结果

Timestamp(‘2048-11-28 17:03:26’)

它还可以直接转化dataframe形式

df = pd.DataFrame({'year': [2015, 2016],
                   'month': [2, 3],
                   'day': [4, 5]})
pd.to_datetime(df)

KNN算法练习题 knn算法简单例题_K近邻_09


这就是pd.to_datetime()的作用。

把日期转化为字典格式,方便操作

time_value = pd.DatetimeIndex(time_value)

pd.DatetimeIndex()为时间戳索引函数 举个例子,获取时间戳1490195805的索引

time=pd.to_datetime(1490195805, unit='s')
time = pd.DatetimeIndex([time])

输出索引

print(time.day)
print(time.year)

Int64Index([22], dtype=‘int64’)
Int64Index([2017], dtype=‘int64’)

这样就可以把pd.to_datetime()后的数据转化为字典格式。总之上面两步的作用就是将时间戳转化为年月日时间格式。

增加特征

因为将时间戳转化为了时间,所以在原来的基础上增加了年月日周等特征,故我们可以来增加一些特征对最后的目标进行判断。由于年和月都一样,故我们不选取年和月当作特征。选用日、周和时作为特征。

data['day'] = time_value.day
data['hour'] = time_value.hour
data['weekday'] = time_value.weekday

删除无用特征:时间戳和row_id

data = data.drop(['time'],axis=1) #axis=1表示删除列。因为已将时间戳转化为年月日,所以时间戳特征已无意义。
data = data.drop(['row_id'],axis=1)

KNN算法练习题 knn算法简单例题_Sklearn_10

对目标进行缩减,place_id有几万个,目标太多。将入住次数少的place_id剔除。

place_count = data.groupby('place_id').count()#以place_id为目标,统计每个place_id的数目
tf = place_count[place_count.x > 3] #只保存place_id数目大于3的,这里用place_count.x,place_count.y……都一样

KNN算法练习题 knn算法简单例题_机器学习_11


至此,已经筛选出了place_id数目大于3的数据。

但是我们发现上面的数据索引为place_id,会给后面的数据处理带来一定的困难。所以我们应将索引换为0,1,2……。需要用到pd.reset_index()

tf = tf.reset_index()#pd.reset_index()可以把索引重置,上面的索引是place_id,重置之后就是0,1,2,3……

KNN算法练习题 knn算法简单例题_机器学习_12

将筛选的数据要求带入到原数据中,对数据进行缩减

data = data[data['place_id'].isin(tf['place_id'])]

KNN算法练习题 knn算法简单例题_Sklearn_13


至此,数据的筛选部分完成。

4、输入到KNN算法

取出特征值和目标值

y = data['place_id'] #目标值
x = data.drop(['place_id'],axis=1)

进行数据的分割:训练集和测试集

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x,y,test_size=0.25)

进行算法流程

knn = KNeighborsClassifier(n_neighbors=5)
#fit
knn.fit(x_train,y_train)
#predict:得出预测结果
y_predict = knn.predict(x_test)
print("预测的目标签到位置为:",y_predict)
#score:得出准确率
print("预测的准确率:",knn.score(x_test,y_test))

预测结果:

KNN算法练习题 knn算法简单例题_KNN_14


我们可以看到,预测结果非常低,只有8%,这也说明我们对数据的处理存在一些问题。

knn算法是需要进行标准化的,而上面的过程没有加入标准化,下面测试一下加上标准化后的结果。

#标准化,对特征值进行标准化,目标值不用
from sklearn.preprocessing import StandardScaler
std = StandardScaler()
x_train = std.fit_transform(x_train)
x_test = std.transform(x_test)#这里直接调用transform就行,不用重新fit一次
knn = KNeighborsClassifier(n_neighbors=5)
#fit
knn.fit(x_train,y_train)
#predict:得出预测结果
y_predict = knn.predict(x_test)
print("预测的目标签到位置为:",y_predict)
#score:得出准确率
print("预测的准确率:",knn.score(x_test,y_test))

预测结果

KNN算法练习题 knn算法简单例题_机器学习_15


很明显,进行标准化后的预测准确率提高到了47%(当然,准确率还是很低)。这也说明,KNN算法需要进行标准化

完整代码

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler
data = pd.read_csv("train.csv")
# 处理数据
data = data.query("x > 1.0 & x < 1.25 & y > 2.5 & y < 2.75")
time_value = pd.to_datetime(data['time'],unit='s')
time_value = pd.DatetimeIndex(time_value)
data['day'] = time_value.day
data['hour'] = time_value.hour
data['weekday'] = time_value.weekday
data = data.drop(['time'],axis=1)
data = data.drop(['row_id'],axis=1)
place_count = data.groupby('place_id').count()#以place_id为目标,统计每个place_id的数目
tf = place_count[place_count.x > 3] #只保存place_id数目大于3的,这里用place_count.x,place_count.y……都一样
tf = tf.reset_index()#pd.reset_index()可以把索引重置,上面的索引是place_id,重置之后就是0,1,2,3……
data = data[data['place_id'].isin(tf['place_id'])]#筛选出data中符合要求的place_id的数据
#取出特征值和目标值
y = data['place_id'] #目标值
x = data.drop(['place_id'],axis=1)
#进行数据的分割:训练集和测试集
x_train, x_test, y_train, y_test = train_test_split(x,y,test_size=0.25)
std = StandardScaler()
x_train = std.fit_transform(x_train)
x_test = std.transform(x_test)#这里直接调用transform就行,不用重新fit一次
knn = KNeighborsClassifier(n_neighbors=5)#设置n为5
#fit
knn.fit(x_train,y_train)
#predict:得出预测结果
y_predict = knn.predict(x_test)
print("预测的目标签到位置为:",y_predict)
#score:得出准确率
print("预测的准确率:",knn.score(x_test,y_test))

总结

计算步骤

  • 计算已知类别数据集中的点与当前点之间的距离;
    按照距离递增次序排序;
  • 选取与当前点距离最小的k个点;
  • 确定前k个点所在类别的出现频率;
  • 返回前k个点所出现频率最高的类别作为当前点的预测分类。

优缺点

优点:

  • 简单,易于理解,易于实现,无需估计参数,无需训练。

缺点:

  • 计算复杂性高;空间复杂性高;
  • 一般数值很大的时候不用这个,计算量太大。
  • 必须制定k值。k值的选取太小,模型会受到异常点的影响。k的选取太大又会受到类别数量的影响。

使用场景:小数据场景,几千——几万样本。