機(jī)器學(xué)習(xí)(四):K-近鄰(KNN)算法原理及案例分析

一、算法簡介

k-近鄰算法(K-NearestNeighbor,簡稱KNN)是1967年由Cover T和Hart P提供的一種基本分類方法惦积,數(shù)據(jù)挖掘分類技術(shù)中最簡單的方法之一。KNN算法的核心思想是如果一個樣本在特征空間中的k個最相鄰的樣本中的大多數(shù)屬于某一個類別搀菩,則樣本也屬于這個類別,并具有這個類別上樣本的特性而咆。


如上圖所示胖腾,綠色圓要被決定賦予哪個類烟零,是紅色三角形還是藍(lán)色四方形?如果K=3胸嘁,由于紅色三角形所占比例是2/3,綠色圓將被賦予紅色三角形那個類瓶摆,如果K=5,由于藍(lán)色四方形比例為3/5性宏,因此綠色圓被賦予藍(lán)色四方形類群井。

二、原理

1毫胜、距離公式:

歐氏距離:歐幾里得距離或歐幾里得度量是歐幾里得空間中兩點(diǎn)之間“普通”(即直線)距離书斜。使用這個距離,歐式空間成為度量空間酵使。相關(guān)聯(lián)的范數(shù)成為歐幾里得范數(shù)荐吉。較早的文獻(xiàn)稱之為畢達(dá)哥拉斯度量。

  • 二維空間公式:
    \rho=\sqrt[]{(x_{2}-x_{1})^2+(y_{2}-y_{1})^2},其中口渔,\rho為點(diǎn)(x_{2},y_{2})與點(diǎn)(x_{1},y_{1})之間的歐式距離
    |x|=\sqrt[]{x_{2}^2+y_{2}^2},其中|x|為點(diǎn)(x_{2},y_{2})到原點(diǎn)的歐氏距離样屠。
  • 三維空間的公式:
    \rho=\sqrt[]{(x_{2}-x_{1})^2+(y_{2}-y_{1})^2+(z_{2}-z_{1})^2},
    |x|=\sqrt[]{{x_{2}^2+y_{2}^2+z_{2}^2}}
  • n維空間的公式
    d(x,y):=\sqrt[]{(x_{1}-y_{1})^2+(x_{2}-y_{2})^2+...+(x_{n}-y_{n})^2}=\sqrt[]{\sum_{i=1}^{n}(x_{i}-y_{i})^2}

2、算法說明

通俗地講,就是計(jì)算一個點(diǎn)與樣本空間所有點(diǎn)之間的距離痪欲,取出與該點(diǎn)最近的k個點(diǎn)悦穿,然后統(tǒng)計(jì)這k個點(diǎn)里面所屬分類比例最大的,則該點(diǎn)就屬于這個占比最大的分類业踢。

一般流程:

  • 收集數(shù)據(jù):可以使用爬蟲進(jìn)行數(shù)據(jù)的收集栗柒,也可以使用第三方提供的免費(fèi)或收費(fèi)的數(shù)據(jù)。
  • 準(zhǔn)備數(shù)據(jù):可以使用python解析知举,預(yù)處理數(shù)據(jù)
  • 分析數(shù)據(jù):可以使用很多方法對數(shù)據(jù)進(jìn)行分析瞬沦,例如使用Matplotlib將數(shù)據(jù)可視化。
  • 特征工程:標(biāo)準(zhǔn)化雇锡,KNN數(shù)據(jù)需要進(jìn)行標(biāo)準(zhǔn)化
  • 進(jìn)行算法流程:得出預(yù)測結(jié)果逛钻,計(jì)算準(zhǔn)確率
  • 使用算法:準(zhǔn)確率在可接受的范圍,就可以運(yùn)行K-近鄰算法進(jìn)行分類

三遮糖、實(shí)戰(zhàn)分析

1绣的、約會網(wǎng)站配對效果判定

1.1背景

海倫女士一直使用在線約會網(wǎng)站尋找適合自己的約會對象,盡管約會網(wǎng)站會推薦不同的選擇欲账,但是她并不是喜歡每一個人屡江,經(jīng)過一番總結(jié),她發(fā)現(xiàn)自己交往過的人可以進(jìn)行如下分類:

  • 不喜歡的人(didntLike)
  • 魅力一般的人(smallDoses)
  • 極具魅力的人(largeDoses)
    經(jīng)過一段時間的數(shù)據(jù)收集赛不,她把這些數(shù)據(jù)放在文本文件datingTestSet.txt中惩嘉,每個樣本占據(jù)一行,總共有1000行踢故。
    數(shù)據(jù)特征:
  • 每年獲得的飛行澄睦瑁客里程數(shù)
  • 玩視頻游戲所消耗時間百分比
  • 每周消費(fèi)的冰激凌公升數(shù)


    數(shù)據(jù)集
