'手把手教你如何利用K均值聚類實現異常值的識別'

算法 Python 可視化技術 千鋒python學院 2019-09-03
"

首先,藉助於Python隨機生成兩組二維數據,用於後文的實戰。為了能夠更加直觀地洞察該數據,我們將其繪製成散點圖。

# 導入第三方包
import numpy as np
import matplotlib.pyplot as plt
# 隨機生成兩組二元正態分佈隨機數
np.random.seed(1234)
mean1 = [0.5, 0.5]
cov1 = [[0.3, 0], [0, 0.1]]
x1, y1 = np.random.multivariate_normal(mean1, cov1, 5000).T
mean2 = [0, 8]
cov2 = [[0.8, 0], [0, 2]]
x2, y2 = np.random.multivariate_normal(mean2, cov2, 5000).T
# 繪製兩組數據的散點圖
plt.rcParams['axes.unicode_minus'] = False
plt.scatter(x1, y1)
plt.scatter(x2, y2)
# 顯示圖形
plt.show()


"

首先,藉助於Python隨機生成兩組二維數據,用於後文的實戰。為了能夠更加直觀地洞察該數據,我們將其繪製成散點圖。

# 導入第三方包
import numpy as np
import matplotlib.pyplot as plt
# 隨機生成兩組二元正態分佈隨機數
np.random.seed(1234)
mean1 = [0.5, 0.5]
cov1 = [[0.3, 0], [0, 0.1]]
x1, y1 = np.random.multivariate_normal(mean1, cov1, 5000).T
mean2 = [0, 8]
cov2 = [[0.8, 0], [0, 2]]
x2, y2 = np.random.multivariate_normal(mean2, cov2, 5000).T
# 繪製兩組數據的散點圖
plt.rcParams['axes.unicode_minus'] = False
plt.scatter(x1, y1)
plt.scatter(x2, y2)
# 顯示圖形
plt.show()


手把手教你如何利用K均值聚類實現異常值的識別

如上圖所示,圖中藍色和紅色之間形成鮮明的簇,其中每個簇內包含5000個數據。如果數據中存在異常點,目測藍色的簇可能會包含更多異常,因為數據點相對分散一些。

K均值聚類的介紹

K均值聚類算法的思路非常通俗易懂,就是不斷地計算各樣本點與簇中心之間的距離,直到收斂為止,其具體的步驟如下:

(1)從數據中隨機挑選k個樣本點作為原始的簇中心。

(2)計算剩餘樣本與簇中心的距離,並把各樣本標記為離k個簇中心最近的類別。

(3)重新計算各簇中樣本點的均值,並以均值作為新的k個簇中心。

(4)不斷重複(2)和(3),直到簇中心的變化趨於穩定,形成最終的k個簇。

也許上面的4個步驟還不足以讓讀者明白Kmeans的執行過程,可以結合下圖更進一步地理解其背後的思想。


"

首先,藉助於Python隨機生成兩組二維數據,用於後文的實戰。為了能夠更加直觀地洞察該數據,我們將其繪製成散點圖。

# 導入第三方包
import numpy as np
import matplotlib.pyplot as plt
# 隨機生成兩組二元正態分佈隨機數
np.random.seed(1234)
mean1 = [0.5, 0.5]
cov1 = [[0.3, 0], [0, 0.1]]
x1, y1 = np.random.multivariate_normal(mean1, cov1, 5000).T
mean2 = [0, 8]
cov2 = [[0.8, 0], [0, 2]]
x2, y2 = np.random.multivariate_normal(mean2, cov2, 5000).T
# 繪製兩組數據的散點圖
plt.rcParams['axes.unicode_minus'] = False
plt.scatter(x1, y1)
plt.scatter(x2, y2)
# 顯示圖形
plt.show()


手把手教你如何利用K均值聚類實現異常值的識別

如上圖所示,圖中藍色和紅色之間形成鮮明的簇,其中每個簇內包含5000個數據。如果數據中存在異常點,目測藍色的簇可能會包含更多異常,因為數據點相對分散一些。

K均值聚類的介紹

K均值聚類算法的思路非常通俗易懂,就是不斷地計算各樣本點與簇中心之間的距離,直到收斂為止,其具體的步驟如下:

