常見聚類(K-means、DSCAN)算法及實現

K-means

K-means

K-means算法優點
1).是解決聚類問題的一種經典算法,原理簡單,實現容易。
2).當簇接近高斯分佈時,它的效果較好。
3).與密度聚類中的DSCAN相比,簇與簇之間劃分清晰。
4). 可作爲其他聚類方法的基礎算法,如譜聚類。

K-means算法缺點
1).在簇的平均值可被定義的情況下才能使用,可能不適用於某些應用。
2).在K-means算法中K是事先給定的,這個K值的選定是非常難以估計的。很多時候,事先並不知道給定的數據集應該分成多少個類別才最合適。
3).在 K-means 算法中,首先需要根據初始聚類中心來確定一個初始劃分,然後對初始劃分進行優化。這個初始聚類中心的選擇對聚類結果有較大的影響,一旦初始值選擇的不好,可能無法得到有效的聚類結果。
4).該算法需要不斷地進行樣本分類調整,不斷地計算調整後的新的聚類中心,因此當數據量非常大時,算法的時間開銷是非常大的。
5).若簇中含有異常點,將導致均值偏離嚴重(即:對噪聲和孤立點數據敏感)。
6).不適用於發現非凸形狀的簇或者大小差別很大的簇。
7). 結果不一定是全局最優,只能保證局部最優
8). 算法時間複雜度比較高 O(nkt)。

K-means算法缺點的改進
1).針對上述缺點2),通過類的自動合併和分裂,得到較爲合理的類型數目K,例如 ISODATA 算法。
2).針對上述缺點3),可選用二分K-均值聚類;或者多設置一些不同的初值,對比最後的運算結果,一直到結果趨於穩定結束。
3).針對上述缺點5),改成求點的中位數,這種聚類方式即K-Mediods聚類(K中值)。

k值如何確定

K-mediods(K中心點)算法

K-mediods算法是對K-means算法的一種改進算法。
K-mediods算法優點
1).優點與K-means算法相同。
2).與K-means相比,K-mediods算法對於噪聲不那麼敏感,離羣點就不會造成劃分結果的偏差過大。

K-mediods算法缺點
1).有K-means同樣的缺點。如:必須事先確定類簇數和中心點,簇數和中心點的選擇對結果影響很大;一般在獲得一個局部最優的解後就停止;對於除數值型以外的數據不適用;只適用於聚類結果爲凸形的數據集等。
2) K-mediods是對K-means的改進,但由於按照中心點選擇的方式進行計算,算法的時間複雜度也比K-means上升了O(n)。

K-mediods算法描述
1). 首先隨機選取一組樣本作爲中心點集,每個中心點對應一個簇
2). 計算各樣本點到各個中心點的距離(如歐幾里德距離),將樣本點放入距離中心點最近的那個簇中
3). 計算各簇中,距離簇內各樣本點距離的絕度誤差最小的點,作爲新的中心點
4). 如果新的中心點集與原中心點集相同,算法終止;如果新的中心點集與原中心點集不完全相同,返回c)

K-mediods算法舉例
a) 設有(A,B,C,D,E,F)一組樣本
b) 隨機選擇B、E爲中心點
c) 計算D和F到B的距離最近,A和C到E的距離最近,則B,D,F爲簇X1,A,C,E爲簇X2
d) 計算X1發現,D作爲中心點的絕對誤差最小,X2中依然是E作爲中心點絕對誤差最小
e) 重新以D、E作爲中心點,重複c)、d)步驟後,不再變換,則簇劃分確定。

層次聚類

密度聚類

DSCAN

DSCAN(Density-Based Spatial Clustering of Applications with Noise,具有噪聲的基於密度的聚類方法)是一種基於密度的空間聚類算法。它將簇定義爲密度相連的點的最大集合,能夠把具有足夠高密度的區域劃分爲簇,並在具有噪聲的空間數據庫中發現任意形狀的聚類。

優點:可以對任何形狀的數據進行聚類,而K-means系列主要針對高斯分佈的數據,類圓形的數據。

函數解釋:
sklearn.cluster.dbscan(X, eps=0.5, min_samples=5, metric=‘minkowski’, algorithm=‘auto’, leaf_size=30, p=2, sample_weight=None, random_state=None)
Parameters:
X:數組或稀疏矩陣,待聚類的特徵數組。
eps:float,可選,閾值,指兩個樣本之間的最大距,在距離內則視爲在同一個鄰域中。
min_samples:int,可選,將某個點視爲核心點的鄰域中的樣本數。
algorithm:{‘auto’,‘ball_tree’,‘kd_tree’,‘brute’},可選最近鄰模塊用來計算點距離和尋找最近鄰的算法。
leaf_size:int,可選(默認值=30)。作爲BallTree或cKDTree的參數,影響構造和查詢的速度,以及存儲樹所需的內存。
Returns:
labels:標籤
參數選擇:

  • 半徑:半徑是最難指定的 ,大了圈住的點就多,簇的個數就少了;反之,簇的個數就多,這對我們最後的結果是有很大影響的。這個時候K距離可以幫助我們來設定半徑r,也就是要找到突變點,比如:首先選中一個點,計算它和其他點之間的距離,從小到大排序,(d1,d2,,dn)(d_{1},d_{2},\cdots,d_{n}),我們發現d3d_{3}d4d_{4}之間的差異很大,於是認爲前面的距離是比較合適的,那麼就可以指定r半徑的大小爲0.13。這雖然是一個可取的方法,但確實很麻煩。