1.2準(zhǔn)備數(shù)據(jù):數(shù)據(jù)解析

給出的數(shù)據(jù)包括了特征值與分類標(biāo)簽向量,我們先將數(shù)據(jù)處理一下殿较,將特征值與分類標(biāo)簽分開:

# -*- coding: UTF-8 -*-
import matplotlib.pyplot as plt
import matplotlib.lines as mlines
import numpy as np
import operator
def dataset():
    """
    打開并解析文件耸峭,對數(shù)據(jù)進(jìn)行分類:
    1代表不喜歡
    2代表魅力一般
    3代表極具魅力
    :return:
    """
    #numpy矩陣的形式

    #打開文件
    fr = open('../../數(shù)據(jù)集/機(jī)器學(xué)習(xí)/分類算法/海倫約會/datingTestSet.txt')
    #讀取文件的所有內(nèi)容
    arrayOne = fr.readlines()
    #得到文件行數(shù)
    numoflines = len(arrayOne)
    #返回numpy矩陣,解析完成的數(shù)據(jù):numoflines
    returnMat = np.zeros((numoflines,3))
    #返回分類標(biāo)簽向量
    classLabelVector = []
    #行的索引
    index = 0
    for line in arrayOne:
        # s.strip(rm)淋纲,當(dāng)rm空時,默認(rèn)刪除空白符(包括'\n','\r','\t',' ')
        line = line.strip()
        # 使用s.split(str="",num=string,cout(str))將字符串根據(jù)'\t'分隔符進(jìn)行切片劳闹。
        listFromLine = line.split('\t')
        # 將數(shù)據(jù)前三列提取出來,存放到returnMat的NumPy矩陣中,也就是特征矩陣
        returnMat[index,:] = listFromLine[0:3]#每三個賦值給矩陣的每一行
        if listFromLine[-1] == 'didntLike':
            classLabelVector.append(1)
        elif listFromLine[-1] == 'smallDoses':
            classLabelVector.append(2)
        elif listFromLine[-1] == 'largeDoses':
            classLabelVector.append(3)
        index += 1

    return returnMat,classLabelVector

if __name__ == "__main__":
    returnMat,classLabelVector = dataset()
    print(returnMat)
    print(classLabelVector)
1.3 分析數(shù)據(jù):數(shù)據(jù)可視化

簡單的處理一下數(shù)據(jù)之后,我們對數(shù)據(jù)進(jìn)行可視化,看一下特征之間的關(guān)系洽瞬,

def showdatas(x,y):
    """
    數(shù)據(jù)可視化
    :return:
    """

    fig = plt.figure(figsize=(15,15))
    #第一張散點(diǎn)圖顯示視頻游戲與飛機(jī)里程數(shù)占比關(guān)系
    ax1 = fig.add_subplot(2,2,1)
    colors = []
    #設(shè)置圖例
    didntLike = mlines.Line2D([], [], color='black', marker='.',markersize=6, label='didntLike')
    smallDoses = mlines.Line2D([], [], color='orange', marker='.',markersize=6, label='smallDoses')
    largeDoses = mlines.Line2D([], [], color='red', marker='.',markersize=6, label='largeDoses')
    for i in y:
        if i == 1:
            colors.append('black')
        if i == 2:
            colors.append('orange')
        if i == 3:
            colors.append('red')
    ax1.scatter(x=x[:,0],y=x[:,1],color=colors,s=15)
    ax1.set_title('每年獲得的飛行潮咎椋客里程數(shù)與玩視頻游戲所消耗時間占比')
    ax1.set_xlabel('每年獲得的飛行常客里程數(shù)')
    ax1.set_ylabel('玩視頻游戲所消耗時間占比')
    # 添加圖例
    ax1.legend(handles=[didntLike,smallDoses,largeDoses])

    #第二張散點(diǎn)圖顯示視頻游戲與冰激凌之間的關(guān)系

    ax2 = fig.add_subplot(2,2,2)
    ax2.scatter(x=x[:,1],y = x[:,2],color=colors,s=15)
    ax2.set_title('視頻游戲消耗時間與每周消費(fèi)的冰激凌公升數(shù)')
    ax2.set_xlabel('玩視頻游戲消耗時間')
    ax2.set_ylabel('每周消費(fèi)的冰激凌公升數(shù)')
    # 添加圖例
    ax2.legend(handles=[didntLike,smallDoses,largeDoses])
    # plt.show()
    # print(colors)

    #第三張散點(diǎn)圖顯示飛機(jī)里程數(shù)與冰激凌公升數(shù)的關(guān)系
    ax3 = fig.add_subplot(2,2,3)
    ax3.scatter(x = x[:,0],y = x[:,2],color=colors,s = 15)
    ax3.set_title('每年飛機(jī)飛行里程數(shù)與每周消費(fèi)的冰激凌公升數(shù)')
    ax3.set_xlabel('每年獲得的飛行郴锴裕客里程數(shù)')
    ax3.set_ylabel('每周消費(fèi)的冰激凌公升數(shù)')
    # 添加圖例
    ax3.legend(handles=[didntLike, smallDoses, largeDoses])
    plt.show()
    return None

通過圖像進(jìn)行簡單的分析我們發(fā)現(xiàn)菩颖,如果考慮玩游戲消耗時間占比與每年獲得的飛行常客里程數(shù)的話为障,感覺海倫喜歡有生活質(zhì)量的男人晦闰,畢竟能經(jīng)常飛放祟,還有時間玩游戲,并且占有一定時間比例鹅髓,大概率是一個生活悠閑舞竿,追求質(zhì)量的人。

1.4窿冯、數(shù)據(jù)歸一化

通過1.1我們發(fā)現(xiàn)數(shù)字差值比較大的屬性對計(jì)算結(jié)果的影響比較大,為了消除這種不同取值范圍的特征值影響過大的問題确徙,我們通常采用的方法是將數(shù)值歸一化醒串,如將取值范圍處理為[0,1]之間,前提是我們認(rèn)為這三種特征是同等重要的鄙皇。

  • 歸一化的計(jì)算公式:
    x = \frac{x- min}{max-min}
def autoNorm(x):
    """
    數(shù)據(jù)歸一化
    newValue = (oldValue - min) / (max - min)
    :param x:
    :return:
    """
    #這邊還可以做一些改進(jìn)芜赌,篩掉一些數(shù)據(jù)
    #獲得數(shù)據(jù)的最小值
    minvals = x.min(0)
    maxvals = x.max(0)
    #最大值和最小值的范圍
    ranges = maxvals - minvals
    #shape(x)返回x的矩陣行列數(shù)
    normx = np.zeros(np.shape(x))
    #返回x的行數(shù)
    m = x.shape[0]
    #原始值減去最小值
    normx = x - np.tile(minvals,(m,1))
    #除以最大和最小值的差,得到歸一化的數(shù)據(jù)
    normx = normx / np.tile(ranges,(m,1))
    #返回歸一化數(shù)據(jù)結(jié)果伴逸,數(shù)據(jù)范圍缠沈,最小值
    return normx,ranges,minvals

if __name__ == "__main__":
    returnMat,classLabelVector = dataset()
    normx, ranges, minvals = autoNorm(returnMat)
    # classifyDataset(normx,classLabelVector)
    # showdatas(returnMat,classLabelVector)
    print(normx)
歸一化后的結(jié)果
1.5 測試算法

將數(shù)據(jù)進(jìn)行歸一化后,我們先將數(shù)據(jù)集劃分出測試集與訓(xùn)練集错蝴,然后進(jìn)行測試算法的工作洲愤,這里我們選擇90%的數(shù)據(jù)為訓(xùn)練集,10%的數(shù)據(jù)為測試集顷锰,

