python實現一個層次聚類方法
阿新 • • 發佈:2017-08-11
mac ima 優先隊列 () don 標簽 中位數 filepath normal
層次聚類(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 returntmp[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
python實現一個層次聚類方法