各種聚類算法(原理+代碼+對比分析)最全總結

序言

最近在準備算法面試,百度金融、京東廣告數據部、新浪信息部都被我面完了,像58同城、掌閱、宜信這樣的口碑不好的公司給我發了面試邀請都被我拒了,只有兩家創業公司面試成功。我的認識是對於一個畢業才一年,本科學歷的我來說目前最適合我的還是互聯網一線公司,雖然前幾次面試都失敗了,最近幾天也分析了失敗的原因,希望以後的路越走越順:
1.沒有認真梳理我一年多的項目,面試的時候不能清晰表達項目流程;
2.自從c語言考試後再也沒有自己寫過程序,都是在編輯器裏面調試,面試時給一張白紙和筆就傻眼了,基礎的排序算法、二叉查找樹、紅黑樹等還是要能信手拈來,直接下筆寫正確;
3.沒有把之前熟悉的算法比如粒子羣算法、各種聚類算法、卷積神經網絡、深度學習的知識系統地複習整理,導致面試時有很多概念模糊甚至想不起來;
4.沒有面試經驗,面試時情商表現偏低。

還是要持續總結,持續積累。


一、聚類的目標

使同一類對象的相似度儘可能地大;不同類對象之間的相似度儘可能地小。


二、聚類算法分類

1.基於劃分

給定一個有N個元組或者紀錄的數據集,分裂法將構造K個分組,每一個分組就代表一個聚類,K<N。
特點:計算量大。很適合發現中小規模的數據庫中小規模的數據庫中的球狀簇。
算法:K-MEANS算法、K-MEDOIDS算法、CLARANS算法

2.基於層次

對給定的數據集進行層次似的分解,直到某種條件滿足爲止。具體又可分爲“自底向上”和“自頂向下”兩種方案。
特點:較小的計算開銷。然而這種技術不能更正錯誤的決定。
算法:BIRCH算法、CURE算法、CHAMELEON算法

3.基於密度

只要一個區域中的點的密度大過某個閾值,就把它加到與之相近的聚類中去。
特點:能克服基於距離的算法只能發現“類圓形”的聚類的缺點。
算法:DBSCAN算法、OPTICS算法、DENCLUE算法

4.基於網格

將數據空間劃分成爲有限個單元(cell)的網格結構,所有的處理都是以單個的單元爲對象的。
特點:處理速度很快,通常這是與目標數據庫中記錄的個數無關的,只與把數據空間分爲多少個單元有關。
算法:STING算法、CLIQUE算法、WAVE-CLUSTER算法

三、DBscan聚類

1.算法原理

DBSCAN(Density-Based Spatial Clustering of Application with Noise)是一種典型的基於密度的聚類算法,在DBSCAN算法中將數據點分爲一下三類:
核心點:在半徑Eps內含有超過MinPts數目的點
邊界點:在半徑Eps內點的數量小於MinPts,但是落在覈心點的鄰域內
噪音點:既不是核心點也不是邊界點的點
在這裏有兩個量,一個是半徑Eps,另一個是指定的數目MinPts
這裏寫圖片描述

2.代碼
#  encoding=utf-8

import numpy as np
from sklearn.cluster import DBSCAN
from sklearn import metrics
from sklearn.datasets.samples_generator import make_blobs
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt


class DBScan (object):
    """
    the class inherits from object, encapsulate the  DBscan algorithm
    """
    def __init__(self, p, l_stauts):

        self.point = p
        self.labels_stats = l_stauts
        self.db = DBSCAN(eps=0.2, min_samples=10).fit(self.point)

    def draw(self):

        coreSamplesMask = np.zeros_like(self.db.labels_, dtype=bool)
        coreSamplesMask[self.db.core_sample_indices_] = True
        labels = self.db.labels_
        nclusters = jiangzao(labels)

        # 輸出模型評估參數,包括估計的集羣數量、均勻度、完整性、V度量、
        # 調整後的蘭德指數、調整後的互信息量、輪廓係數
        print('Estimated number of clusters: %d' % nclusters)
        print("Homogeneity: %0.3f" % metrics.homogeneity_score(self.labels_stats, labels))
        print("Completeness: %0.3f" % metrics.completeness_score(self.labels_stats, labels))
        print("V-measure: %0.3f" % metrics.v_measure_score(self.labels_stats, labels))
        print("Adjusted Rand Index: %0.3f"
              % metrics.adjusted_rand_score(self.labels_stats, labels))
        print("Adjusted Mutual Information: %0.3f"
              % metrics.adjusted_mutual_info_score(self.labels_stats, labels))
        print("Silhouette Coefficient: %0.3f"
              % metrics.silhouette_score(self.point, labels))

        # 繪製結果
        # 黑色被移除,並被標記爲噪音。
        unique_labels = set(labels)
        colors = plt.cm.Spectral(np.linspace(0, 1, len(unique_labels)))
        for k, col in zip(unique_labels, colors):
            if k == -1:
                # 黑色用於噪聲
                col = 'k'

            classMemberMask = (labels == k)

            # 畫出分類點集
            xy = self.point[classMemberMask & coreSamplesMask]
            plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor=col,
                     markeredgecolor='k', markersize=6)

            # 畫出噪聲點集
            xy = self.point[classMemberMask & ~coreSamplesMask]
            plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor=col,
                     markeredgecolor='k', markersize=3)
        # 加標題,顯示分類數
        plt.title('Estimated number of clusters: %d' % nclusters)
        plt.show()


def jiangzao (labels):

    # 標籤中的簇數,忽略噪聲(如果存在)
    clusters = len(set(labels)) - (1 if -1 in labels else 0)
    return clusters

def standar_scaler(points):

    p = StandardScaler().fit_transform(points)
    return p