def classify(x_data,y_data,labels,k):
    """
    Knn算法柬赐,分類器
    x_data:訓(xùn)練集
    y_data:測試集
    labels:分類標(biāo)簽
    k:選取的分類區(qū)域
    :return:
    """
    #返回訓(xùn)練集的行數(shù)
    xdatasize = y_data.shape[0]
    #將測試集在行列上進(jìn)行復(fù)制,并減去訓(xùn)練集
    diffMat = np.tile(x_data,(xdatasize,1)) - y_data
    #求特征矩陣差的平方
    sqdiffMat = diffMat**2
    #平方求和
    sqDistance = sqdiffMat.sum(axis=1)
    #開方計(jì)算距離
    distance = sqDistance ** 0.5
    #返回距離從小到大排序后的索引值
    sortedDistance = distance.argsort()
    #定一個記錄類別次數(shù)的字典
    classified = {}
    #將排序后的類別記錄
    for i in range(k):
        #選出前k個元素的類別
        votedlabels = labels[sortedDistance[i]]
        # dict.get(key,default=None),字典的get()方法,返回指定鍵的值,如果值不在字典中返回默認(rèn)值官紫。
        # 計(jì)算類別次數(shù)
        classified[votedlabels] = classified.get(votedlabels,0) + 1
    # python3中用items()替換python2中的iteritems()
    # key=operator.itemgetter(1)根據(jù)字典的值進(jìn)行排序
    # key=operator.itemgetter(0)根據(jù)字典的鍵進(jìn)行排序
    # reverse降序排序字典
    sortedCounts = sorted(classified.items(), key=operator.itemgetter(1), reverse=True)
    #第一個存放的就是出現(xiàn)次數(shù)最多的類別
    return sortedCounts[0][0]


def classifyDataset(normx,labels):
    """
    劃分測試集與訓(xùn)練集
    將訓(xùn)練集的10%劃分為測試集
    :return:
    """
    alpha = 0.1
    #獲得歸一化后數(shù)據(jù)集的行數(shù)
    m = normx.shape[0]
    #10%的數(shù)據(jù)為測試集
    numtest = int (m * alpha)
    #分類錯誤計(jì)數(shù)
    errorcount = 0.0

    #調(diào)用算法 進(jìn)行分類
    for i in range(numtest):
        # 前numtest為測試集肛宋,后m-numtest為訓(xùn)練
        classfyresult = classify(normx[i,:],normx[numtest:m,:],labels[numtest:m],4)
        print("分類結(jié)果:%d\t真實(shí)類別:%d" % (classfyresult,labels[i]))
        if (classfyresult != labels[i]):
            errorcount += 1
    print("錯誤率為:%f%%" % (errorcount / float(numtest)* 100))

    return None
if __name__ == "__main__":
    returnMat,classLabelVector = dataset()
    normx, ranges, minvals = autoNorm(returnMat)
    classifyDataset(normx,classLabelVector)
    # showdatas(returnMat,classLabelVector)
    # print(normx)

運(yùn)行結(jié)果

通過上圖的運(yùn)行結(jié)果我們發(fā)現(xiàn),錯誤率為4%束世,這是一個相當(dāng)不錯的結(jié)果酝陈。如果改變alpha與k值,錯誤率隨著變臉值的變化而變化毁涉。大家可以下去試一下沉帮。

2、預(yù)測一個人簽到的地方(Kaggle比賽項(xiàng)目)

這個比賽是由facebook與kaggle聯(lián)合舉行的一場比賽薪丁,目的是預(yù)測一個人想簽到哪個地方遇西,為了比賽的目的,F(xiàn)acebook創(chuàng)建了一個人工世界严嗜,由10萬×10公里的正方形中的100000個地方組成粱檀,對于給定的一組坐標(biāo),我們的任務(wù)是返回最可能的位置的排名列表漫玄,數(shù)據(jù)被制作成類似于來自移動設(shè)備的位置信號茄蚯,從而更具真實(shí)性压彭。
首先,我們來看一下數(shù)據(jù)渗常,


數(shù)據(jù)相對較大壮不,有幾百萬行,這里只展示一小部分皱碘。
第二個案例我們將不再把算法的具體步驟寫出來询一,sklearn有專門封裝好的KNN算法,我們只需要調(diào)用一下癌椿,改變一下參數(shù)即可

  • 數(shù)據(jù)預(yù)處理
    先導(dǎo)入一下需要用到的包健蕊,
import pandas as pd
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

1、數(shù)據(jù)量問題
一方面數(shù)據(jù)量過大踢俄,另一方面缩功,有些數(shù)據(jù)值較小,研究價值不大都办,我們將這部分?jǐn)?shù)據(jù)進(jìn)行刪除嫡锌。

