“集体智慧编程”之第三章:“发现群组”的 k均值聚类
来源:互联网 发布:linux 启动数据库 编辑:程序博客网 时间:2024/06/05 19:16
分级聚类的缺点
此前学习的分级聚类、与列聚类,有二个缺点:
- 计算量大,数据越大运行越缓慢。
- 没有确切的将数据分成不同的组,只是形成了树状图。虽然我倒觉得如果写过多的代码这一点还是可以搞定的。
k均值聚类可以应对上述两种缺点,我们会预先告诉算法生成的聚类数量,也就是我要产生几个类。原理
如下图所示,对于5个数据项和两个聚类过程是这样的,先随机产生两个聚类点,那么每一个数据项都会离一其中一个最近,那么将其分配给那个聚类。比如,A/B分配了给了上方的点,C/D/E分配给了下方的点。紧着,将聚类点的位置会发生改变,会改变到分配给它的所有数据项的中心位置。然后再进行一次分配,很明显C的距离离上面那一个点变得更近了。所以再将C分配给了上面的聚类点,接着,聚类点的位置再次发生改变。如此一来。当然分配的过程中,没有一个点的分配状况被改变,那么聚类点的位置也不会发生改变。此时,聚类结束。我们产生了两个聚类。所以与分级聚类相比,其还会接受一个额外的参数,就是希望参数多少个聚类的多少。k均值聚类的代码
代码如下:- import random
-
-
- def kcluster(rows,distance=pearson,k=4):
-
- ranges=[(min([row[i] for row in rows]),max([row[i] for row in rows])) for i in range(len(rows[0]))]
-
-
-
-
-
-
- clusters=[[random.random()*(ranges[i][1]-ranges[i][0])+ranges[i][0] for i in range(len(rows[0]))] for j in range(k)]
-
- lastmatches=None
- for t in range(100):
- print 'Iteration %d' % t
- bestmatches=[[]for i in range(k)]
-
-
- for j in range(len(rows)):
- row=rows[j]
- bestmatch=0
- for i in range(k):
- d=distance(clusters[i],row)
- if d<distance(clusters[bestmatch],row):bestmatch=i
- bestmatches[bestmatch].append(j)
-
- if bestmatches==lastmatches:break
- lastmatches=bestmatches
-
-
-
- for i in range(k):
- avgs=[0.0]*len(rows[0])
- if len(bestmatches[i])>0:
- for rowid in bestmatches[i]:
- for m in range(len(rows[rowid])):
- avgs[m]+=rows[rowid][m]
- for j in range(len(avgs)):
- avgs[j]/=len(bestmatches[i])
- clusters[i]=avgs
-
-
- return bestmatches
执行代码:- blognames,words,data=readfile('blogdata.txt')
- kclust=kcluster(data,k=10)
- print [blognames[r] for r in kclust[0]]
结果如下:
我只打印了聚类中的第一个类的结果。
- >>>
- Iteration 0
- Iteration 1
- Iteration 2
- Iteration 3
- Iteration 4
- Iteration 5
- Iteration 6
- ['Hot Air', 'Talking Points Memo: by Joshua Micah Marshall', 'Andrew Sullivan | The Daily Dish', "Captain's Quarters", 'Power Line', 'The Blotter', 'Crooks and Liars', 'Think Progress', 'NewsBusters.org - Exposing Liberal Media Bias']
- >>>
很有意思的是:我发现每次结果都不一样。那这必然是因为初始随机点产生的不同的原因吧。
对项目的启示
这是一个纯粹对算法的学习,从聚类对项目的帮助来说,我已经在前两篇博客里谈的太多了,这里就不重复了。
需要记住,这个聚类产生的计算速度更快,而且可以确定产生多少个种类。这非常重要。到时在优化速度的时候必然有很大的帮助
全部源代码
-
- def readfile(filename):
- lines=[line for line in file(filename)]
-
-
- colnames=lines[0].strip().split('\t')[1:]
- rownames=[]
- data=[]
- for line in lines[1:]:
- p=line.strip().split('\t')
-
- rownames.append(p[0])
-
- data.append([float(x) for x in p[1:]])
- return rownames,colnames,data
-
-
- from math import sqrt
- def pearson(v1,v2):
-
- sum1=sum(v1)
- sum2=sum(v2)
-
-
- sum1Sq=sum([pow(v,2) for v in v1])
- sum2Sq=sum([pow(v,2) for v in v2])
-
-
- pSum=sum([v1[i]*v2[i] for i in range(len(v1))])
-
-
- num=pSum-(sum1*sum2/len(v1))
- den=sqrt((sum1Sq-pow(sum1,2)/len(v1))*(sum2Sq-pow(sum2,2)/len(v1)))
- if den==0:return 0
-
- return 1.0-num/den
-
-
-
- import random
-
- def kcluster(rows,distance=pearson,k=4):
-
- ranges=[(min([row[i] for row in rows]),max([row[i] for row in rows])) for i in range(len(rows[0]))]
-
-
-
-
-
- clusters=[[random.random()*(ranges[i][1]-ranges[i][0])+ranges[i][0] for i in range(len(rows[0]))] for j in range(k)]
-
- lastmatches=None
- for t in range(100):
- print 'Iteration %d' % t
- bestmatches=[[]for i in range(k)]
-
-
- for j in range(len(rows)):
- row=rows[j]
- bestmatch=0
- for i in range(k):
- d=distance(clusters[i],row)
- if d<distance(clusters[bestmatch],row):bestmatch=i
- bestmatches[bestmatch].append(j)
-
- if bestmatches==lastmatches:break
- lastmatches=bestmatches
-
-
- for i in range(k):
- avgs=[0.0]*len(rows[0])
- if len(bestmatches[i])>0:
- for rowid in bestmatches[i]:
- for m in range(len(rows[rowid])):
- avgs[m]+=rows[rowid][m]
- for j in range(len(avgs)):
- avgs[j]/=len(bestmatches[i])
- clusters[i]=avgs
-
- return bestmatches
-
- blognames,words,data=readfile('blogdata.txt')
- kclust=kcluster(data,k=10)
- print [blognames[r] for r in kclust[0]]
-
-
-
-
代码、数据已传至网盘: