python聚类分析代码_kmeans 聚类 --- (代码为: 博客数据聚类) (python )

kmeans聚类  迭代时间远比层次聚类的要少,处理大数据,kmeans优势极为突出.。

对博客数据进行聚类,实验测试了: 层次聚类的列聚类(单词聚类)几乎要上1小时,而kmeans对列聚类只需要迭代4次!! 快速极多。

如图:包含两个聚类的kmean聚类过程:

总思路:

将所有要聚类的博客,全部用word表示成一个向量,即每篇博客都是由单词组成的,然后形成了一个单词-博客 的矩阵,矩阵里的权重值就是单词在当前博客出现的总次数。

这样kmeans就是要将这些词频矩阵进行聚类。其实kmeans这里用到的距离相似度是用pearson。

聚类之前,先读取数据文件blogdata.txt.文本如下:

源文件第一列是博客名。第一行从第二列起,是这些博客的单词列表的所有单词。所以这里便有个wordlist。

这里的博客内容全部用单词表示,从第二行开始,每一行都是表示一篇博客。

defreadfile(filename):#取得文件的所有内容,用数组存文件的每行数据

lines = [line for line infile(filename)]#获取矩阵的第一行数据,用数组列表columnames存储 所有列名

columnnames =[]

columnnames= lines[0].strip().split('\t')[1:] #从数组下标为1的开始取,不要 下标为0的,因为下标为0,是“Blog”,删去,返回的是数组列表

rownames=[]

data=[]

splitwords=[]

datatemp=[]for line in lines[1:]: #从下标为1的line数组里取各行,即从矩阵第二行开始去

splitwords = line.strip().split('\t')#每行的第一列是行名 ,rownames存的是所有行名

rownames.append(splitwords[0])#剩下部分是该行对应的数据

#data.append(splitwords[1:]) #这里数据虽然是数字,但添加的是string类型,但是应该改成添加float类型

datatemp = [float(x) for x in splitwords[1:]] #即,datatemp存的是所有x的数组:[x]。 等式右边的一开始[]号不能省略啊

data.append(datatemp) #最终data里的每个元素都是一个datatemp数组,也就是data每个元素就存着 矩阵一行,在本例,data每个元素就是存着一个博客的数据

return rownames,columnnames,rowsdata #rownames是博客名,方便以后将结果树状打印

这里的rownames将是wordnames:

rowsdata就是:

所以kmeans就是对上面这一串数字rowdata进行聚类的。

kmeans聚类步骤:

def kmeanscluster(rowsdata,distance = pearson , finalclusternumberK = 4):

