Fork me on GitHub

分类算法之k-近邻算法(KNN)

一、k-近邻算法概述

 1、什么是k-近邻算法

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

2、欧式距离

两个样本的距离可以通过如下公式计算,又叫欧式距离。比方说计算a(a1,a2,a3),b(b1,b2,b3)样本之间的距离:

\[\sqrt {{{\left( {{a_1} - {b_1}} \right)}^2} + {{({a_2} - {b_2})}^2} + ({a_3} - {b_3})} \]

3、实例

 我们可以根据一部电影中的某些特征来判断该电影属于什么类别:

 我们可以计算未知电影与已知电影的欧式距离,从而判断类别:

 按照欧式距离的计算公式计算,比如:

\[\sqrt {{{\left( {18 - 3} \right)}^2} + {{(90 - 104)}^2}}  = 20.5\]

根据距离的远近,从而判断未知样本与哪个类别更近,就可以判断未知样本的类别。

二、案例

(一)k-近邻算法API

1、sklearn.neighbors.KNeighborsClassifier(n_neighbors=5,algorithm='auto')

  • n_neighbors:int,可选(默认= 5),k_neighbors查询默认使用的邻居数
  • algorithm:{‘auto’,‘ball_tree’,‘kd_tree’,‘brute’},可选用于计算最近邻居的算法。‘ball_tree’将会使用 BallTree;‘kd_tree’将使用 KDTree;‘auto’将尝试根据传递给fit方法的值来决定最合适的算法。 (不同实现方式影响效率)

(二)k-近邻算法实例(预测入住位置)

1、实例说明

  可进入https://www.kaggle.com/c/facebook-v-predicting-check-ins/data查看详情,Facebook创建了一个人工世界,由10万乘10公里的正方形中的100,000多个地方组成。对于给定的一组坐标,您的任务是返回最可能的位置的排名列表。

文件说明:

  • train.csv,test.csv 

          row_id:签到事件的ID
          xy:坐标
          精度:位置精度
          时间:时间戳
         place_id:入住位置ID,这是您要预测的目标

其数据形式如下:

   row_id       x       y  accuracy    time    place_id
0       0  0.7941  9.0809        54  470702  8523065625
1       1  5.9567  4.7968        13  186555  1757726713
2       2  8.3078  7.0407        74  322648  1137537235
3       3  7.3665  2.5165        65  704587  6567393236
4       4  4.0961  1.1307        31  472130  7440663949
5       5  3.8099  1.9586        75  178065  6289802927
6       6  6.3336  4.3720        13  666829  9931249544
7       7  5.7409  6.7697        85  369002  5662813655
8       8  4.3114  6.9410         3  166384  8471780938
9       9  6.3414  0.0758        65  400060  1253803156
...

2、实例解析

显然,xy坐标、位置精度、时间戳是特征值,入住位置是目标值;那么这就是一个分类问题。这个数据量是比较大的,我们可以对其进行如下处理:

  • 缩小数据范围(0<x<2,0<y<2)
  • 将时间戳转换成年、月、日、时等新的特征
  • 删除少于指定位置的签到人数位置删除

然后,再进行特征选取与目标值选取,以及做下面的操作。

3、实现 

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
import numpy as np


def knn():
    """
    近邻算法:预测入住位置
    :return:
    """
    # 读取训练集数据
    df = pd.read_csv("./data/k-近邻算法数据/train.csv")
    # 读取前5行数据
    # print(df.head(5))

    """
    一、进行数据处理:
    1、缩小数据范围
    2、时间戳处理
    3、删除少于指定位置的签到人数位置删除
    """
    # 1、缩小数据范围
    df = df.query("x>0 & x<1.2 & y>0 & y<1.23")    # 2、时间戳处理
    df_time = pd.to_datetime(df["time"], unit='s')
        #把日期格式处理成字典格式
    time_dict = pd.DatetimeIndex(df_time)
    # 构造时间特征
    df["day"] = time_dict.day
    df["hour"] = time_dict.hour
    df["weekday"] = time_dict.weekday
        # 删除时间戳这一列
    df = df.drop(['time'], axis=1)
    # 3、删除少于指定位置的签到人数位置删除
    place_count = df.groupby('place_id').count()
    # 过滤出少于指定位置的签到人数位置,通过reset_index将索引转成列进行操作
    pf = place_count[place_count["row_id"] > 3].reset_index()
    # 根据指定place_id进行过滤
    df = df[df['place_id'].isin(pf['place_id'])]

    """
    二、获取特征值、目标值
    """
    # 1、获取特征值
    x = df.drop(['place_id'], axis=1)

    # 2、获取目标值
    y = df['place_id']

    """
    三、进行数据集分割,分成训练集和测试集
    """
    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.25)