def knn():
    """
    knn預(yù)測分類
    :return:
    """
    # 讀取數(shù)據(jù)
    data = pd.read_csv('../../數(shù)據(jù)集/機(jī)器學(xué)習(xí)/分類算法/facebook-v-predicting-check-ins/train.csv')
    print(data.count(axis=0)) 
    # 數(shù)據(jù)預(yù)處理
    #縮小數(shù)據(jù)范圍
    data = data.query(" x > 0.25 & x < 1.25 & y > 2.5 &y < 2.75")
    print(data.count(axis=0))
if __name__ == "__main__":
    knn()

我們看一下結(jié)果:


也就是說原先由29118021條數(shù)據(jù),經(jīng)過處理后琳钉,數(shù)據(jù)有62594條势木。
2、時間處理
這里的時間是時間戳槽卫,我們將其轉(zhuǎn)成標(biāo)準(zhǔn)格式跟压,并分割,以此添加一些特征值歼培,

def knn():
    """
    knn預(yù)測分類
    :return:
    """
    # 讀取數(shù)據(jù)
    data = pd.read_csv('../../數(shù)據(jù)集/機(jī)器學(xué)習(xí)/分類算法/facebook-v-predicting-check-ins/train.csv')
    #print(data.count(axis=0))

    # 數(shù)據(jù)預(yù)處理
    #1震蒋、縮小數(shù)據(jù)范圍
    data = data.query(" x > 0.25 & x < 1.25 & y > 2.5 &y < 2.75")
    # print(data.count(axis=0))
    #2、處理時間數(shù)據(jù)
    data['time'] = pd.to_datetime(data['time'], unit='s')
    #把日期格式轉(zhuǎn)換成  字典格式
    time_value = pd.DatetimeIndex(data['time'])
    # print(time_value)
    #3躲庄、增加分割的日期數(shù)據(jù)
    data['day'] = time_value.day
    data['hour']= time_value.hour
    data['weekday'] = time_value.weekday
    print(data.head())
if __name__ == "__main__":
    knn()


處理完之后可以發(fā)現(xiàn)查剖,多出了day,hour,weekday等幾列數(shù)據(jù)。
3噪窘、刪除一些沒用的數(shù)據(jù)
經(jīng)過上面時間處理之后笋庄,time這一列已經(jīng)變成了三列(day,hour,weekday)數(shù)據(jù),time時間戳的數(shù)據(jù)就用不到了倔监,我們可以將它刪掉直砂,

def knn():
    """
    knn預(yù)測分類
    :return:
    """
    # 讀取數(shù)據(jù)
    data = pd.read_csv('../../數(shù)據(jù)集/機(jī)器學(xué)習(xí)/分類算法/facebook-v-predicting-check-ins/train.csv')
    # print(data.count(axis=0))

    # 數(shù)據(jù)預(yù)處理
    #1、縮小數(shù)據(jù)范圍
    data = data.query(" x > 0.25 & x < 1.25 & y > 2.5 &y < 2.75")
    # print(data.count(axis=0))
    #2浩习、處理時間數(shù)據(jù)
    data['time'] = pd.to_datetime(data['time'], unit='s')
    #把日期格式轉(zhuǎn)換成  字典格式
    time_value = pd.DatetimeIndex(data['time'])
    # print(time_value)
    #3静暂、增加分割的日期數(shù)據(jù)
    data['day'] = time_value.day
    data['hour']= time_value.hour
    data['weekday'] = time_value.weekday
    # print(data.head())

    #4、刪除沒用的數(shù)據(jù)
    data = data.drop(['time'],axis=1)
    print(data.head())
if __name__ == "__main__":
    knn()


5谱秽、篩選掉不符合條件的值
這里我們將簽到次數(shù)少于3次的篩掉洽蛀。

def knn():
    """
    knn預(yù)測分類
    :return:
    """
    # 讀取數(shù)據(jù)
    data = pd.read_csv('../../數(shù)據(jù)集/機(jī)器學(xué)習(xí)/分類算法/facebook-v-predicting-check-ins/train.csv')
    # print(data.count(axis=0))

    # 數(shù)據(jù)預(yù)處理
    #1摹迷、縮小數(shù)據(jù)范圍
    data = data.query(" x > 0.25 & x < 1.25 & y > 2.5 &y < 2.75")
    # print(data.count(axis=0))
    #2、處理時間數(shù)據(jù)
    data['time'] = pd.to_datetime(data['time'], unit='s')
    #把日期格式轉(zhuǎn)換成  字典格式
    time_value = pd.DatetimeIndex(data['time'])
    # print(time_value)
    #3郊供、增加分割的日期數(shù)據(jù)
    data['day'] = time_value.day
    data['hour']= time_value.hour
    data['weekday'] = time_value.weekday
    # print(data.head())

    #4峡碉、刪除沒用的數(shù)據(jù)
    data = data.drop(['time'],axis=1)
    # print(data.head())
    #5、將簽到位置少于n個用戶的刪除
    place_count = data.groupby('place_id').count()
    print(place_count)
    tf = place_count[place_count.row_id > 3].reset_index()
    data = data[data['place_id'].isin(tf.place_id)]
    print(data)
