“集体智慧编程”之第三章:带偏好条件的聚类及聚类的展示方式

本文探讨了基于偏好的聚类算法及其在用户物品偏好场景中的应用。通过对比Tanimoto系数与皮尔逊相关度,展示了不同相似度计算方法对聚类结果的影响,并通过树状图和二维图进行了直观展示。

带偏好条件的聚类


含义


第五章讲求最优解的时候,也有一个涉及偏好的对宿舍分配人员的例子。我发现这个挺喜欢讲针对偏好的情况。然而,我个人认为这个针对偏好这一说法,还是比较复杂的,所以我认为最好不要试着去理解其词语背后深刻的含义,没有深刻的含义。举例子说明就很好办

有一个网站:Zebo(www.zebo.com),允许人们列出自己有拥有的物品和希望拥有的物品。我们今天的例子就是针对其列出来的信息做一次聚类。说实话,光凭感觉就觉得这些数据非常有价值,对其研究很值得。
数据集的获得可以自己从网上抓获,也可以直接使用书中为我们附带的zebo.txt文档,在zebo.txt文档中,只有列出用户希望拥有的物品:1/0,1表示想要,0表示不想要。例子暂时没有研究自己的已经拥有的物品,实际上我没有上过zebo,要列出自己需要的物品我觉这个还比较复杂,自己的东西那么多,怎么方便列的完呢?

相似度的计算


书中认为:皮尔逊相关度非常适合前几个例子的博客数据集,因为:单词的词频对应某篇博客是有具体的几个几个的。此处对于,无论是拥有物品或者想拥有的物品,却只有两个数,有或没有:1或0,想要或者不想要:1或0.所以我们使用Tanimoto系数计算两个用户的相关度。Tanimoto系统的具体含义在推荐的那篇博客里已经说清楚了,就是交集与并集的比率。

代码如下:
[python]  view plain  copy
  1. def tanimoto(v1,v2):  
  2.     c1,c2,share=0,0,0  
  3.   
  4.   
  5.     for i in range(len(v1)):  
  6.         if v1[i]!=0:c1+=1#v1中有这个物品  
  7.         if v2[i]!=0:c2+=1  
  8.         if v1[i]!=0 and v2[i]!=0:share+=1#如果在两个集合中都有这些物品  
  9.   
  10.   
  11.     return 1.0-(float(share)/(c1+c2-share))  

对相似度计算的感悟

对应哪个计算方式好,书中的意思是在针对0/1的情况时,用Tanimoto系统好,但是其实我发现Tanimoto系数算出来的和我想象中不太一样,而又比较倾向与想象中的使用方式,当然后来向师哥证实,这确实是一种公式,只是我还没学习过。

想象中的方式是:并集乘以2/交集。但这个交集不要减去并集。
比如,用1,2...代替物品,组成集合
A用户:[1,2,3,4,5,6]
B用户:[2,4,6,7,8,9]
显然两者并集为[2,4,6],供三个元素。
两者相似度,用我的想法算出来就(3*2)/12=0.5
从集合内观察,我觉得有一半是一样的,所以就是0.5
然而如果用Tanimoto计算的话,就是3/(12-3)=0.33333333。这就是使我困惑的地方。
因为用Tanimoto计算出来的相似不是0.5,而且觉得目测就该是0.5。

此外,在我推荐那篇博客里已经证实:就算用Pearson计算出来的结果也不会差太差,也就是pearson也可以用来计算1/0这种的情况。实际上书中在在推荐那一章中对标签的处理也使用了pearson公式。我认为现在纠结这样相似度的计算意义不是特别大,因为真实的数据我还没有到手,到手之后,我们再比较不同的公式,改进不同的公式,这才是非常有必要的。


聚类展示


树状图


Tanimoto计算相似度


因为之前,我们已经写好了产生树状图的一系列代码。相当于现在我们只是调用一下之前写好的代码就可以产生结果。

调用代码:


[python]  view plain  copy
  1. wants,people,data=readfile('zebo.txt')  
  2. clust=hcluster(data,distance=tanimoto)  
  3. drawdendrogram(clust,wants,jpeg='zebo树状图.jpg')  


我们可以得到下一幅图:





我们截取其中一部分来看,会有有趣的发现:


很有意思吧,想要鞋子的想要衣服,想要psp的想要tv,想要xbox360的想要ps3。想要laptop的也想要MP3。没看懂为什么想要playstation 3想要马。