if __name__ == "__main__":
     """
     test class dbScan
     """
     centers = [[1, 1], [-1, -1], [-1, 1], [1, -1]]
     point, labelsTrue = make_blobs(n_samples=2000, centers=centers, cluster_std=0.4,
                                    random_state=0)
     point = standar_scaler(point)
     db = DBScan(point, labelsTrue)
     db.draw()
3.圖形輸出

這裏寫圖片描述
如圖算法自動將數據集分成了4簇,用四種顏色代表。每一簇內較大的點代表核心對象,較小的點代表邊界點(與簇內其他點密度相連,但是自身不是核心對象)。黑色的點代表離羣點或者叫噪聲點。

4.控制檯輸出
Estimated number of clusters: 4
Homogeneity: 0.928
Completeness: 0.862
V-measure: 0.894
Adjusted Rand Index: 0.928
Adjusted Mutual Information: 0.862
Silhouette Coefficient: 0.584

四、K-means聚類

1.算法原理

這裏寫圖片描述

2.代碼
#coding=utf-8
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans

#從磁盤讀取城市經緯度數據
X = []
f = open('city.txt')
for v in f:
    X.append([float(v.split(',')[1]), float(v.split(',')[2])])
#轉換成numpy array
X = np.array(X)
#類簇的數量
n_clusters = 5
#現在把數據和對應的分類書放入聚類函數中進行聚類
cls = KMeans(n_clusters).fit(X)
#X中每項所屬分類的一個列表
cls.labels_
#畫圖
markers = ['^', 'x', 'o', '*', '+']
for i in range(n_clusters):
  members = cls.labels_ == i
  plt.scatter(X[members, 0], X[members, 1], s=60, marker=markers[i], c='b', alpha=0.5)
plt.title(' ')
plt.show()
3.圖像輸出

這裏寫圖片描述

五、層次聚類

1.算法簡介

凝聚層次聚類:所謂凝聚的,指的是該算法初始時,將每個點作爲一個簇,每一步合併兩個最接近的簇。另外即使到最後,對於噪音點或是離羣點也往往還是各佔一簇的,除非過度合併。對於這裏的“最接近”,有下面三種定義。我在實現是使用了MIN,該方法在合併時,只要依次取當前最近的點對,如果這個點對當前不在一個簇中,將所在的兩個簇合並就行:

  • 單鏈(MIN):定義簇的鄰近度爲不同兩個簇的兩個最近的點之間的距離。
  • 全鏈(MAX):定義簇的鄰近度爲不同兩個簇的兩個最遠的點之間的距離。
  • 組平均:定義簇的鄰近度爲取自兩個不同簇的所有點對鄰近度的平均值。
2.代碼
# scoding=utf-8
# Agglomerative Hierarchical Clustering(AHC)
import pylab as pl
from operator import itemgetter
from collections import OrderedDict,Counter
points = [[int(eachpoint.split('#')[0]), int(eachpoint.split('#')[1])] for eachpoint in open("points","r")]
# 初始時每個點指派爲單獨一簇
groups = [idx for idx in range(len(points))]
# 計算每個點對之間的距離
disP2P = {}
for idx1,point1 in enumerate(points):
  for idx2,point2 in enumerate(points):
    if (idx1 < idx2):
      distance = pow(abs(point1[0]-point2[0]),2) + pow(abs(point1[1]-point2[1]),2)
      disP2P[str(idx1)+"#"+str(idx2)] = distance
# 按距離降序將各個點對排序
disP2P = OrderedDict(sorted(disP2P.iteritems(), key=itemgetter(1), reverse=True))
# 當前有的簇個數
groupNum = len(groups)
# 過分合並會帶入噪音點的影響,當簇數減爲finalGroupNum時,停止合併
finalGroupNum = int(groupNum*0.1)
while groupNum > finalGroupNum:
  # 選取下一個距離最近的點對
  twopoins,distance = disP2P.popitem()
  pointA = int(twopoins.split('#')[0])
  pointB = int(twopoins.split('#')[1])
  pointAGroup = groups[pointA]
  pointBGroup = groups[pointB]
  # 當前距離最近兩點若不在同一簇中,將點B所在的簇中的所有點合併到點A所在的簇中,此時當前簇數減1
  if(pointAGroup != pointBGroup):
    for idx in range(len(groups)):
      if groups[idx] == pointBGroup:
        groups[idx] = pointAGroup
    groupNum -= 1
# 選取規模最大的3個簇,其他簇歸爲噪音點
wantGroupNum = 3
finalGroup = Counter(groups).most_common(wantGroupNum)
finalGroup = [onecount[0] for onecount in finalGroup]
dropPoints = [points[idx] for idx in range(len(points)) if groups[idx] not in finalGroup]
# 打印規模最大的3個簇中的點
group1 = [points[idx] for idx in range(len(points)) if groups[idx]==finalGroup[0]]
group2 = [points[idx] for idx in range(len(points)) if groups[idx]==finalGroup[1]]
group3 = [points[idx] for idx in range(len(points)) if groups[idx]==finalGroup[2]]
pl.plot([eachpoint[0] for eachpoint in group1], [eachpoint[1] for eachpoint in group1], 'or')
pl.plot([eachpoint[0] for eachpoint in group2], [eachpoint[1] for eachpoint in group2], 'oy')
pl.plot([eachpoint[0] for eachpoint in group3], [eachpoint[1] for eachpoint in group3], 'og')
# 打印噪音點,黑色
pl.plot([eachpoint[0] for eachpoint in dropPoints], [eachpoint[1] for eachpoint in dropPoints], 'ok')
pl.show()
3.圖像輸出

這裏寫圖片描述

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