if __name__ == "__main__":
    knn()


上面是根據(jù)條件篩選的結(jié)果驮审,下面是刪掉數(shù)據(jù)后的結(jié)果鲫寄。
6、取特征值與目標(biāo)值疯淫,并分割測試集與訓(xùn)練集

def knn():
    """
    knn預(yù)測分類
    :return:
    """
    # 讀取數(shù)據(jù)
    data = pd.read_csv('../../數(shù)據(jù)集/機(jī)器學(xué)習(xí)/分類算法/facebook-v-predicting-check-ins/train.csv')
    # print(data.count(axis=0))

    # 數(shù)據(jù)預(yù)處理
    #1塔拳、縮小數(shù)據(jù)范圍
    data = data.query(" x > 0.25 & x < 1.25 & y > 2.5 &y < 2.75")
    # print(data.count(axis=0))
    #2、處理時間數(shù)據(jù)
    data['time'] = pd.to_datetime(data['time'], unit='s')
    #把日期格式轉(zhuǎn)換成  字典格式
    time_value = pd.DatetimeIndex(data['time'])
    # print(time_value)
    #3峡竣、增加分割的日期數(shù)據(jù)
    data['day'] = time_value.day
    data['hour']= time_value.hour
    data['weekday'] = time_value.weekday
    # print(data.head())

    #4、刪除沒用的數(shù)據(jù)
    data = data.drop(['time'],axis=1)
    # print(data.head())
    #5量九、將簽到位置少于n個用戶的刪除
    place_count = data.groupby('place_id').count()
    # print(place_count)
    tf = place_count[place_count.row_id > 3].reset_index()
    data = data[data['place_id'].isin(tf.place_id)]
    # print(data)
    #
    #取出數(shù)據(jù)當(dāng)中的特征值和目標(biāo)值
    y=data['place_id']
    x=data.drop(['place_id'],axis=1)
    x=data.drop(['row_id'],axis=1)
    #
    #進(jìn)行數(shù)據(jù)集的分割
    x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.25)
if __name__ == "__main__":
    knn()

這里沒有把分割后的測試集與訓(xùn)練集展示出來适掰,有興趣的同學(xué)可以輸出看一下。
7荠列、特征標(biāo)準(zhǔn)化
特征歸一化與特征標(biāo)準(zhǔn)化是特征預(yù)處理的重要方法类浪,目的是降低某特征值過大對結(jié)果的影響,特征標(biāo)準(zhǔn)化是KNN算法最常用的特征處理方法肌似,它將原始特征值處理為服從均值為0费就,方差為1的正態(tài)分布。

  • 標(biāo)準(zhǔn)化公式:
    x' = \frac{x - mean}{\sigma} ,這里mean是指每一列的平均值川队,\sigma是標(biāo)準(zhǔn)差
    var = \frac{(x_{1}-mean)^2+(x_{2}-mean)^2+...+(x_{n}-mean)^2}{n}
    \sigma = \sqrt{var}
    關(guān)于特征標(biāo)準(zhǔn)化力细,sklearn中封裝有標(biāo)準(zhǔn)化的API,scikit-learn.preprocessing.StandardScaler,我們直接調(diào)用就可以固额,