(1)從數據中隨機挑選k個樣本點作為原始的簇中心。

(2)計算剩餘樣本與簇中心的距離,並把各樣本標記為離k個簇中心最近的類別。

(3)重新計算各簇中樣本點的均值,並以均值作為新的k個簇中心。

(4)不斷重複(2)和(3),直到簇中心的變化趨於穩定,形成最終的k個簇。

也許上面的4個步驟還不足以讓讀者明白Kmeans的執行過程,可以結合下圖更進一步地理解其背後的思想。


手把手教你如何利用K均值聚類實現異常值的識別


如上圖所示,通過9個子圖對Kmeans聚類過程加以說明:子圖1,從原始樣本中隨機挑選兩個數據點作為初始的簇中心,即子圖中的兩個五角星;子圖2,將其餘樣本點與這兩個五角星分別計算距離(距離的度量可選擇歐氏距離、曼哈頓距離等),然後將每個樣本點劃分到離五角星最近的簇,即子圖中按虛線隔開的兩部分;子圖3,計算兩個簇內樣本點的均值,得到新的簇中心,即子圖中的五角星;子圖4,根據新的簇中心,繼續計算各樣本與五角星之間的距離,得到子圖5的劃分結果和子圖6中新的簇內樣本均值;以此類推,最終得到理想的聚類效果,如子圖9所示,圖中的五角星即最終的簇中心點。

在上文中,我們生成了兩組隨機數據,從圖中一眼就可以看出需聚為兩類,然而在實際應用中,很多數據都無法通過可視化或直覺判斷聚類的個數(即K值)。但這不代表沒有方法鎖定最佳的K值,在書《從零開始學Python數據分析與挖掘》的第十五章介紹了“拐點法”、“輪廓係數法”和“間隔統計量法”,感興趣的朋友可以去了解一下。這裡就使用書中的自定義函數,測試一下K應該對應的值:


# 將兩組數據集彙總到數據框中
X = pd.DataFrame(np.concatenate([np.array([x1, y1]), np.array([x2, y2])], axis=1).T)
X.rename(columns = {0:'x1',1:'x2'}, inplace = True)
# 自定義函數的調用
k_SSE(X, 10)


"

首先,藉助於Python隨機生成兩組二維數據,用於後文的實戰。為了能夠更加直觀地洞察該數據,我們將其繪製成散點圖。

# 導入第三方包
import numpy as np
import matplotlib.pyplot as plt
# 隨機生成兩組二元正態分佈隨機數
np.random.seed(1234)
mean1 = [0.5, 0.5]
cov1 = [[0.3, 0], [0, 0.1]]
x1, y1 = np.random.multivariate_normal(mean1, cov1, 5000).T
mean2 = [0, 8]
cov2 = [[0.8, 0], [0, 2]]
x2, y2 = np.random.multivariate_normal(mean2, cov2, 5000).T
# 繪製兩組數據的散點圖
plt.rcParams['axes.unicode_minus'] = False
plt.scatter(x1, y1)
plt.scatter(x2, y2)
# 顯示圖形
plt.show()


手把手教你如何利用K均值聚類實現異常值的識別

如上圖所示,圖中藍色和紅色之間形成鮮明的簇,其中每個簇內包含5000個數據。如果數據中存在異常點,目測藍色的簇可能會包含更多異常,因為數據點相對分散一些。

K均值聚類的介紹

K均值聚類算法的思路非常通俗易懂,就是不斷地計算各樣本點與簇中心之間的距離,直到收斂為止,其具體的步驟如下:

(1)從數據中隨機挑選k個樣本點作為原始的簇中心。

(2)計算剩餘樣本與簇中心的距離,並把各樣本標記為離k個簇中心最近的類別。

(3)重新計算各簇中樣本點的均值,並以均值作為新的k個簇中心。

(4)不斷重複(2)和(3),直到簇中心的變化趨於穩定,形成最終的k個簇。

也許上面的4個步驟還不足以讓讀者明白Kmeans的執行過程,可以結合下圖更進一步地理解其背後的思想。


手把手教你如何利用K均值聚類實現異常值的識別


