第十一章 K-Means(K均值)算法模型实现(上)

最近打算把所有的数据挖掘领域的算法研究一遍并用python实现写成文章。本篇是对KMeans算法的上半段代码的实现的大概总结。下篇会尽快更新,共勉!


聚类算法用于根据数据的特征发现数据项的相似性,并将相似的数据项放在同一个组中,相似性采用距离进行描述。


K-means聚类

简单的说,一般流程如下:先随机选取k个点,将每个点分配给它们,得到最初的k个分类;在每个分类中计算均值,将点重新分配,划归到最近的中心点;重复上述步骤直到点的划归不再改变。

K-Means算法的SAS实现

K-means算法可以用SAS的proc fastclus实现。主要涉及两个问题。首先是初始点的选择。如果指定replace=random,则系统随机选取maxcluster选项指定个数的完整观测作为凝聚点。如果分析员对研究情景比较了解,可以利用专业知识指定初始分类,那么可以在proc fastclus中设定seed=dataset选项,SAS会从dataset中读取前k个观测作为初始凝聚点。此外,SAS还提供了系统自动选择初始凝聚点的方法,该方法需要指定maxclusters和radius选项,其中radius为凝聚点之间允许的最小距离。默认值是maxclusters=100,radius=0,效果是选取数据集中的前100个观测作为凝聚点。fastclus过程总是选择第一个完整观测作为第一个凝聚点,然后依次考察剩余观测,与第一个凝聚点的距离大于radius指定值的观测作为第二个凝聚点。当凝聚点的个数未达到maxcluster,且所考察观测与已有凝聚点间距离均大于radius指定值时,则所考察的观测成为下一个凝聚点。如果一个观测完整且与所有凝聚点距离均大于radius,但凝聚点个数已经达到最大值,此时fastclus过程进行两种替换检验,检验能否用当前观测替换已有凝聚点。第一个检验是替换距离最近两凝聚点检验,如果凝聚点与当前观测的最小距离大于已有凝聚点的最小距离,则一个已有凝聚点将被替换,当前观测将替换距离最近的两个凝聚点中的一个,使得替换后当前观测与最近距离两凝聚点中未被替换的那个距离最远。第二个检验是替换当前观测最近凝聚点检验,如果当前观测到除最近凝聚点外的所有凝聚点的最小距离大于当前观测最近凝聚点到所有其他凝聚点的最小距离,进行替换。如果两种检验都说明该观测不能替换已有凝聚点,则转向下一个观测,直到考察完数据集中的所有观测。当然,这种检测可以用replace=none|part|full来控制,none表示不进行替换检验,part表示只进行第一种替换检验;full为默认值,两种替换检验都进行。 另一个问题是分类的修改方法。默认的方法是按批修改法,即选定一批凝聚点后;将所有观测按与其距离最近的凝聚点归类;计算每一类重心,将重心作为新的凝聚点,若新凝聚点与旧凝聚点完全重合,则终止算法,否则重新归类。批量修改法是全部观测调整完毕后,才改变类的凝聚点,而逐个修改法是每个观测一旦调整后立即改变类凝聚点,而立即改变需要算法即时验证改变后的凝聚点集合是否仍然满足radius的约束。如果不满足radius的约束,SAS会将小于radius的两类合并,计算重心,作为合并后类的凝聚点,如此往复,直到凝聚点满足radius条件。要让SAS执行逐个修改法,需要声明drift选项。

补充 K-means聚类算法的问题是,均值的计算受异常点的干扰比较严重。为了克服这个问题,可以采用K中值法。


K-medoid聚类

PAM(Partition Around Medoids)是K-medoid的基础算法,基本流程如下:首先随机选择k个对象作为中心,把每个对象分配给离它最近的中心。然后随机地选择一个非中心对象替换中心对象,计算分配后的距离改进量。聚类的过程就是不断迭代,进行中心对象和非中心对象的反复替换过程,直到目标函数不再有改进为止。

PAM算法的问题在于伸缩性不好,需要测试所有的替换,只适用于小数据量的聚类。为了提高该算法的可伸缩性,有人提出了CLARAN算法,本质如下:从总体数据中生成多个样本数据,在每个样本数据上应用PAM算法得到一组K中值点;取出所有样本中结果最好的那一组作为最后的解。