0.1 0.11 0.13 0.4 0.46 0.49
d1d_{1} d2d_{2} d3d_{3} d4d_{4} d5d_{5} d6d_{6}

代碼

for i in range(2724):
    item_third_ = item_third[i]
    item_third_name_ = item_third_name[i]
    dept_id_ = dept_id[i]
    dept_name_ = dept_name[i]
    
    # m
    info_sql = "select m,dt from table where dept_id='"+ dept_id_ +"' and item_third='"+ item_third_ +"'"
    info_df = spark.sql(info_sql)
    info_df = info_df.sort(info_df.dt.asc())
    info_list = info_df.collect()
    m =  [float(x['m']) for x in info_list]
    dt = [x['dt'] for x in info_list]
    dt_time = [datetime.datetime.strptime(x,"%Y-%m") for x in dt]
    
    # average_sale_price
    average_sale_price_sql = "select average_sale_price,dt from table where item_third='"+ item_third_ +"'"
    average_sale_price_df = spark.sql(average_sale_price_sql)
    average_sale_price_df = average_sale_price_df.sort(average_sale_price_df.dt.asc())
    average_sale_price_list = average_sale_price_df.collect()
    average_sale_price =  [float(x['average_sale_price']) for x in average_sale_price_list]
    dt_2 = [x['dt'] for x in average_sale_price_list]
    dt_2_time = [datetime.datetime.strptime(x,"%Y-%m") for x in dt_2]    
    
    # draw image
    plt.rcParams['font.sans-serif'] = ['SimHei'] 
    plt.rcParams['axes.unicode_minus'] = False  
    plt.rcParams['savefig.dpi'] = 100
    plt.rcParams['figure.dpi'] = 100
    fig = plt.figure()
    fig.set_size_inches(18.5, 10.5)
    plt.title('%s_%s_%s_%s_m' %(dept_id_, dept_name_, item_third_, item_third_name_))
    
    ax1 = fig.add_subplot(211)
    plt.plot(dt_time, m)
    plt.grid() 
    plt.xlabel('dt')
    plt.ylabel('m')

    ax2 = fig.add_subplot(212)
    plt.plot(dt_2_time, average_sale_price)
    plt.grid()
    plt.xlabel('dt_2')
    plt.ylabel('average_sale_price')
    plt.title('average_sale_price')
    
    dept_name_backup = dept_name_.replace('/','')
    item_third_name_backup = item_third_name_.replace('/','')
    plt.savefig('./nox/average_sale_price_2year/%s_%s.png'%(dept_name_backup, item_third_name_backup), dpi=150) 

國外有一個特別有意思的網站:https://www.naftaliharris.com/blog/visualizing-dbscan-clustering/
它可以把我們DBSCAN的迭代過程動態圖畫出來。
在這裏插入圖片描述
DBSCAN聚類的最終結果,如下:
在這裏插入圖片描述
沒有顏色標註的就是圈不到的樣本點,也就是離羣點,DBSCAN聚類算法在檢測離羣點的任務上也有較好的效果。如果是傳統的Kmeans聚類,我們也來看一下效果:
在這裏插入圖片描述
完美的體現出來DBSCAN算法基於密度聚類的優勢。

譜聚類

常用的評估方法:輪廓係數(Silhouette)

s(i)=b(i)a(i)max{b(i),a(i)} s(i)=\frac{b(i)-a(i)}{max \left \{ b(i),a(i) \right \}}
s(i)={1a(i)b(i),a(i)<b(i)0,a(i)=b(i)a(i)b(i)1,a(i)>b(i) s(i) = \left\{\begin{matrix} 1-\frac{a(i)}{b(i)},a(i)<b(i) & \\ & \\ 0,a(i)=b(i) & \\ & \\ \frac{a(i)}{b(i)}-1,a(i)>b(i) & \end{matrix}\right.
計算樣本i到同簇其它樣本到平均距離ai。ai越小,說明樣本i越應該被聚類到該簇,將ai稱爲樣本i到簇內不相似度。
計算樣本i到其它某簇Cj的所有樣本的平均距離bij,稱爲樣本i與簇Cj的不相似度。定義爲樣本i的簇間不相似度:bi=min(bi1,bi2,…,bik2)

si接近1,則說明樣本i聚類合理
si接近-1,則說明樣本i更應該分類到另外的簇
若si近似爲0,則說明樣本i在兩個簇的邊界上

所有樣本的sis_{i}的均值稱爲聚類結果的輪廓係數,是該聚類是否合理、有效的度量。

相似度度量及相互係數

相似度、距離計算

參考:
https://blog.csdn.net/luanpeng825485697/article/details/79443512
https://blog.csdn.net/huacha__/article/details/81094891

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