def knn():
    """
    knn預(yù)測分類
    :return:
    """
    # 讀取數(shù)據(jù)
    data = pd.read_csv('../../數(shù)據(jù)集/機(jī)器學(xué)習(xí)/分類算法/facebook-v-predicting-check-ins/train.csv')
    # print(data.count(axis=0))

    # 數(shù)據(jù)預(yù)處理
    #1眠蚂、縮小數(shù)據(jù)范圍
    data = data.query(" x > 0.25 & x < 1.25 & y > 2.5 &y < 2.75")
    # print(data.count(axis=0))
    #2、處理時間數(shù)據(jù)
    data['time'] = pd.to_datetime(data['time'], unit='s')
    #把日期格式轉(zhuǎn)換成  字典格式
    time_value = pd.DatetimeIndex(data['time'])
    # print(time_value)
    #3斗躏、增加分割的日期數(shù)據(jù)
    data['day'] = time_value.day
    data['hour']= time_value.hour
    data['weekday'] = time_value.weekday
    # print(data.head())

    #4逝慧、刪除沒用的數(shù)據(jù)
    data = data.drop(['time'],axis=1)
    # print(data.head())
    #5、將簽到位置少于n個用戶的刪除
    place_count = data.groupby('place_id').count()
    # print(place_count)
    tf = place_count[place_count.row_id > 3].reset_index()
    data = data[data['place_id'].isin(tf.place_id)]
    # print(data)
    #
    #取出數(shù)據(jù)當(dāng)中的特征值和目標(biāo)值
    y=data['place_id']
    x=data.drop(['place_id'],axis=1)
    x=data.drop(['row_id'],axis=1)
    #
    #進(jìn)行數(shù)據(jù)集的分割
    x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.25)

    # 特征工程(標(biāo)準(zhǔn)化)
    std=StandardScaler()

    # 對測試集和訓(xùn)練集的特征值進(jìn)行標(biāo)準(zhǔn)化
    x_train = std.fit_transform(x_train)
    x_test = std.fit_transform(x_test)
    print(x_train)
    print(x_test)
if __name__ == "__main__":
    knn()


8啄糙、進(jìn)行算法流程笛臣,得出預(yù)測結(jié)果和準(zhǔn)確率
KNN算法在sklearn提供的sklearn.neighbors.KNeighborsClassifier(n_neighbors=5,algorithm='auto')中,

參數(shù) 說明
n_neighbors隧饼,int,可選(默認(rèn)=5) k_neighbors查詢默認(rèn)使用的鄰居數(shù)
algorithm沈堡,{'auto','ball_tree','kd_tree','brute'} 'ball_tree'將會使用BallTree,'kd_tree'將使用KDTree.'auto'將嘗試根據(jù)傳遞給fit方法的值來決定最適合的算法。(不同實(shí)現(xiàn)方式影響效率 )

我們來看一下測試結(jié)果桑李,

def knn():
    """
    knn預(yù)測分類
    :return:
    """
    # 讀取數(shù)據(jù)
    data = pd.read_csv('../../數(shù)據(jù)集/機(jī)器學(xué)習(xí)/分類算法/facebook-v-predicting-check-ins/train.csv')
    # print(data.count(axis=0))

    # 數(shù)據(jù)預(yù)處理
    #1踱蛀、縮小數(shù)據(jù)范圍
    data = data.query(" x > 0.25 & x < 1.25 & y > 2.5 &y < 2.75")
    # print(data.count(axis=0))
    #2窿给、處理時間數(shù)據(jù)
    data['time'] = pd.to_datetime(data['time'], unit='s')
    #把日期格式轉(zhuǎn)換成  字典格式
    time_value = pd.DatetimeIndex(data['time'])
    # print(time_value)
    #3、增加分割的日期數(shù)據(jù)
    data['day'] = time_value.day
    data['hour']= time_value.hour
    data['weekday'] = time_value.weekday
    # print(data.head())

    #4率拒、刪除沒用的數(shù)據(jù)
    data = data.drop(['time'],axis=1)
    # print(data.head())
    #5崩泡、將簽到位置少于n個用戶的刪除
    place_count = data.groupby('place_id').count()
    # print(place_count)
    tf = place_count[place_count.row_id > 3].reset_index()
    data = data[data['place_id'].isin(tf.place_id)]
    # print(data)
    #
    #取出數(shù)據(jù)當(dāng)中的特征值和目標(biāo)值
    y=data['place_id']
    x=data.drop(['place_id'],axis=1)
    x=data.drop(['row_id'],axis=1)
    #
    #進(jìn)行數(shù)據(jù)集的分割
    x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.25)

    # 特征工程(標(biāo)準(zhǔn)化)
    std=StandardScaler()

    # 對測試集和訓(xùn)練集的特征值進(jìn)行標(biāo)準(zhǔn)化
    x_train = std.fit_transform(x_train)
    x_test = std.fit_transform(x_test)


    # 進(jìn)行算法流程
    knn = KNeighborsClassifier(n_neighbors=7)

    #fit predict  score
    knn.fit(x_train,y_train)

    #得出預(yù)測結(jié)果
    y_predict = knn.predict(x_test)
    print("預(yù)測的目標(biāo)簽到位置為:",y_predict)

    #得出準(zhǔn)確率
    print("預(yù)測的準(zhǔn)確率:",knn.score(x_test,y_test))
    return None