1.随机创建k个中心点(对应博客聚类,则是创k个中心类似博客,但是这k个中心类似博客基本不会命中原来的博客,这k个中心点是新创建的

#中心值博客的所有列的值(即单词的值)就是最小最大博客的平均值。

例如: 所有行里,对所有列 求出每列的最小大值,然后将两者平均,则中心博客 的所有列就是这些平均值

如:原数据为:每行表示一个博客,列表示单词。数字表示单词在博客出现的次数。

0 02 1...2 3 4 10...4 60 0 ...

则对每列进行最小最大值,求出最大最小值博客:

最小值: 0 0 0 0

最大值:4 6 4 10

则中心博客的各列是 最小值博客 和最大值博客的 平均:2 3 2 5

代码中,rangeslistofAllWords存储则会是:[ (0,4) ,(0,6) ,(0,4),(0,10)...... ] 所以[2,3 , 2, 5....] 就设为中心点

'''但在下面代码实现中,求中心博客,并不是简单求平均值,而是引用了随机数!!即如radom()*(max-min) +min

若radom()是0.2,则

最终中心博客是;

(0.4 0.6 0.4 1 ) + (0 0 0 0)

为:0.4 0.6 0.4 1

'''

代码如下:

#确定每个点的最小、大值,即确定一个单词 出现次数的最大最小值。即创2行单词数列 的二维数组,定义如下:

rangeslistofAllWords=[]for i in range(len(rowsdata[0])): #对所有列进行遍历,每一列的最大最小值找出来,也就是每一个单词的最大最小出现次数找出来

minvalue = min(line[i] for line inrowsdata)

maxvalue= max(line[i] for line inrowsdata)

Elementofcolumn=(minvalue,maxvalue)

rangeslistofAllWords.append(Elementofcolumn)#rangslistofallwords 每个元素里是一个元组

#随机创建k个中心点(对应博客聚类,则是创k个中心类似博客)。

meancluster =[ [(rangeslistofAllWords[j][1] - rangeslistofAllWords[j][0]) * random.random() +rangeslistofAllWords[j][0]for j inrange(len(rowsdata[0]))]for i in range(0,finalclusternumberK)] #创建i行 j列二维数组,i*j

2. 遍历数据所有行(即遍历所有博客),为每行找到匹配的中心点。 计算每行与每个中心点的距离(这里用的pearson距离),

对每行,匹配中心点,找出与该行距离最小的中心点,从而归入到该中心点的簇去,即放进lowestDistanceMatches[]去。记住放进去的是ID,而不是这些行本身。这样会简洁很多

#设置匹配列表 (如果用字典表示,怎样实现呢????

lowestDistanceMatches =[[]for i in range (finalclusternumberK)] #初始化:二维数组,列表套列表 = ,并定义它列表的长度

#遍历数据的所有行,为每行找到匹配的中心点 (即距离该行最小的中心点)

for j in range(len(rowsdata)): #遍历所有行

row = rowsdata[j] #方便记住row该行对应的id :j

IndexofBestMatchmeancluster =0

lowestdistance= distance(row,meancluster[0]) #初始化最小距离

for i in range(finalclusternumberK): #遍历所有的中心点

if distance(row,meancluster[i])

lowestdistance=distance(row,meancluster[i])

IndexofBestMatchmeancluster= i #只传下标ID, 跟选择排序一样。

lowestDistanceMatches[IndexofBestMatchmeancluster].append(j) #j是该行的下标,IndexofBestMatchmeancluster 为该行匹配的中心点的下标

3.把中心点变成其所有成员的平均位置处

#中心点重新分布,中心点为所在簇的平均值

for i in range(finalclusternumberK): #遍历所有的中心点

newmeanrowforallWords = [0.0]*len(rowsdata[0])#很重要!! 初始化了数组列表,并且定义 数组列表的长度 ,为什么不[for i range(len(rowsdata))]呢??

#遍历同一个簇下的所有行,从而求得 平均值的行

if len(lowestDistanceMatches[i] )>0: #有一些中心点是聚不到元素

for rowid in lowestDistanceMatches[i]: #lowestDistanceMatches[i] 存储着那些 聚敛在一起的博客行 。相当与二维数组[i][rowid]

#print 'one '

for colid in range(len(rowsdata[0])): #遍历中心点的所有列

newmeanrowforallWords[colid]+=rowsdata[rowid][colid]for j in range(len(newmeanrowforallWords)): #求平均

newmeanrowforallWords[j]/=len(lowestDistanceMatches[i])#print newmeanrowforallWords

meancluster[i]=newmeanrowforallWords

4. 迭代第2,3步,直到 lowestDistanceMatches[]里的元素不再变化。(要比较是否变化,就要多设一个以前匹配列表lastmatches来比较)

lastmatches =Nonefor times in range(100):print '迭代 第%d 次:' % times

#在这里插入第2,3 步的 代码。第2,3步代码都与上一句print对齐,同属for times的内容。 最好是将下面终止条件一段代码,放在第2,3步代码 之间,可以早点break迭代

#终止条件: lowestDistanceMatches 匹配结果没变过,则说明聚类完成,结束

if lowestDistanceMatches ==lastmatches:breaklastmatches= lowestDistanceMatches

最终的目的就是求出lowestDistancematches[]

return lowestDistanceMatches

到此,kmean函数定义完成。

定义pearson距离 函数:

defpearson(v1,v2):#Simple sums

sum1=sum(v1)

sum2=sum(v2)#Sums of the squares

sum1Sq=sum([pow(v,2) for v inv1])

sum2Sq=sum([pow(v,2) for v inv2])#Sum of the products

pSum=sum([v1[i]*v2[i] for i inrange(len(v1))])#Calculate r (Pearson score)

num=pSum-(sum1*sum2/len(v1))

den=sqrt((sum1Sq-pow(sum1,2)/len(v1))*(sum2Sq-pow(sum2,2)/len(v1)))if den==0: return0return 1.0-num/den

对列聚类,定义转置矩阵 rotatematrix 即可。

最后,运行kmeans函数:

blognames ,wordnames, rowsdata = HierarchicalCluster.readfile("blogdata.txt")choice= int (raw_input('请输入:1表示行聚类:博客聚类; 输入2表示列聚类,单词kmeans聚类:'))if choice == 1 : #为什么加了一个if ,运行就有问题???

print '行聚类:博客聚类 开始:'clusters= kmeanscluster(rowsdata, finalclusternumberK=4)#遍历结果,输出结果

for i inrange(len(clusters)):print [ blognames[j] for j in clusters[i]] #一句输出等于下面三句!

#for j in clusters[i]:#print '%s ,'% blognames[j],#print '\n'

else:print '列聚类(单词kmeans聚类)如下:'colsdata=rotatematrix(rowsdata)

clusters= kmeanscluster(colsdata, finalclusternumberK=10)#遍历结果,输出结果

for i inrange(len(clusters)):print [ wordnames[j] for j in clusters[i]] #一句输出等于下面三句!

运行结果:

缺点:

结果并不能如层次聚类那样图形化展示出来,还没找到这样第三方库,

树状图展示结果,效果会更好

附层次聚类代码:

def hcluster(rowsdata , distance =pearson):'''层次聚类:所有原始数据项当做一组聚类,最终所有组至底向上地聚类成一个聚类。

这里对 矩阵行 (行表示博客) 进行聚类,每一行数据当成一组。即对博客进行聚类,相似的博客聚在一起

循环体遍历计算:

每一组可能的配对 并计算它们的相关度closest,以此找到最佳配对。 相关度 用pearson距离来衡量

最佳匹配的两个聚类合并成一个聚类, 新生成的聚类所包含数据 等于 两个旧聚类的数据 求均值 的结果

一直循环直到只剩下一个聚类cluster[0]为止

变量定义:

distances是一个字典,存储所有距离的计算值

id =i 表示第i个博客,

lowestDistacePair =(i,j)表示一个元组,当前最小距离的一对,这一对簇即将合并成一个簇。

rowsdata[i]和vec存的内容都是:[0.0, 1.0, 0.0, 3.0,'''currentclustid= -1distancesDic= {} #distances是一个字典,存储所有距离的计算值 。如果是行聚类,存储距离的字典不会很大,因为是博客作为key.而如果是列聚类,字典会很大,因为单词word作为key.所以列聚类就不存储 这些距离了。直接比较 。

#最开始的聚类是数据集的行

'''所有聚类都放在一个数组cluster里,数组rowsdata会传到bicluster类的vec参数去。

clust是一个列表数组,该数组每个元素就是存着rowsdata一个元素,而rowsdata存的是矩阵一行的元素,

总之,clust数组存的是博客数组,一个元素存的是一个博客。clust数组一个元素就是矩阵一行'''clust= [bicluster(rowsdata[i],id = i) for i inrange(len(rowsdata))]'''id =i 这一步很重要 ,第几个博客,则id为几。 等于第i行数据

clust存的是矩阵的每行。rowsdata每个元素赋给vec。

rowsdata[i]和vec存的内容都是:[0.0, 1.0, 0.0, 3.0,'''

while len(clust)>1: #当聚类还剩最后一个,则跳出循环

lowestDistacePair = (0,1)

closestness= distance(clust[0].vec , clust[1].vec) #先对矩阵第一行和第二行聚类,即对第一个博客、第二个博客聚类.

#遍历每一个配对,寻找最小距离的一对,即找 lowestDistancePair

for i in range(len(clust)): #一个clust[i]就代表一个博客

for j in range(i+1,len(clust)): #range(i,)添加 i很重要,这样就不用重复计算以计算好的距离,因为计算距离和距离是一样的,所以添加一个范围(i,)就省下极多时间

'''上面的字典distancesDic 那五句代码,可以不用,只用下面一句代码代替即可,只用下面

一句代码意思是,不需要存储距离'''d=distance(clust[i].vec ,clust[j].vec)if d

closestness=d

lowestDistacePair=(i,j)

#计算两个聚类的平均值 (在找到了最小距离的一对lowerstDistancePair后),作为新合并簇的博客值 vec

mergevec = [] #mergevec是数组

mergevec =[

(clust[lowestDistacePair[0]].vec[i]+ clust[lowestDistacePair[1]].vec[i]) /2.0

for i inrange(len(clust[0].vec))

]#vec内容是:[0.0, 1.0, 0.0, 3.0,.....所以vec也是一个数组

#建立新的聚类簇,更新该簇信息

newcluster = bicluster( mergevec,left =clust[lowestDistacePair[0]],

right= clust[lowestDistacePair[1]],

distance= closestness, id =currentclustid

)#新合并的簇的id都是负数,currentclustid都是负数

#不在原始集合中的聚类(即新合并的聚类),其id为负数。

currentclustid -=1

#删去在clust的已经被合并的最小聚类。每次都删去最小的一对,以找下一对。。

#del数组是先要del[1],再del[0].顺序不能反。

#不然出错IndexError: list assignment index out of range:引用超过list最大索引。删除一定要先删除下标最大的,再删除下标最小的??是吗?

del clust[lowestDistacePair[1]]delclust[lowestDistacePair[0]]

clust.append(newcluster)#最终是将clust数组里所有元素都删掉,直到clust元素还剩下一个,即clust[0].

return clust[0]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值