如上圖所示,通過9個子圖對Kmeans聚類過程加以說明:子圖1,從原始樣本中隨機挑選兩個數據點作為初始的簇中心,即子圖中的兩個五角星;子圖2,將其餘樣本點與這兩個五角星分別計算距離(距離的度量可選擇歐氏距離、曼哈頓距離等),然後將每個樣本點劃分到離五角星最近的簇,即子圖中按虛線隔開的兩部分;子圖3,計算兩個簇內樣本點的均值,得到新的簇中心,即子圖中的五角星;子圖4,根據新的簇中心,繼續計算各樣本與五角星之間的距離,得到子圖5的劃分結果和子圖6中新的簇內樣本均值;以此類推,最終得到理想的聚類效果,如子圖9所示,圖中的五角星即最終的簇中心點。

在上文中,我們生成了兩組隨機數據,從圖中一眼就可以看出需聚為兩類,然而在實際應用中,很多數據都無法通過可視化或直覺判斷聚類的個數(即K值)。但這不代表沒有方法鎖定最佳的K值,在書《從零開始學Python數據分析與挖掘》的第十五章介紹了“拐點法”、“輪廓係數法”和“間隔統計量法”,感興趣的朋友可以去了解一下。這裡就使用書中的自定義函數,測試一下K應該對應的值:


# 將兩組數據集彙總到數據框中
X = pd.DataFrame(np.concatenate([np.array([x1, y1]), np.array([x2, y2])], axis=1).T)
X.rename(columns = {0:'x1',1:'x2'}, inplace = True)
# 自定義函數的調用
k_SSE(X, 10)


手把手教你如何利用K均值聚類實現異常值的識別

如上圖所示,當簇的個數為2時形成了一個明顯的“拐點”,因為 K值從1到2時,折線的斜率都比較大,但是值為3時斜率突然就降低了很多,並且之後的簇對應的斜率都變動很小。所以,合理的值應該為2,與模擬的兩個簇數據相吻合。

異常點識別原理

使用K均值聚類的思想識別數據中的異常點還是非常簡單的,具體步驟如下:

  • 利用“拐點法”、“輪廓係數法”、“間隔統計量法”或者“經驗法”確定聚類的個數;
  • 基於具體的K值,對數據實施K均值聚類的應用;
  • 基於聚類的結果,計算簇內每個點到簇中心的距離;
  • 將距離跟閾值相比較,如果其大於閾值則認為是異常,否則正常;


案例實戰

為了驗證我們在前文所說的的直覺(“目測藍色的簇可能會包含更多異常”),接下來通過構造自定義函數,計算簇內的每個點與簇中心的距離,並判斷其是否超過閾值的異常點下方代碼可能有點長,但仔細閱讀並查看對應的註釋內容,相信你一定能夠理解代碼的思想。


def kmeans_outliers(data, clusters, is_scale = True):
# 指定聚類個數,準備進行數據聚類
kmeans = KMeans(n_clusters=clusters)
# 用於存儲聚類相關的結果
cluster_res = []
# 判斷是否需要對數據做標準化處理
if is_scale:
std_data = scale(data) # 標準化
kmeans.fit(std_data) # 聚類擬合
# 返回簇標籤
labels = kmeans.labels_
# 返回簇中心
centers = kmeans.cluster_centers_
for label in set(labels):
# 計算簇內樣本點與簇中心的距離
diff = std_data[np.array(labels) == label,] - \\
- np.array(centers[label])
dist = np.sum(np.square(diff), axis=1)
# 計算判斷異常的閾值
UL = dist.mean() + 3*dist.std()
# 識別異常值,1表示異常,0表示正常
OutLine = np.where(dist > UL, 1, 0)
raw_data = data.loc[np.array(labels) == label,]
new_data = pd.DataFrame({'Label':label,'Dist':dist,'OutLier':OutLine})
# 重新修正兩個數據框的行編號
raw_data.index = new_data.index = range(raw_data.shape[0])
# 數據的列合併
cluster_res.append(pd.concat([raw_data,new_data], axis = 1))
else:
kmeans.fit(data) # 聚類擬合
# 返回簇標籤
labels = kmeans.labels_
# 返回簇中心
centers = kmeans.cluster_centers_
for label in set(labels):
# 計算簇內樣本點與簇中心的距離
diff = np.array(data.loc[np.array(labels) == label,]) - \\
- np.array(centers[label])
dist = np.sum(np.square(diff), axis=1)
UL = dist.mean() + 3*dist.std()
OutLine = np.where(dist > UL, 1, 0)
raw_data = data.loc[np.array(labels) == label,]
new_data = pd.DataFrame({'Label':label,'Dist':dist,'OutLier':OutLine})
raw_data.index = new_data.index = range(raw_data.shape[0])
cluster_res.append(pd.concat([raw_data,new_data], axis = 1))
# 返回數據的行合併結果
return pd.concat(cluster_res)
# 調用函數,返回異常檢測的結果
res = kmeans_outliers(X,2,False)
# res
# 繪圖
sns.lmplot(x="x1", y="x2", hue='OutLier', data=res,
fit_reg=False, legend=False)
plt.legend(loc='best')
plt.show()