if __name__ == "__main__":
    knn()

預(yù)測結(jié)果是62.8%,結(jié)果并不是很理想猬膨,還有很多可以改進(jìn)的地方角撞,比如數(shù)據(jù)集的篩選可以重新設(shè)置一下,n_neighbors可以設(shè)置為5...等等勃痴,大家可以下去試一下谒所。KNN算法的實(shí)例分析到這里就結(jié)束了,最后我們總結(jié)一下KNN算法的優(yōu)缺點(diǎn)沛申。

四劣领、算法總結(jié)

1、優(yōu)點(diǎn)

  • 簡單铁材,易于理解尖淘,易于實(shí)現(xiàn),無需估計(jì)參數(shù)著觉,無需訓(xùn)練
  • 適合對稀有事件進(jìn)行分類
  • 特別適合于多分類(對象具有多個類別標(biāo)簽)村生,KNN比SVM的表現(xiàn)要好。
  • 可用于數(shù)值型數(shù)據(jù)和離散型數(shù)據(jù)
  • 對異常值不敏感
    2饼丘、缺點(diǎn)
  • 計(jì)算復(fù)雜性高趁桃,空間復(fù)雜性高
  • 樣本不平衡問題(即有些類別的樣本數(shù)量很多,而其它樣本的數(shù)量很少)
  • 一般數(shù)值很大的時候不用這個肄鸽,計(jì)算量太大卫病;但是單個樣本又不能太少,否則容易發(fā)生誤分
  • 最大的缺點(diǎn)是無法給出數(shù)據(jù)的內(nèi)在含義
    KNN算法到這里就講完了贴捡,文中的第一個案例是參考的非常優(yōu)秀的博主Jack-Cui : http://blog.csdn.net/c406495762的博客忽肛,有興趣的同學(xué)可以看一下。
    接下來的這段時間烂斋,主要是更新機(jī)器學(xué)習(xí)的算法屹逛,希望有興趣的同學(xué)可以關(guān)注評論,一起學(xué)習(xí)汛骂。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末罕模,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子帘瞭,更是在濱河造成了極大的恐慌淑掌,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蝶念,死亡現(xiàn)場離奇詭異抛腕,居然都是意外死亡芋绸,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進(jìn)店門担敌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來摔敛,“玉大人,你說我怎么就攤上這事全封÷黻迹” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵刹悴,是天一觀的道長行楞。 經(jīng)常有香客問我,道長土匀,這世上最難降的妖魔是什么子房? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮就轧,結(jié)果婚禮上池颈,老公的妹妹穿的比我還像新娘。我一直安慰自己钓丰,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布每币。 她就那樣靜靜地躺著携丁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪兰怠。 梳的紋絲不亂的頭發(fā)上梦鉴,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天,我揣著相機(jī)與錄音揭保,去河邊找鬼肥橙。 笑死,一個胖子當(dāng)著我的面吹牛秸侣,可吹牛的內(nèi)容都是我干的存筏。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼味榛,長吁一口氣:“原來是場噩夢啊……” “哼椭坚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起搏色,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤善茎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后频轿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體垂涯,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡烁焙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了耕赘。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片骄蝇。...
    茶點(diǎn)故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖鞠苟,靈堂內(nèi)的尸體忽然破棺而出乞榨,到底是詐尸還是另有隱情,我是刑警寧澤当娱,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布吃既,位于F島的核電站,受9級特大地震影響跨细,放射性物質(zhì)發(fā)生泄漏鹦倚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一冀惭、第九天 我趴在偏房一處隱蔽的房頂上張望震叙。 院中可真熱鬧,春花似錦散休、人聲如沸媒楼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽划址。三九已至,卻和暖如春限府,著一層夾襖步出監(jiān)牢的瞬間夺颤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工胁勺, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留世澜,地道東北人。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓署穗,卻偏偏與公主長得像寥裂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子案疲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評論 2 350