KNN除了可以做分類和預測,你還知道它可以識別異常值嗎?

 

前言

首先跟各位讀者朋友道個歉,這篇文章來的較晚,距離上一篇有關數據分析中異常值的判斷已超過3個月。在《Python數據清洗--異常值識別與處理01》文中,介紹了兩種單變量的異常識別方法,分別是分位數法(即藉助於箱線圖的策略)和Sigma法(即藉助於正態分佈的假設)。

然而這兩種方法,並不能從全局的角度識別出數據中可能存在的異常點。爲解決這個問題,本文將藉助於KNN模型的思想,從多變量的角度,判斷全局數據中的異常點。本文中所涉及的代碼和數據源均可從文末的鏈接中下載。

 

KNN算法介紹

KNN模型屬於有監督的學習算法,它的中文名稱爲K最近鄰算法,該模型是通過搜尋最近的k個已知類別樣本對未知類別樣本進行預判,當然也可以對連續的Y變量做預測。關於“最近”的度量就是應用點之間的距離(如計算歐氏距離、曼哈頓距離、餘弦相似度等),如果距離越小,說明它們之間越近。爲了使讀者能夠理解KNN模型的思想,簡單繪製瞭如下的示意圖。

如上圖所示,假設數據集中一共含有兩種類別,分別用五角星和三角形表示,待預測樣本爲各圓的圓心。如果以近鄰個數k=5爲例,就可以通過投票方式快速得到未知樣本所屬的類別。該算法的背後是如何實現上面分類的呢?它的具體步驟可以描述爲:

  • 確定未知樣本近鄰的個數k值。

  • 根據某種度量樣本間相似度的指標(如歐氏距離)將每一個未知類別樣本的最近k個已知樣本搜尋出來,形成一個個簇。

  • 對搜尋出來的已知樣本進行投票,將各簇下類別最多的分類用作未知樣本點的預測。

 

異常點識別原理

異常點是指遠離大部分正常點的樣本點,再直白點說,異常點一定是跟大部分的樣本點都隔得很遠。基於這個思想,我們只需要依次計算每個樣本點與它最近的K個樣本的平均距離。再利用計算的距離與閾值進行比較,如果大於閾值,則認爲是異常點。同樣,爲了幫助讀者理解如何利用KNN思想,實現異常值的識別,我手工畫了一張圖。

如上圖所示,一共包含16個樣本點,每一個樣本點都可以跟剩餘的15個樣本點算歐式距離,再從15個距離中找出最小的K個距離,並計算平均距離,用於衡量該樣本點與其它樣本的相似度。

不妨以最近的5個近鄰爲例,目測圖中的五角星應該就是異常點,因爲它到最近5個樣本點的平均距離,一定超過其他點的最近5個鄰居的平均距離。

理論、思想說了那麼多,下面我們就直接開幹,爲了方便後續的分析和可視化,我們選取了中國統計局公佈的2018年各省常住人口量與GDP數據。希望從該數據中,尋找到可能存在異常點。

 

案例實戰

首先,基於該數據,繪製各省常住人口量與GDP的散點圖,讓大家對數據有一個直觀的認識。

# 導入後文即將用到的第三方模塊
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import scale

# 導入數據
datas = pd.read_excel('economics.xlsx')
# 繪製2018年我國各省人口量與GDP之間的散點圖
plt.scatter(datas.Population, datas.GDP)
plt.xlabel('Population')
plt.ylabel('GDP')
plt.show()

如上圖所示,直覺上圖中右上角的三個點可能是異常點,因爲它們與大部分的數據點距離都比較遠。爲了驗證我們的直覺,接下來通過構造自定義函數,計算每個點與剩餘點的距離,並基於最近5個樣本點算平均距離,尋找是否超過閾值的異常點(閾值的計算是《Python數據清洗--異常值識別與處理01》爲中介紹的分位數法)。下方代碼可能有點長,但仔細閱讀並查看對應的註釋內容,相信你一定能夠理解代碼的思想。

​
# 藉助於K近鄰算法,尋找數據中可能存在的異常點
def knn_outliner(data, K):
    # 數據的標準處理
    std_data = scale(data)
    # 重新轉換爲數據框
    std_data = pd.DataFrame(std_data)

    # 構造空列表,用於存儲每個樣本點的K近鄰平均距離
    all_dist = []
    for i in range(std_data.shape[0]):
        # 計算第i個數據樣本點與其他樣本點的距離
        diff_i = np.array(std_data.loc[std_data.index != i, :]) \
                 - np.array(std_data.loc[std_data.index == i, :])
        dist_i = np.sum(np.square(diff_i), axis=1)
        # 從中尋找最近的K個鄰居,並計算近鄰的平均距離
        avg_dist_i = np.mean(np.sort(dist_i)[:K])
        # 記錄每一個樣本點距離其他樣本點的平均距離
        all_dist.append(avg_dist_i)

    # 根據分位數法,尋找判斷異常的閾值
    Q1 = pd.Series(all_dist).quantile(0.25)
    Q3 = pd.Series(all_dist).quantile(0.75)
    thread = Q3 + 3 * (Q3 - Q1)
    is_outline = pd.Series(all_dist) > thread

    # 合併數據(原始數據、近鄰的平均距離和是否異常)
    final_res = pd.concat([data, pd.Series(all_dist, name='Dist'), pd.Series(is_outline, name='IsOutline')], axis=1)
    # 返回數據結果
    return final_res

# 調用函數,返回異常檢測的結果
res = knn_outliner(datas[['Population', 'GDP']], K=5)

# 繪圖
sns.lmplot(x="Population", y="GDP", hue='IsOutline', data=res,
           fit_reg=False, legend=False)
plt.legend(loc='best')
plt.show()

​

如上圖所示,基於5個近鄰的KNN思想,尋找到了4個異常點,與之前我們的直覺判斷還是非常吻合的。讀者也可以嘗試其他幾種可能的K值,並對比每一種K值所得到的異常點是否存在較大的差異。

 

KNN的短板

從思想、理論到實戰,大家一定會發現,基於KNN模型尋找異常點,所要經過的運算次數還是非常多的(例如針對100個樣本點尋找可能的異常,需迭代計算100×99次的運算)。所以,基於KNN模型尋找異常點是不適合於高維數據和大批量數據的;而且距離的計算採用的是歐式距離公式,只能針對球形簇的樣本數據尋找異常,對於非球形簇則無法很好的搜尋異常。

 

結語

OK,今天的內容就分享到這裏,下一期將會跟大家分享如何基於K均值模型,針對大批量數據做異常點檢測。如果你有任何問題,歡迎在公衆號的留言區域表達你的疑問。

數據鏈接:https://pan.baidu.com/s/1G7t85yTS0rLduwbYWZPunw

提取碼:675v

 

推薦閱讀--Top5

Python要上天啊!一行代碼就可以搞定炫酷的數據可視化!

這100多個數據分析常用指標和術語你都分清楚了嗎?

while循環與for循環到底差在哪裏?舉幾個例子給你看!

學習Python,避開這17個低級錯誤,養成良好的編程習慣!

超全Python速查表,GitHub標星4600

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章