在《從零開始學Python【30】--DBSCAN聚類(理論部分)》一文中我們側重介紹了有關密度聚類的理論知識,涉及的內容包含密度聚類中的一些重要概念(如核心對象、直接密度可達、密度相連等)和密度聚類的具體步驟。在本次文章中,我們將通過一個小的數據案例,講解如何基於Python實現密度聚類的實戰。
函數說明在Python的sklearn模塊中,cluster子模塊集成了常用的聚類算法,如K均值聚類、密度聚類和層次聚類等。對於密度聚類而言,讀者可以直接調用cluster子模塊中的DBSCAN「類」,有關該「類」的語法和參數含義如下:
cluster.DBSCAN(eps=0.5, min_samples=5, metric='euclidean', metric_params=None, algorithm='auto', leaf_size=30, p=None, n_jobs=1)eps:用於設置密度聚類中的ε領域,即半徑,默認為0.5;min_samples:用於設置ε領域內最少的樣本量,默認為5;metric:用於指定計算點之間距離的方法,默認為歐氏距離;metric_params:用於指定metric所對應的其他參數值;algorithm:在計算點之間距離的過程中,用於指點搜尋最近鄰樣本點的算法;默認為'auto',表示密度聚類會自動選擇一個合適的搜尋方法;如果為'ball_tree',則表示使用球樹搜尋最近鄰;如果為'kd_tree',則表示使用K-D樹搜尋最近鄰;如果為'brute',則表示使用暴力法搜尋最近鄰;leaf_size:當參數algorithm為'ball_tree'或'kd_tree'時,用於指定樹的葉子節點中所包含的最多樣本量,默認為30;該參數會影響搜尋樹的構建和搜尋最近鄰的速度;p:當參數metric為閔可夫斯基距離時('minkowski'),p=1,表示計算點之間的曼哈頓距離;p=2,表示計算點之間的歐氏距離;該參數的默認值為2;n_jobs:用於設置密度聚類算法並行計算所需的CPU數量,默認為1表示僅使用1個CPU運行算法,即不使用並行運算功能;
需要說明的是,在DBSCAN「類」中,參數eps和min_samples需要同時調參,即通常會指定幾個候選值,並從候選值中挑選出合理的閾值;在參數eps固定的情況下,如果參數min_samples越大,所形成的核心對象就越少,往往會誤判出許多異常點,聚成的簇數目也會增加,反之,會產生大量的核心對象,導致聚成的簇數目減少;在參數min_samples固定的情況下,參數eps越大,就會導致更多的點落入到ε領域內,進而使核心對象增多,最終使聚成的簇數目減少,反之,會導致核心對象大量減少,最終聚成的簇數目增多。在參數eps和min_samples不合理的情況下,簇數目的增加或減少往往都是錯誤的,例如應該聚為一類的樣本由於簇數目的增加而聚為多類;不該聚為一類的樣本由於簇數目的減少而聚為一類。
算法實戰在密度聚類算法的實戰部分,我們將使用國內31個省份的人口出生率和死亡率數據作為分析對象。首先,將數據讀入到Python中,並繪製出生率和死亡率數據的散點圖,代碼如下:
Province = pd.read_excel(r'C:\Users\Administrator\Desktop\Province.xlsx')Province.head()
plt.scatter(Province.Birth_Rate, Province.Death_Rate)
plt.xlabel('Birth_Rate')plt.ylabel('Death_Rate')
plt.show()
上圖所示,31個點分別代表了各省份人口的出生率和死亡率,通過肉眼,就能夠快速地發現三個簇,即圖中的虛線框,其他不在圈內的點可能就是異常點了。接下來利用密度聚類對該數據集進行驗證,代碼如下:
from sklearn import preprocessing
from sklearn import cluster
import numpy as np
predictors = ['Birth_Rate','Death_Rate']
X = preprocessing.scale(Province[predictors])X = pd.DataFrame(X)
res = []
for eps in np.arange(0.001,1,0.05):
for min_samples in range(2,10): dbscan = cluster.DBSCAN(eps = eps, min_samples = min_samples)
dbscan.fit(X)
n_clusters = len([i for i in set(dbscan.labels_) if i != -1])
outliners = np.sum(np.where(dbscan.labels_ == -1, 1,0))
stats = str(pd.Series([i for i in dbscan.labels_ if i != -1]).value_counts().values) res.append({'eps':eps,'min_samples':min_samples,'n_clusters':n_clusters,'outliners':outliners,'stats':stats})
df = pd.DataFrame(res)
df.loc[df.n_clusters == 3, :]
如上表所示,如果需要將數據聚為3類,則得到如上幾種參數組合,這裡不妨選擇eps為0.801,min_samples為3的參數值(因為該參數組合下的異常點個數比較合理)。接下來,利用如上所得的參數組合,構造密度聚類模型,實現原始數據集的聚類,代碼如下:
dbscan = cluster.DBSCAN(eps = 0.801, min_samples = 3)
dbscan.fit(X)Province['dbscan_label'] = dbscan.labels_
sns.lmplot(x = 'Birth_Rate', y = 'Death_Rate', hue = 'dbscan_label', data = Province, markers = ['*','d','^','o'], fit_reg = False, legend = False)
for x,y,text in zip(Province.Birth_Rate,Province.Death_Rate, Province.Province): plt.text(x+0.1,y-0.1,text, size = 8)
plt.hlines(y = 5.8, xmin = Province.Birth_Rate.min(), xmax = Province.Birth_Rate.max(), linestyles = '--', colors = 'red')plt.vlines(x = 10, ymin = Province.Death_Rate.min(), ymax = Province.Death_Rate.max(), linestyles = '--', colors = 'red')
plt.xlabel('Birth_Rate')plt.ylabel('Death_Rate')
plt.show()
如上圖所示,三角形、菱形和圓形所代表的點即為三個不同的簇,五角星所代表的點即為異常點,這個聚類效果還是非常不錯的,對比建模之前的結論非常吻合。從上圖可知,以北京、天津、上海為代表的省份,屬於低出生率和低死亡率類型;廣東、寧夏和新疆三個省份屬於高出生率和低死亡率類型;江蘇、四川、湖北為代表的省份屬於高出生率和高死亡率類型。四個異常點中,黑龍江與遼寧比較相似,屬於低出生率和高死亡率類型;山東省屬於極高出生率和高死亡率的省份;西藏屬於高出生率和低死亡率的省份,但它與廣東、寧夏和新疆更為相似。
OK,關於使用Python完成密度聚類的實戰我們就分享到這裡,在我的新書《從零開始學Python數據分析與挖掘》中,對密度聚類算法的落地也作了更多的講解。如果你有任何問題,歡迎在公眾號的留言區域表達你的疑問。同時,也歡迎各位朋友繼續轉發與分享文中的內容,讓更多的人學習和進步。
數據分析1480
學習與分享,取長補短,關注小號!