""" 四、特征工程:标准化 """ sd = StandardScaler() # 对训练集进行标准化 x_train = sd.fit_transform(x_train.astype(np.float64)) # 对测试集进行标准化 x_test = sd.transform(x_test.astype(np.float64))
""" 五、进行KNN算法预测 fit predict score """ knn = KNeighborsClassifier(n_neighbors=5) knn.fit(x_train, y_train) # 预测位置 y_predict = knn.predict(x_test) # print('预测的位置:',y_predict) # 准确率 predict_accurate = knn.score(x_test, y_test) print(predict_accurate) if __name__ == '__main__': knn()
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
import numpy as np


def knn():
    """
    近邻算法:预测入住位置
    :return:
    """
    # 读取训练集数据
    df = pd.read_csv("./data/k-近邻算法数据/train.csv")

    # 读取前5行数据
    # print(df.head(5))

    """
    进行数据处理:
    1、缩小数据范围
    2、时间戳处理
    3、删除少于指定位置的签到人数位置删除
    """
    # 1、缩小数据范围
    df = df.query("x>0 & x<1.2 & y>0 & y<1.23")
    #
    # 2、时间戳处理
    df_time = pd.to_datetime(df["time"], unit='s')
    # print(df_time)
    """
    132        1970-01-08 19:14:45
    142        1970-01-02 00:41:22
    ...
    """
    #   #把日期格式处理成字典格式
    time_dict = pd.DatetimeIndex(df_time)
    # print(time_dict)
    """
    DatetimeIndex(
    ['1970-01-08 19:14:45', '1970-01-02 00:41:22',
   '1970-01-07 06:32:23', '1970-01-02 18:59:24',...], 
    dtype='datetime64[ns]', name='time', length=417477, freq=None
    )
    """
    # 构造时间特征
    df["day"] = time_dict.day
    df["hour"] = time_dict.hour
    df["weekday"] = time_dict.weekday

    # 删除时间戳这一列
    df = df.drop(['time'], axis=1)
    # print(df)
    """
                    row_id       x       y   ...     day  hour  weekday
    132            132  0.1902  0.1510   ...       8    19        3
    142            142  0.1318  0.4975   ...       2     0        4
    149            149  0.0179  0.2321   ...       7     6        2
    ...
    """
    # 3、删除少于指定位置的签到人数位置删除
    place_count = df.groupby('place_id').count()
    # print(place_count)
    """
                    row_id    x    y  accuracy  day  hour  weekday
    place_id                                                  
    1000213704      22   22   22        22   22    22       22
    1000842315       6    6    6         6    6     6        6
    1002574526       1    1    1         1    1     1        1
    1002803051       1    1    1         1    1     1        1
    ...
    """
    # 过滤出少于指定位置的签到人数位置,通过reset_index将索引转成列进行操作
    pf = place_count[place_count["row_id"] > 3].reset_index()
    # print(pf)
    """
               place_id  row_id    x    y  accuracy  day  hour  weekday
    0     1000213704      22   22   22        22   22    22       22
    1     1000842315       6    6    6         6    6     6        6
    ...
    """
    # 根据指定place_id进行过滤
    df = df[df['place_id'].isin(pf['place_id'])]
    # print(df)

    """
    获取特征值、目标值
    """
    # 1、获取特征值
    x = df.drop(['place_id'], axis=1)

    # 2、获取目标值
    y = df['place_id']

    """
    进行数据集分割,分成训练集和测试集
    """
    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.25)
    """
    特征工程:标准化
    """
    sd = StandardScaler()
    # 对训练集进行标准化
    x_train = sd.fit_transform(x_train.astype(np.float64))
    # 对测试集进行标准化
    x_test = sd.transform(x_test.astype(np.float64))
    """
    进行KNN算法预测
    fit predict score
    """
    knn = KNeighborsClassifier(n_neighbors=5)
    knn.fit(x_train, y_train)
    # 预测位置
    y_predict = knn.predict(x_test)
    # print('预测的位置:',y_predict)
    """
    [2013736336 4137191191 5861856288 ... 4223174852 8114087113 7163230644]
    """
    # 准确率
    predict_accurate = knn.score(x_test, y_test)
    print(predict_accurate)


if __name__ == '__main__':
    knn()
完整输出

可以看出,上面的实现的大致步骤是:获取数据与处理数据-->获取特征值与目标值-->进行数据集切割-->特征工程(标准化、降维等)-->算法预测

三、k-近邻算法的优缺点 

1、优点

k-近邻算法的优点很明显,那就是简单、易于理解、易于计算。

2、缺点

  • 内存开销大

可以看到,计算两个样本的欧式距离,如果样本数量较大,这样系统的开销比较大。

  • k值的选择需慎重

  k值在上面的实例过程中体现在KNeighborsClassifier方法的n_neighbors参数,如果这个参数过小,易受到异常点的影响;如果参数过大,那么容易受k值数量的波动。

 

posted @ 2020-05-29 23:38  iveBoy  阅读(1064)  评论(0编辑  收藏  举报
TOP