"

首先,藉助於Python隨機生成兩組二維數據,用於後文的實戰。為了能夠更加直觀地洞察該數據,我們將其繪製成散點圖。

# 導入第三方包
import numpy as np
import matplotlib.pyplot as plt
# 隨機生成兩組二元正態分佈隨機數
np.random.seed(1234)
mean1 = [0.5, 0.5]
cov1 = [[0.3, 0], [0, 0.1]]
x1, y1 = np.random.multivariate_normal(mean1, cov1, 5000).T
mean2 = [0, 8]
cov2 = [[0.8, 0], [0, 2]]
x2, y2 = np.random.multivariate_normal(mean2, cov2, 5000).T
# 繪製兩組數據的散點圖
plt.rcParams['axes.unicode_minus'] = False
plt.scatter(x1, y1)
plt.scatter(x2, y2)
# 顯示圖形
plt.show()


手把手教你如何利用K均值聚類實現異常值的識別

如上圖所示,圖中藍色和紅色之間形成鮮明的簇,其中每個簇內包含5000個數據。如果數據中存在異常點,目測藍色的簇可能會包含更多異常,因為數據點相對分散一些。

K均值聚類的介紹

K均值聚類算法的思路非常通俗易懂,就是不斷地計算各樣本點與簇中心之間的距離,直到收斂為止,其具體的步驟如下:

(1)從數據中隨機挑選k個樣本點作為原始的簇中心。

(2)計算剩餘樣本與簇中心的距離,並把各樣本標記為離k個簇中心最近的類別。

(3)重新計算各簇中樣本點的均值,並以均值作為新的k個簇中心。

(4)不斷重複(2)和(3),直到簇中心的變化趨於穩定,形成最終的k個簇。

也許上面的4個步驟還不足以讓讀者明白Kmeans的執行過程,可以結合下圖更進一步地理解其背後的思想。


手把手教你如何利用K均值聚類實現異常值的識別


如上圖所示,通過9個子圖對Kmeans聚類過程加以說明:子圖1,從原始樣本中隨機挑選兩個數據點作為初始的簇中心,即子圖中的兩個五角星;子圖2,將其餘樣本點與這兩個五角星分別計算距離(距離的度量可選擇歐氏距離、曼哈頓距離等),然後將每個樣本點劃分到離五角星最近的簇,即子圖中按虛線隔開的兩部分;子圖3,計算兩個簇內樣本點的均值,得到新的簇中心,即子圖中的五角星;子圖4,根據新的簇中心,繼續計算各樣本與五角星之間的距離,得到子圖5的劃分結果和子圖6中新的簇內樣本均值;以此類推,最終得到理想的聚類效果,如子圖9所示,圖中的五角星即最終的簇中心點。

在上文中,我們生成了兩組隨機數據,從圖中一眼就可以看出需聚為兩類,然而在實際應用中,很多數據都無法通過可視化或直覺判斷聚類的個數(即K值)。但這不代表沒有方法鎖定最佳的K值,在書《從零開始學Python數據分析與挖掘》的第十五章介紹了“拐點法”、“輪廓係數法”和“間隔統計量法”,感興趣的朋友可以去了解一下。這裡就使用書中的自定義函數,測試一下K應該對應的值:


# 將兩組數據集彙總到數據框中
X = pd.DataFrame(np.concatenate([np.array([x1, y1]), np.array([x2, y2])], axis=1).T)
X.rename(columns = {0:'x1',1:'x2'}, inplace = True)
# 自定義函數的調用
k_SSE(X, 10)


手把手教你如何利用K均值聚類實現異常值的識別

如上圖所示,當簇的個數為2時形成了一個明顯的“拐點”,因為 K值從1到2時,折線的斜率都比較大,但是值為3時斜率突然就降低了很多,並且之後的簇對應的斜率都變動很小。所以,合理的值應該為2,與模擬的兩個簇數據相吻合。

異常點識別原理

使用K均值聚類的思想識別數據中的異常點還是非常簡單的,具體步驟如下:

  • 利用“拐點法”、“輪廓係數法”、“間隔統計量法”或者“經驗法”確定聚類的個數;
  • 基於具體的K值,對數據實施K均值聚類的應用;
  • 基於聚類的結果,計算簇內每個點到簇中心的距離;
  • 將距離跟閾值相比較,如果其大於閾值則認為是異常,否則正常;


案例實戰

為了驗證我們在前文所說的的直覺(“目測藍色的簇可能會包含更多異常”),接下來通過構造自定義函數,計算簇內的每個點與簇中心的距離,並判斷其是否超過閾值的異常點下方代碼可能有點長,但仔細閱讀並查看對應的註釋內容,相信你一定能夠理解代碼的思想。


def kmeans_outliers(data, clusters, is_scale = True):
# 指定聚類個數,準備進行數據聚類
kmeans = KMeans(n_clusters=clusters)
# 用於存儲聚類相關的結果
cluster_res = []
# 判斷是否需要對數據做標準化處理
if is_scale:
std_data = scale(data) # 標準化
kmeans.fit(std_data) # 聚類擬合
# 返回簇標籤
labels = kmeans.labels_
# 返回簇中心
centers = kmeans.cluster_centers_
for label in set(labels):
# 計算簇內樣本點與簇中心的距離
diff = std_data[np.array(labels) == label,] - \\
- np.array(centers[label])
dist = np.sum(np.square(diff), axis=1)
# 計算判斷異常的閾值
UL = dist.mean() + 3*dist.std()
# 識別異常值,1表示異常,0表示正常
OutLine = np.where(dist > UL, 1, 0)
raw_data = data.loc[np.array(labels) == label,]
new_data = pd.DataFrame({'Label':label,'Dist':dist,'OutLier':OutLine})
# 重新修正兩個數據框的行編號
raw_data.index = new_data.index = range(raw_data.shape[0])
# 數據的列合併
cluster_res.append(pd.concat([raw_data,new_data], axis = 1))
else:
kmeans.fit(data) # 聚類擬合
# 返回簇標籤
labels = kmeans.labels_
# 返回簇中心
centers = kmeans.cluster_centers_
for label in set(labels):
# 計算簇內樣本點與簇中心的距離
diff = np.array(data.loc[np.array(labels) == label,]) - \\
- np.array(centers[label])
dist = np.sum(np.square(diff), axis=1)
UL = dist.mean() + 3*dist.std()
OutLine = np.where(dist > UL, 1, 0)
raw_data = data.loc[np.array(labels) == label,]
new_data = pd.DataFrame({'Label':label,'Dist':dist,'OutLier':OutLine})
raw_data.index = new_data.index = range(raw_data.shape[0])
cluster_res.append(pd.concat([raw_data,new_data], axis = 1))
# 返回數據的行合併結果
return pd.concat(cluster_res)
# 調用函數,返回異常檢測的結果
res = kmeans_outliers(X,2,False)
# res
# 繪圖
sns.lmplot(x="x1", y="x2", hue='OutLier', data=res,
fit_reg=False, legend=False)
plt.legend(loc='best')
plt.show()


手把手教你如何利用K均值聚類實現異常值的識別


如上圖所示,藍色的點即為異常點。從藍色點的分佈來看,上面那一簇所對應的異常點比較多(與之前的預判一致),而下面簇的異常點較少,且全部集中在散點的右側。

"

相關推薦

推薦中...