CLARAN算法存在的问题是,算法的聚类质量依赖于样本的质量。 为了提高PAM和CLARAN算法的聚类质量,有人在CLARAN算法的基础上提出了CLARANS算法。与CLARAN相比,最大的区别在于没有一个时刻算法局限于固定的一个样本中,自始自终,算法的样本数据都是随机抽样的。其算法过程如下。将每套k个中值点作为一个节点,若两个节点之间有k-1个点相同,则成为邻居。用户事先指定两个数,一是最大的邻居数,二是最大的局部最优点数。算法随机选取一个当前点,随机地取出其中的一个邻居,看目标值是否有改进,如果有改进,则用邻居替代当前点,重新开始搜索邻居的过程;若抽取了最大邻居数的邻居,发现当前点最优,那么就找到了一个局部最优点。找到一个局部最优点后,再随机抽取一个当前点,进行上面的过程,直到找到了用户指定最大数量的局部最优点。比较每个局部最优点的目标值,取最优的那个点作为结果,即可得到k个中值点,于是k个类就可以轻松得到。CLARANS算法的效果不错,但算法复杂度更高。


python3 kMeans 算法实现:

# coding=utf-8

import numpy

from numpy import *

from numpy.random.mtrand import power

from numpy import mat

#加载数据

def loadDataMat(fileName):

dataMat = []

fr = open(fileName)

for line in fr.readlines():

#print line

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

#print curLine

fltLine =list(map(float,curLine)) #map all elements to float() ,map函数在python2与python3中输出结果不同,python2中map函数输出的是一个list,python3中map函数输出的是一个迭代,所以这里要转换成list.

#print(fltLine)

dataMat.append(fltLine)

#print(dataMat)

return dataMat

# 计算欧式距离

def distEclud(vecA, vecB):

return sqrt(sum(numpy.power(abs(vecA-vecB),2))) #power函数在numpy与matplotlib中都存在,但这里的power函数是numpy模块里的,所以引用时要说明,不然会报错。

# 寻找K个随机质心

def randCent(dataMat, k):

n =shape(dataMat)[1]

centroids =mat(zeros((k,n))) #这里的zeros函数是numpy里的,所以必须要import numpy

for j in range(n):

# 每列中的最小值

minJ = numpy.min(dataMat[:,j]) #这里的min与max函数也是numpy里的,当时运行时报错,加了numpy.就没问题了

# 每列中的最大值减去最小值

rangeJ = float(numpy.max(dataMat[:,j]) - minJ)

# 得到k个质心

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

return centroids

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

m =shape(dataMat)[0]

#注意数据格式的转化,这里的clusterAssment是matrix格式的

clusterAssment = mat(zeros((m,2)))

#print(clusterAssment)

centroids = createCent(dataMat, k)

#print(centroids)

clusterChanged =True

while clusterChanged:

clusterChanged = False

#数据是二维的,有m行,对每一行即每个样本进行迭代计算

for i in range(m):

minDist =inf;minIndex =-1

for j in range(k):

# 计算任意点和数k个质心的距离

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

#选出最小的距离

if distJI < minDist:

minDist =distJI ;minIndex=j

#更新数据,直到最小值的索引不变

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

#clusterAssment存储的是每行即每个样本对应的质心的最小索引和值

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

#print (centroids)

#print(clusterAssment)

#更新质心,取对应相同质心的所有行索引的平均值,作为新的质心axis=0表示沿着二维数组的列来取的平均值。

for cent in range(k):

#clusterAssment[i,0].A操作是将numpy的mat格式矩阵转换为数组

ptsInClust = dataMat[nonzero(clusterAssment[:,0].A==cent)[0]]

centroids[cent,:] =mean(ptsInClust,axis=0)

return centroids,clusterAssment

if __name__=='__main__':#主函数调用

import matplotlib

matplotlib.use('Agg')#后面的savefig保存照片时要用到这个

from matplotlib import pyplot as plt

from matplotlib.pyplot import savefig

#要划分的聚类数目

k=3

#加载数据

dataMat = mat(loadDataMat('C:/Users/HZF/Desktop/python数据挖掘十大算法实现/11.csv'))

#print (dataMat)

numSamples = dataMat.shape[0]

#print(numSamples)

#计算聚类中心

centroids,clusterAssment =kMeans(dataMat,k)

print (centroids,clusterAssment)

#画出所有的聚类数据,根据所处聚类中心的不同用不同的表示方法

mark = ['or', 'ob', 'og', 'ok', '^r', '+r', 'sr', 'dr', '

for i in range(numSamples):

markIndex = int(clusterAssment[i, 0])

plt.plot(dataMat[i, 0], dataMat[i, 1], mark[markIndex])

#画出聚类中心

mark = ['Dr', 'Db', 'Dg', 'Dk', '^b', '+b', 'sb', 'db', '

# 画聚类中心,一共有四类

for i in range(k):

plt.plot(centroids[i, 0], centroids[i, 1], mark[i], markersize = 12)

plt.savefig('glp.png', dpi = 75)#savefig可以保存照片到当前位置

plt.show()

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

#if __name__=='__main__':

#k=4

#dataMat = mat(loadDataMat('testSet.txt'))

#myCentroids,clusterAssment =kMeans(dataMat,k)

#plt.show()

#savefig('glp.jpg', dpi = 75)

(未完待续)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容