python實現一個層次聚類方法


層次聚類(Hierarchical Clustering)

一.概念

  層次聚類不需要指定聚類的數目,首先它是將數據中的每個實例看作一個類,然后將最相似的兩個類合並,該過程迭代計算只到剩下一個類為止,類由兩個子類構成,每個子類又由更小的兩個子類構成。如下圖所示:

二.合並方法

在聚類中每次迭代都將兩個最近的類進行合並,這個類間的距離計算方法常用的有三種:

1.單連接聚類(Single-linkage clustering)

  在單連接聚類中,兩個類間的距離定義為一個類的所有實例到另一個類的所有實例之間最短的那個距離。如上圖中類A(A1,A2),B(B1,B2),C(C1,C2),A類和B類間的最短距離是A1到B1,所以A類與B類更近,所有A和B合並。

2.全連接聚類(Complete-linkage clustering)

  在全連接聚類中,兩個類間的距離定義為一個類的所有實例到另一個類的所有實例之間最長的那個距離。圖中A類和B類間最長距離是A2到B2,B類和C類最長距離是B1到C1,distance(B1-C1)<distance(A2-B2),所以B類和C類合並在一起。

3.平均連接聚類(Average-linkage clustering)

  在平均連接聚類中,類間的距離為一個類的所有實例到另一個類的所有實例的平均距離。

三.python實現(單連接)

  1 #!/usr/bin/python
  2 # -*- coding: utf-8 -*-
  3 
  4 from queue import PriorityQueue
  5 import math
  6 import codecs
  7 
  8 
  9 """
 10 層次聚類
 11 """
 12 class HCluster:
 13 
 14     #一列的中位數
 15     def getMedian(self,alist):
 16         tmp = list(alist)
 17         tmp.sort()
 18         alen = len(tmp)
 19         if alen % 2 == 1:
 20             return tmp[alen // 2]
 21         else:
 22             return (tmp[alen // 2] + tmp[(alen // 2) - 1]) / 2
 23 
 24     #對數值型數據進行歸一化,使用絕對標准分[絕對標准差->asd=sum(x-u)/len(x),x的標准分->(x-u)/絕對標准差,u是中位數]
 25     def normalize(self,column):
 26         median = self.getMedian(column)
 27         asd = sum([abs(x - median) for x in column]) / len(column)
 28         result = [(x - median) / asd for x in column]
 29         return result
 30 
 31     def __init__(self,filepath):
 32         self.data={}
 33         self.counter=0
 34         self.queue=PriorityQueue()
 35         line_1=True#開頭第一行
 36         with codecs.open(filepath,'r','utf-8') as f:
 37             for line in f:
 38                 #第一行為描述信息
 39                 if line_1:
 40                     line_1=False
 41                     header=line.split(',')
 42                     self.cols=len(header)
 43                     self.data=[[] for i in range(self.cols)]
 44                 else:
 45                     instances=line.split(',')
 46                     toggle=0
 47                     for instance in range(self.cols):
 48                         if toggle==0:
 49                             self.data[instance].append(instances[instance])
 50                             toggle=1
 51                         else:
 52                             self.data[instance].append(float(instances[instance]))
 53         #歸一化數值列
 54         for i in range(1,self.cols):
 55             self.data[i]=self.normalize(self.data[i])
 56 
 57         #歐氏距離計算元素i到所有其它元素的距離,放到鄰居字典中,比如i=1,j=2...,結構如i=1的鄰居-》{2: ((1,2), 1.23),  3: ((1, 3), 2.3)... }
 58         #找到最近鄰
 59         #基於最近鄰將元素放到優先隊列中
 60         #data[0]放的是label標簽,data[1]和data[2]是數值型屬性
 61         rows=len(self.data[0])
 62         for i in range(rows):
 63             minDistance=10000
 64             nearestNeighbor=0
 65             neighbors={}
 66             for j in range(rows):
 67                 if i!=j:
 68                     dist=self.distance(i,j)
 69                     if i<j:
 70                         pair=(i,j)
 71                     else:
 72                         pair=(j,i)
 73                     neighbors[j]=(pair,dist)
 74                     if dist<minDistance:
 75                         minDistance=dist
 76                         nearestNeighbor=j
 77             #創建最近鄰對
 78             if i<nearestNeighbor:
 79                 nearestPair=(i,nearestNeighbor)
 80             else:
 81                 nearestPair=(nearestNeighbor,i)
 82             #放入優先對列中,(最近鄰距離,counter,[label標簽名,最近鄰元組,所有鄰居])
 83             self.queue.put((minDistance,self.counter,[[self.data[0][i]],nearestPair,neighbors]))
 84             self.counter+=1
 85 
 86     #歐氏距離,d(x,y)=math.sqrt(sum((x-y)*(x-y)))
 87     def distance(self,i,j):
 88         sumSquares=0
 89         for k in range(1,self.cols):
 90             sumSquares+=(self.data[k][i]-self.data[k][j])**2
 91         return math.sqrt(sumSquares)
 92 
 93     #聚類
 94     def cluster(self):
 95         done=False
 96         while not done:
 97             topOne=self.queue.get()
 98             nearestPair=topOne[2][1]
 99             if not self.queue.empty():
100                 nextOne=self.queue.get()
101                 nearPair=nextOne[2][1]
102                 tmp=[]
103                 #nextOne是否是topOne的最近鄰,如不是繼續找
104                 while nearPair!=nearestPair:
105                     tmp.append((nextOne[0],self.counter,nextOne[2]))
106                     self.counter+=1
107                     nextOne=self.queue.get()
108                     nearPair=nextOne[2][1]
109                 #重新加回Pop出的不相等最近鄰的元素
110                 for item in tmp:
111                     self.queue.put(item)
112 
113                 if len(topOne[2][0])==1:
114                     item1=topOne[2][0][0]
115                 else:
116                     item1=topOne[2][0]
117                 if len(nextOne[2][0])==1:
118                     item2=nextOne[2][0][0]
119                 else:
120                     item2=nextOne[2][0]
121                 #聯合兩個最近鄰族成一個新族
122                 curCluster=(item1,item2)
123                 #下面使用單連接方法建立新族中的鄰居距離元素,一:計算上面新族的最近鄰。二:建立新的鄰居。如果 item1和item3距離是2,item2和item3距離是4,則在新族中的距離是2
124                 minDistance=10000
125                 nearestPair=()
126                 nearestNeighbor=''
127                 merged={}
128                 nNeighbors=nextOne[2][2]
129                 for key,value in topOne[2][2].items():
130                     if key in nNeighbors:
131                         if nNeighbors[key][1]<value[1]:
132                             dist=nNeighbors[key]
133                         else:
134                             dist=value
135                         if dist[1]<minDistance:
136                             minDistance=dist[1]
137                             nearestPair=dist[0]
138                             nearestNeighbor=key
139                         merged[key]=dist
140                 if merged=={}:
141                     return curCluster
142                 else:
143                     self.queue.put((minDistance,self.counter,[curCluster,nearestPair,merged]))
144                     self.counter+=1
145 
146 if __name__=='__main__':
147     hcluser=HCluster('filePath')
148     cluser=hcluser.cluster()
149     print(cluser)

 參考:1.machine.learning.an.algorithmic.perspective.2nd.edition.

    2.a programmer's guide to data mining


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM