第十一章 K-Means(K均值)算法模型實現(中)

python2 代碼實現:

from numpy import *

import numpy

def loadDataSet(fileName): ? #general function to parse tab -delimited floats

dataMat = [] ? ? ? ?#assume last column is target value

fr = open(fileName)

for line in fr.readlines():

curLine = line.strip().split('\t')

fltLine = map(float,curLine) #map all elements to float()

dataMat.append(fltLine)

return dataMat

def distEclud(vecA, vecB):

return sqrt(sum(power(vecA - vecB, 2))) #la.norm(vecA-vecB)

def randCent(dataSet, k):

n = shape(dataSet)[1]

centroids = mat(zeros((k,n)))#create centroid mat

for j in range(n):#create random cluster centers, within bounds of each dimension

minJ = min(dataSet[:,j])

rangeJ = float(max(dataSet[:,j]) - minJ)

centroids[:,j] = mat(minJ + rangeJ * random.rand(k,1))

return centroids

def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):

m = shape(dataSet)[0]

clusterAssment = mat(zeros((m,2)))#create mat to assign data points

#to a centroid, also holds SE of each point

centroids = createCent(dataSet, k)

clusterChanged = True

while clusterChanged:

clusterChanged = False

for i in range(m):#for each data point assign it to the closest centroid

minDist = inf; minIndex = -1

for j in range(k):

distJI = distMeas(centroids[j,:],dataSet[i,:])

if distJI < minDist:

minDist = distJI; minIndex = j

if clusterAssment[i,0] != minIndex: clusterChanged = True

clusterAssment[i,:] = minIndex,minDist**2

print centroids

for cent in range(k):#recalculate centroids

ptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]#get all the point in this cluster

centroids[cent,:] = mean(ptsInClust, axis=0) #assign centroid to mean

return centroids, clusterAssment

#以上代碼基本跟上篇python3的代碼一致,除了2跟3語法不同的語句外

二分K均值算法

為了克服K均值算法收斂于局部最小值的問題,提出了二分K均值算法。

算法思想

該算法首先將所有點作為一個簇,然后將該簇一分為2,之后選擇其中一個簇繼續進行劃分,劃分規則是按照最大化SSE(目標函數)的值。

主要步驟:

將所有點看成一個簇

計算每一個簇的總誤差

在給定的簇上進行K均值聚類,計算將簇一分為二的總誤差

選擇使得誤差最小的那個簇進行再次劃分

重復步驟2,直到簇的個數滿足要求

具體實現

#!/usr/bin/python

def biKmeans(dataSet, k, distMeas=distEclud):

m = shape(dataSet)[0]

clusterAssment = mat(zeros((m,2)))#創建一個矩陣存儲每個點的簇分配結果及平方誤差

centroid0 = mean(dataSet, axis=0).tolist()[0]#計算整個數據集的質心

centList =[centroid0] #create a list with one centroid#使用一個列表來保留所有的質心

for j in range(m):#calc initial Error 遍歷數據集中所有點

clusterAssment[j,1] = distMeas(mat(centroid0), dataSet[j,:])**2#計算每個點到質心的誤差值

while (len(centList) < k):#該循環會不停對簇進行劃分,直到得到想要的簇數目為止,為此需要比較劃分前后的sse

lowestSSE = inf#開始將最小SSE設為無窮大

for i in range(len(centList)):#遍歷簇列表centList中的每個簇來決定最佳的簇進行劃分

ptsInCurrCluster = dataSet[nonzero(clusterAssment[:,0].A==i)[0],:]#get the data points currently in cluster i 對每個簇,對該簇中的所有點看成一個小的數據集ptsInCurrCluster

centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distMeas)#將ptsInCurrCluster輸入到函數kmeans()(k=2)中進行處理生成2個質心簇,并給出每個簇的誤差值

#誤差與剩余數據集的誤差之和將作為本次劃分的誤差

sseSplit = sum(splitClustAss[:,1])#compare the SSE to the currrent minimum

sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:,0].A!=i)[0],1])

print "sseSplit, and notSplit: ",sseSplit,sseNotSplit

if (sseSplit + sseNotSplit) < lowestSSE:#如果該劃分的sse值最小,則本次劃分保存

...

一旦決定了要劃分的簇,就要執行實際劃分操作,即將要劃分的簇中所有點的簇分配結果進行修改即可。當使用KMEANS()函數并簇數為2時,得到兩個編號0與1的結果簇,需要將這些簇編號修改改為劃分簇與新加簇的編號,該過程通過2個數組過濾器完成

...

bestCentToSplit = i

bestNewCents = centroidMat

bestClustAss = splitClustAss.copy()

lowestSSE = sseSplit + sseNotSplit#

bestClustAss[nonzero(bestClustAss[:,0].A == 1)[0],0] = len(centList) #change 1 to 3,4, or whatever

bestClustAss[nonzero(bestClustAss[:,0].A == 0)[0],0] = bestCentToSplit

print 'the bestCentToSplit is: ',bestCentToSplit

print 'the len of bestClustAss is: ', len(bestClustAss)

centList[bestCentToSplit] = bestNewCents[0,:].tolist()[0]#replace a centroid with two best centroids #新的簇分配結果被更新

centList.append(bestNewCents[1,:].tolist()[0])#新的質心添加到centlist中

clusterAssment[nonzero(clusterAssment[:,0].A == bestCentToSplit)[0],:]= bestClustAss #reassign new clusters, and SSE

return mat(centList), clusterAssment #while循環結束后,同kmeans()函數一樣,函數返回質心列表與簇分配結果

下面幾個函數是將一個API字典里的地址轉換為經度與緯度,然后對給出的地址坐標進行聚類,最后畫出所有點以及簇中心,并看看聚類結果到底如何。

import urllib

import json

def geoGrab(stAddress, city): #函數geoGrab()從雅虎返回一個字典

apiStem = 'http://where.yahooapis.com/geocode?' #create a dict and constants for the goecoder

params = {}

params['flags'] = 'J'#JSON return type返回類型為json格式

params['appid'] = 'aaa0VN6k'

params['location'] = '%s %s' % (stAddress, city)

url_params = urllib.urlencode(params)#urllib的urlencode()函數將創建的字典轉換為可通過URL進行傳遞的字符串格式

yahooApi = apiStem + url_params

print yahooApi #打印輸出的URL

c=urllib.urlopen(yahooApi)#打開url

return json.loads(c.read())#讀取返回值

由于返回值是json格式,所以可以使用json的python模塊來將其解碼為一個字典,一旦返回了解碼后的字典,也就意味著你成功的對一個地址進行了地理編碼。

from time import sleep

def massPlaceFind(fileName):#massplacefind()函數將所有這些封裝起來并且將相關信息保存到文件中

fw = open('places.txt', 'w')#打開一個文本文件

for line in open(fileName).readlines():

line = line.strip()

lineArr = line.split('\t')#tab分割的文件

retDict = geoGrab(lineArr[1], lineArr[2])#獲取第2列第3列結果,并輸入到geoGrab函數中

if retDict['ResultSet']['Error'] == 0:#檢查geoGrab()的輸出字典判斷有沒有錯誤,如果沒有錯誤,就可以從字典中讀取經緯度,

lat = float(retDict['ResultSet']['Results'][0]['latitude'])

lng = float(retDict['ResultSet']['Results'][0]['longitude'])

print "%s\t%f\t%f" % (lineArr[0], lat, lng)

fw.write('%s\t%f\t%f\n' % (line, lat, lng))#這些值被添加到原來的原來對應的行上,同時寫入到新的文件中

else: print "error fetching" #如果有錯誤,就不需要抽取經緯度

sleep(1)#利用sleep()函數將massPlaceFind()函數推遲1秒,為了保護不要在短時間內過于頻繁的調用API,因為如果頻繁調用,請求可能會被封掉,所以推遲一下比較好。

fw.close()

對地理坐標進行聚類

def distSLC(vecA, vecB):#Spherical Law of Cosines

a = sin(vecA[0,1]*pi/180) * sin(vecB[0,1]*pi/180)

b = cos(vecA[0,1]*pi/180) * cos(vecB[0,1]*pi/180) * \

cos(pi * (vecB[0,0]-vecA[0,0]) /180)#使用球面余弦定理來計算2個經緯度之間的距離,因為兩級與赤道同時走相同的距離對經緯度的變化不同

return arccos(a + b)*6371.0 #pi is imported with numpy#返回地球表面兩點之間的距離

#簇聚類及繪圖函數

import matplotlib

import matplotlib.pyplot as plt

def clusterClubs(numClust=3):

datList = []

for line in open('C:/Users/HZF/Desktop/python數據挖掘十大算法實現/11.csv').readlines():

lineArr = line.strip().split(',')

#print lineArr

#np.array

fltLine = map(float,lineArr)

#print fltLine

datList.append(fltLine)

#print dataList

#datList=numpy.array(datList)

#print datList

datMat = mat(datList)

myCentroids, clustAssing= kMeans(datMat, numClust)#使用kmeans算法聚類,這里也可以調用bikmeans聚類

#print myCentroids, clustAssing

fig = plt.figure()

rect=[0.1,0.1,0.8,0.8]

scatterMarkers=['s', 'o', '^', '8', 'p', \

'd', 'v', 'h', '>', '<']#標記類型

axprops = dict(xticks=[], yticks=[])

ax0=fig.add_axes(rect, label='ax0', **axprops)

plt.savefig('glp.png', dpi = 75)

imgp=plt.imread('portland.png')#imread()函數基于一副圖像來創建矩陣

ax0.imshow(imgP)#繪制該矩陣

ax1=fig.add_axes(rect, label='ax1', frameon=False)

for i in range(numClust):#遍歷每一個簇

ptsInCurrCluster = datMat[nonzero(clustAssing[:,0].A==i)[0],:]

markerStyle = scatterMarkers[i % len(scatterMarkers)]#使用索引i%len(scatterMarkers)選擇標記形狀,當有更多簇時,可以循環使用這些標記

ax1.scatter(ptsInCurrCluster[:,0].flatten().A[0], ptsInCurrCluster[:,1].flatten().A[0], marker=markerStyle, s=90)#一一畫出來

ax1.scatter(myCentroids[:,0].flatten().A[0], myCentroids[:,1].flatten().A[0], marker='+', s=300)#使用+字標記表示簇中心

plt.show()#顯示在圖中

if __name__=='__main__': #主函數調用

clusterClubs(numClust=3)

這篇是對kmeans算法的python2實現,剛開始在簡書上寫代碼,排版不是很專業,還在尋找合適的排版軟件,后續會改變,這些代碼可以在諸如editplus等python專業編輯器里自動排版。下篇主要寫應用及參考文獻。會盡快更新!

(待續)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,125評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,506評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,614評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,402評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,934評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,168評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,690評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,596評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,027評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,404評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,662評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,398評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,743評論 2 370

推薦閱讀更多精彩內容