皮尔逊计算相似度

其实虽然数据部分只是0和1,其实也是可以用皮尔逊计算的,计算的代码:


[python]  view plain  copy
  1. <span style="font-family: Arial, Helvetica, sans-serif;">blognames,words,data=readfile('zebo.txt')</span>  
[python]  view plain  copy
  1. clust=hcluster(data)  
  2. drawdendrogram(clust,blognames,jpeg='zebo皮尔逊计算相似度.jpg')  


得到的大图:



分析其中一部分:


结果和tanimoto算出来的不太一样,但还是比较类似的,不过也都是很有趣的结果,比如想要frind的话也想要家人,也想要boyfriend


二维图


原理


树状图可以展示两个物品之间的相似情况和差距,比如最右边结合的必然是相似度最高的。然而,现在我们要展示一种更为方便的、直观的方式。说白了,就是把所有元素放在一张图上,然后离它近的他们的相似度就高,离的远的相似度就低。
原理:
书上管它叫多维缩放技术,使用这个技术,最终确定了元素怎么放在一张图上。

首先我们用相似度计算方式计算出各个元素之间的相似度(可以理解为博客名)。




然后我们把这几个元素随机的放在图上:如下图所示



这个几个在图上元素有距离吧?就用差平方之和算,如下图所示,



上图距离不对,我们要让它们几个符合相似度的数值,比如A元素,应该离B,近一些,离C、D远一些。所以,对应A元素而已,就有一个受力的情况,然后我们就对A进行移动,使他满足与其他几个元素的相似度的数值。

然后反反复复,就可以直到所有的点都无法移动为止。

代码

计算每个元素的坐标,代码如下:

[python]  view plain  copy
  1. #可以随意修改相似度  
  2. def scaledown(data,distance=tanimoto,rate=0.01):  
  3.    n(data)  
  4.   
  5.   
  6.     #每一对数据项之间的由相似度计算出来的正确距离  
  7.     #当然计算出来是一个二维数组,关键是为什么第一for循环就用n,第二个for循环就用(0,n)我觉得没区别吧  
  8.     realdist=[[distance(data[i],data[j]) for j in range(n)] for i in range(0,n)]  
  9.   
  10.   
  11.     outersum=0.0  
  12.   
  13.   
  14.     #随机初始化节点在二维空间中的起始位置,一共有n个节点,所有loc是一个二维坐标数组,有n个(x,y)的形式  
  15.     loc=[[random.random(),random.random()]for i in range(n)]  
  16.     #用fakedist装初始位置的时候,各个节点之间的差距  
  17.     fakedist=[[0.0 for j in range(n)] for i in range(n)]  
  18.   
  19.   
  20.     lasterror=None  
  21.     for m in range(0,1000):#最多循环1000次  
  22.         for i in range(n):  
  23.             for j in range(n):  
  24.                 #算的就是i和j之间的距离。这里是取了i节点的(x,y)和j节点的(x,y)  
  25.                 #不要被loc[i]迷惑了,其实他就是2,这里就相当于取了x坐标,然后相减再平凡,再取y坐标,相减再平方,最后都加起来。  
  26.                 fakedist[i][j]=sqrt(sum([pow(loc[i][x]-loc[j][x],2for x in range(len(loc[i]))]))  
  27.                   
  28.         #移动节点  
  29.         grad=[[0.0,0.0for i in range(n)]  
  30.           
  31.         totalerror=0  
  32.         for k in range(n):  
  33.             for j in range(n):  
  34.                 if j==k:continue  
  35.                 #误差值等于目标距离与当前距离之间的差值的百分比  
  36.                 #误差值算出来可以是负的,也可以是正的,如果是负的就会远离  
  37.                 errorterm=(fakedist[j][k]-realdist[j][k])/realdist[j][k]  
  38.                   
  39.                 #每个节点都要根据误差的多少,按比例移离或者移向其他节点  
  40.                 #[k][0]代码x坐标,[k][1]代表y坐标  
  41.                 #这一段需要数学功底比较好的人才能处理吧  
  42.                 grad[k][0]+=((loc[k][0]-loc[j][0])/fakedist[j][k])*errorterm  
  43.                 grad[k][1]+=((loc[k][1]-loc[j][1])/fakedist[j][k])*errorterm  
  44.                 #记录总的误差数  
  45.                 totalerror+=abs(errorterm)  
  46.         print totalerror  
  47.   
  48.   
  49.         #如果节点移动之后的情况变得更坏,那么就结束循环  
  50.         if lasterror and lasterror<totalerror:break  
  51.         lasterror=totalerror  
  52.         #根据rate参数和grad值相乘,移动每一个节点  
  53.         for k in range(n):  
  54.             loc[k][0]-=rate*grad[k][0]  
  55.             loc[k][1]-=rate*grad[k][1]  
  56.     return loc  

在画出这些元素的代码如下:
[python]  view plain  copy
  1. def draw2d(data,labels,jpeg='mds2d.jpg'):  
  2.     img=Image.new('RGB',(800,800),(255,255,255))  
  3.     draw=ImageDraw.Draw(img)  
  4.     for i in range(len(data)):  
  5.         x=(data[i][0]+0.5)*400#为什么画布的大小为800,为什么x,y要乘以400  
  6.         y=(data[i][1]+0.5)*400#实际上是实践得出来的,因为返回来的数都是-0.5到1.3之间,所有800的画布和乘以400比较合适,加0.5只是为了让图像尽可能的居中  
  7.         draw.text((x,y),labels[i],(0,0,0))  
  8.     img.show()  
  9.     img.save(jpeg,'JPEG')  

执行以下代码:
[python]  view plain  copy
  1. blognames,words,data=readfile('zebo.txt')  
  2. coords=scaledown(data)  
  3. print coords  
  4. draw2d(coords,blognames,jpeg='zebo二维图 Tanimoto相似度2.jpg')  


结果图


可以得到想要的结果:

我对博客数据执行了一次,对zebo数据执行了4次,两次pearson相关度计算,两次Tanimoto相关度计算。

博客数据的图:


zebo数据
共四份,两份是用Tanimoto计算,两份用pearson计算

首先是pearson计算的:
第二份

Tanimoto计算:

第二份:


针对zebo的结果,结论如下:
产生的结果和最初随机产生的位置很有关系,所以每次都一样
产生的结果有点不如预期,因为在树状图中,无论如何改,鞋子和衣服相似度总是很高的,然后在二维图中看,无论改了相似度计算方式还是多试几次,鞋子和衣服我觉得都可以远。当然是不是还有什么关键我没有察觉到。

启示


针对项目好像暂时没想到有什么帮助。不过在学个途中,突然想起了k均值聚类,我发现还是对项目很有帮助的。因为我们不是要做一个页面让用户选艺人或者是标签吗?比如一个页面出10个艺人,最多10页,那么我们就可以使用k均值聚类,要求分10类。然后每一页从每一个类中选一个。就能预防一个页面出现了同类型的艺人。

总的来说,在聚类时,我们针对不同的情况,应该多考虑几种不同的相似度计算方式。多考虑几种结果描述的方式:树状图、二维图,然后再结合项目的实际情况来选择。


全部源代码


myClustersOfPreferences


[python]  view plain  copy
  1. # -*- coding: cp936 -*-  
  2. def tanimoto(v1,v2):  
  3.     c1,c2,share=0,0,0  
  4.   
  5.     for i in range(len(v1)):  
  6.         if v1[i]!=0:c1+=1#v1中有这个物品  
  7.         if v2[i]!=0:c2+=1  
  8.         if v1[i]!=0 and v2[i]!=0:share+=1#如果在两个集合中都有这些物品  
  9.   
  10.     return 1.0-(float(share)/(c1+c2-share))  
  11. from PIL import Image,ImageDraw  
  12. # -*- coding: cp936 -*-  
  13. def readfile(filename):  
  14.     lines=[line for line in file(filename)]  
  15.   
  16.     #第一行是列标题,也就是被统计的单词是哪些  
  17.     colnames=lines[0].strip().split('\t')[1:]#之所以从1开始,是因为第0列是用来放置博客名了  
  18.     rownames=[]  
  19.     data=[]  
  20.     for line in lines[1:]:#第一列是单词,但二列开始才是对不同的单词的计数  
  21.         p=line.strip().split('\t')  
  22.         #每行都是的第一列都是行名  
  23.         rownames.append(p[0])  
  24.         #剩余部分就是该行对应的数据  
  25.         data.append([float(x) for x in p[1:]])#data是一个列表,这个列表里每一个元素都是一个列表,每一列表的元素就是对应了colnames[]里面的单词  
  26.     return rownames,colnames,data  
  27.   
  28.   
  29. #图中每一个点都是一个该类的对象,而其中叶节点显然就是原始数据,而枝节点的数据主要来自其叶节点的均值。  
  30. class bicluster:  
  31.     def __init__(self,vec,left=None,right=None,distance=0.0,id=None):  
  32.         self.left=left  
  33.         self.right=right  
  34.         self.vec=vec#就是词频列表  
  35.         self.id=id  
  36.         self.distance=distance  
  37.   
  38. def hcluster(rows,distance=tanimoto):  
  39.     distances={}#每计算一对节点的距离值就会保存在这个里面,这样避免了重复计算  
  40.     currentclustid=-1  
  41.   
  42.     #最开始的聚类就是数据集中的一行一行,每一行都是一个元素  
  43.     #clust是一个列表,列表里面是一个又一个biccluster的对象  
  44.     clust=[bicluster(rows[i],id=i) for i in range(len(rows))]  
  45.   
  46.     while len(clust)>1:  
  47.         lowestpair=(0,1)#先假如说lowestpair是0和1号  
  48.         closest=distance(clust[0].vec,clust[1].vec)#同样将0和1的pearson相关度计算出来放着。  
  49.         #遍历每一对节点,找到pearson相关系数最小的  
  50.         for i in range(len(clust)):  
  51.             for j in range(i+1,len(clust)):  
  52.                 #用distances来缓存距离的计算值  
  53.                 if(clust[i].id,clust[j].id) not in distances:  
  54.                     distances[(clust[i].id,clust[j].id)]=distance(clust[i].vec,clust[j].vec)  
  55.                 d=distances[(clust[i].id,clust[j].id)]  
  56.                 if d<closest:  
  57.                     closest=d  
  58.                     lowestpair=(i,j)  
  59.         #找到这个次循环的最小一对后,产生新的枝节点。先计算出这个新的枝节点的词频  
  60.         mergevec=[(clust[lowestpair[0]].vec[i]+clust[lowestpair[1]].vec[i])/2.0 for i in range(len(clust[0].vec))]  
  61.   
  62.         #建立新的聚类  
  63.         newcluster=bicluster(mergevec,left=clust[lowestpair[0]],right=clust[lowestpair[1]],distance=closest,id=currentclustid)  
  64.   
  65.         #不在初始集合中的聚类,其id设置为负数  
  66.         currentclustid-=1  
  67.         del clust[lowestpair[1]]  
  68.         del clust[lowestpair[0]]  
  69.         clust.append(newcluster)  
  70.   
  71.     #当只有一个元素之后,就返回,这个节点相当于根节点  
  72.     return clust[0]  
  73.   
  74.   
  75.   
  76. def getheight(clust):  
  77.     #这是一个叶节点吗?若是,则高度为1  
  78.     if clust.left==None and clust.right ==None:return 1  
  79.     #否则,高度为每个分支的高度之和  
  80.     return getheight(clust.left)+getheight(clust.right)  
  81.   
  82.   
  83. def getdepth(clust):  
  84.     #一个叶节点的距离是0.0,这是因为叶节点之后就没有了,将其放在最左边也没事  
  85.     if clust.left==None and clust.right ==None:return 0  
  86.   
  87.     #而一个枝节点的距离等于左右两侧分支中距离较大的那一个  
  88.     #加上自身距离:所谓自身距离,与就是某节点与两一节点合并时候的相似度  
  89.     return max(getdepth(clust.left),getdepth(clust.right))+clust.distance  
  90.   
  91. def drawdendrogram(clust,labels,jpeg='clusters.jpg'):  
  92.     #高度和宽度  
  93.     h=getheight(clust)*20  
  94.     w=1200  
  95.     depth=getdepth(clust)  
  96.   
  97.     #我们固定了宽度,所以需要对每一个节点的横向摆放做一个缩放,而不像高度一样,每一个叶节点都分配20  
  98.     scaling=float(w-150)/depth  
  99.   
  100.     #新建一张白色的背景图片  
  101.     img=Image.new('RGB',(w,h),(255,255,255))  
  102.     draw=ImageDraw.Draw(img)  
  103.   
  104.     draw.line((0,h/2,10,h/2),fill=(255,0,0))#仅仅是画了一个起点  
  105.       
  106.     #画第一个节点  
  107.     drawnode(draw,clust,10,(h/2),scaling,labels)  
  108.     img.save(jpeg,'JPEG')  
  109.   
  110. def drawnode(draw,clust,x,y,scaling,labels):  
  111.     if clust.id<0:  
  112.         h1=getheight(clust.left)*20#两个分支的高度  
  113.         h2=getheight(clust.right)*20  
  114.   
  115.         top=y-(h1+h2)/2#如果是第一次画点的话,top居然是最高点,也就是等于0。是上面边界。针对某一个节点,其高度就是左节点的高度加右节点的高度。  
  116.         bottom=y+(h1+h2)/2#这个确实也是下边界。  
  117.   
  118.         #线的长度  
  119.         ll=clust.distance*scaling  
  120.   
  121.         #聚类到其子节点的垂直线  
  122.         draw.line((x,top+h1/2,x,bottom-h2/2),fill=(255,0,0))  
  123.   
  124.         #连接左侧节点的水平线  
  125.         draw.line((x,top+h1/2,x+ll,top+h1/2),fill=(255,0,0))  
  126.   
  127.         #连接右侧节点的水平线  
  128.         draw.line((x,bottom-h2/2,x+ll,bottom-h2/2),fill=(255,0,0))  
  129.   
  130.         #调用函数绘制左右节点  
  131.         drawnode(draw,clust.left,x+ll,top+h1/2,scaling,labels)  
  132.         drawnode(draw,clust.right,x+ll,bottom-h2/2,scaling,labels)  
  133.     else:  
  134.         #如果这是一个叶节点,则绘制节点的标签。其实现在突然觉得这种思路非常好。绘制的是标签,本题中绘制的博客名字  
  135.         draw.text((x+5,y-7),labels[clust.id],(0,0,0))  
  136.   
  137. wants,people,data=readfile('zebo.txt')  
  138. clust=hcluster(data,distance=tanimoto)  
  139. drawdendrogram(clust,wants,jpeg='zebo树状图.jpg')  

MyViewDataInTwoDimensions

[python]  view plain  copy
  1. # -*- coding: cp936 -*-  
  2. from PIL import Image,ImageDraw  
  3. import random  
  4. # -*- coding: cp936 -*-  
  5. def readfile(filename):  
  6.     lines=[line for line in file(filename)]  
  7.   
  8.     #第一行是列标题,也就是被统计的单词是哪些  
  9.     colnames=lines[0].strip().split('\t')[1:]#之所以从1开始,是因为第0列是用来放置博客名了  
  10.     rownames=[]  
  11.     data=[]  
  12.     for line in lines[1:]:#第一列是单词,但二列开始才是对不同的单词的计数  
  13.         p=line.strip().split('\t')  
  14.         #每行都是的第一列都是行名  
  15.         rownames.append(p[0])  
  16.         #剩余部分就是该行对应的数据  
  17.         data.append([float(x) for x in p[1:]])#data是一个列表,这个列表里每一个元素都是一个列表,每一列表的元素就是对应了colnames[]里面的单词  
  18.     return rownames,colnames,data  
  19.   
  20.   
  21. from math import sqrt  
  22. def pearson(v1,v2):  
  23.     #先求和  
  24.     sum1=sum(v1)  
  25.     sum2=sum(v2)  
  26.   
  27.     #求平方和  
  28.     sum1Sq=sum([pow(v,2for v in v1])  
  29.     sum2Sq=sum([pow(v,2for v in v2])  
  30.   
  31.     #求乘积之和  
  32.     pSum=sum([v1[i]*v2[i] for i in range(len(v1))])  
  33.   
  34.     #计算pearson相关系数  
  35.     num=pSum-(sum1*sum2/len(v1))  
  36.     den=sqrt((sum1Sq-pow(sum1,2)/len(v1))*(sum2Sq-pow(sum2,2)/len(v1)))  
  37.     if den==0:return 0  
  38.   
  39.     return 1.0-num/den#因为在本题中,我们想要相似度也大的两个元素的距离越近,所以才用1去减它们  
  40.   
  41. def tanimoto(v1,v2):  
  42.     c1,c2,share=0,0,0  
  43.   
  44.     for i in range(len(v1)):  
  45.         if v1[i]!=0:c1+=1#v1中有这个物品  
  46.         if v2[i]!=0:c2+=1  
  47.         if v1[i]!=0 and v2[i]!=0:share+=1#如果在两个集合中都有这些物品  
  48.   
  49.     return 1.0-(float(share)/(c1+c2-share))  
  50.           
  51. def scaledown(data,distance=tanimoto,rate=0.01):  
  52.     n=len(data)  
  53.   
  54.     #每一对数据项之间的由相似度计算出来的正确距离  
  55.     #当然计算出来是一个二维数组,关键是为什么第一for循环就用n,第二个for循环就用(0,n)我觉得没区别吧  
  56.     realdist=[[distance(data[i],data[j]) for j in range(n)] for i in range(0,n)]  
  57.   
  58.     outersum=0.0  
  59.   
  60.     #随机初始化节点在二维空间中的起始位置,一共有n个节点,所有loc是一个二维坐标数组,有n个(x,y)的形式  
  61.     loc=[[random.random(),random.random()]for i in range(n)]  
  62.     #用fakedist装初始位置的时候,各个节点之间的差距  
  63.     fakedist=[[0.0 for j in range(n)] for i in range(n)]  
  64.   
  65.     lasterror=None  
  66.     for m in range(0,1000):#最多循环1000次  
  67.         for i in range(n):  
  68.             for j in range(n):  
  69.                 #算的就是i和j之间的距离。这里是取了i节点的(x,y)和j节点的(x,y)  
  70.                 #不要被loc[i]迷惑了,其实他就是2,这里就相当于取了x坐标,然后相减再平凡,再取y坐标,相减再平方,最后都加起来。  
  71.                 fakedist[i][j]=sqrt(sum([pow(loc[i][x]-loc[j][x],2for x in range(len(loc[i]))]))  
  72.                   
  73.         #移动节点  
  74.         grad=[[0.0,0.0for i in range(n)]  
  75.           
  76.         totalerror=0  
  77.         for k in range(n):  
  78.             for j in range(n):  
  79.                 if j==k:continue  
  80.                 #误差值等于目标距离与当前距离之间的差值的百分比  
  81.                 #误差值算出来可以是负的,也可以是正的,如果是负的就会远离  
  82.                 errorterm=(fakedist[j][k]-realdist[j][k])/realdist[j][k]  
  83.                   
  84.                 #每个节点都要根据误差的多少,按比例移离或者移向其他节点  
  85.                 #[k][0]代码x坐标,[k][1]代表y坐标  
  86.                 #这一段需要数学功底比较好的人才能处理吧  
  87.                 grad[k][0]+=((loc[k][0]-loc[j][0])/fakedist[j][k])*errorterm  
  88.                 grad[k][1]+=((loc[k][1]-loc[j][1])/fakedist[j][k])*errorterm  
  89.                 #记录总的误差数  
  90.                 totalerror+=abs(errorterm)  
  91.         print totalerror  
  92.   
  93.         #如果节点移动之后的情况变得更坏,那么就结束循环  
  94.         if lasterror and lasterror<totalerror:break  
  95.         lasterror=totalerror  
  96.         #根据rate参数和grad值相乘,移动每一个节点  
  97.         for k in range(n):  
  98.             loc[k][0]-=rate*grad[k][0]  
  99.             loc[k][1]-=rate*grad[k][1]  
  100.     return loc  
  101.   
  102. def draw2d(data,labels,jpeg='mds2d.jpg'):  
  103.     img=Image.new('RGB',(800,800),(255,255,255))  
  104.     draw=ImageDraw.Draw(img)  
  105.     for i in range(len(data)):  
  106.         x=(data[i][0]+0.5)*400#为什么画布的大小为2000,为什么x,y要乘以1000  
  107.         y=(data[i][1]+0.5)*400#实际上是实践得出来的,因为返回来的数都是-0.5到1.3之间,所有2000的画布和乘以1000比较合适,加0.5只是为了让图像尽可能的居中  
  108.         draw.text((x,y),labels[i],(0,0,0))  
  109.     img.show()  
  110.     img.save(jpeg,'JPEG')  
  111.           
  112.   
  113.   
  114. blognames,words,data=readfile('zebo.txt')  
  115. coords=scaledown(data)  
  116. print coords  
  117. draw2d(coords,blognames,jpeg='zebo二维图 Tanimoto相似度2.jpg')  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值