【问题标题】:How to compare clusters?如何比较集群?
【发布时间】:2013-07-25 20:54:34
【问题描述】:

希望这可以用 python 完成!我对相同的数据使用了两个聚类程序,现在两者都有一个聚类文件。我重新格式化了文件,使它们看起来像这样:

Cluster 0:
Brucellaceae(10)
    Brucella(10)
        abortus(1)
        canis(1)
        ceti(1)
        inopinata(1)
        melitensis(1)
        microti(1)
        neotomae(1)
        ovis(1)
        pinnipedialis(1)
        suis(1)
Cluster 1:
    Streptomycetaceae(28)
        Streptomyces(28)
            achromogenes(1)
            albaduncus(1)
            anthocyanicus(1)

etc.

这些文件包含细菌种类信息。所以我有簇号(簇 0),然后在它下面的“家族”(布鲁氏菌科)和该家族中的细菌数量(10)。下面是在该科中发现的属(名称后跟编号,布鲁氏菌(10)),最后是每个属中的物种(流产(1)等)。

我的问题:我有 2 个以这种方式格式化的文件,并想编写一个程序来查找两者之间的差异。唯一的问题是两个程序以不同的方式聚类,所以两个聚类可能相同,即使实际的“聚类编号”不同(因此一个文件中的聚类 1 的内容可能与另一个文件中的聚类 43 匹配,唯一不同的是实际的簇号)。所以我需要一些东西来忽略集群编号并专注于集群内容。

有什么方法可以比较这两个文件来检查差异吗?甚至可能吗?任何想法将不胜感激!

【问题讨论】:

  • 解析类并比较该类的对象
  • 我想你可以在一个集群中有几个家庭,对吧?
  • 使用diff 仍然可能是最简单的解决方案。
  • 它们是行为和数据的容器。
  • 您是否阅读过关于聚类分析的维基百科页面?它讨论了比较两个聚类的外部评估措施。

标签: python algorithm cluster-analysis


【解决方案1】:

比较两个集群并非易事,重新发明轮子不太可能成功。看看这个包,它有很多不同的集群相似度指标,并且可以比较树状图(你拥有的数据结构)。

该库名为 CluSim,可在此处找到: https://github.com/Hoosier-Clusters/clusim/

【讨论】:

  • 请提供链接中的一些重要细节,因为链接将来很容易过期/重定向。
【解决方案2】:

从 Stackoverflow 学到了这么多,终于有机会回馈了!与目前提供的方法不同的方法是重新标记集群以最大化对齐,然后比较变得容易。例如,如果一个算法将标签分配给一组六个项目,如 L1=[0,0,1,1,2,2] 而另一种算法分配 L2=[2,2,0,0,1,1],您希望这两个标签是等价的,因为 L1 和 L2 本质上是将项目分割成相同的集群。这种方法重新标记 L2 以最大化对齐,在上面的示例中,将导致 L2==L1。

我在"Menéndez, Héctor D. A genetic approach to the graph and spectral clustering problem. MS thesis. 2012." 中找到了解决这个问题的方法,下面是使用 numpy 的 Python 实现。我对 Python 比较陌生,所以可能会有更好的实现,但我认为这可以完成工作:

def alignClusters(clstr1,clstr2):
"""Given 2 cluster assignments, this funciton will rename the second to 
   maximize alignment of elements within each cluster. This method is 
   described in in Menéndez, Héctor D. A genetic approach to the graph and 
   spectral clustering problem. MS thesis. 2012. (Assumes cluster labels
   are consecutive integers starting with zero)

   INPUTS:
   clstr1 - The first clustering assignment
   clstr2 - The second clustering assignment

   OUTPUTS:
   clstr2_temp - The second clustering assignment with clusters renumbered to
   maximize alignment with the first clustering assignment """
K = np.max(clstr1)+1
simdist = np.zeros((K,K))

for i in range(K):
    for j in range(K):
        dcix = clstr1==i
        dcjx = clstr2==j
        dd = np.dot(dcix.astype(int),dcjx.astype(int))
        simdist[i,j] = (dd/np.sum(dcix!=0) + dd/np.sum(dcjx!=0))/2
mask = np.zeros((K,K))
for i in range(K):
    simdist_vec = np.reshape(simdist.T,(K**2,1))
    I = np.argmax(simdist_vec)
    xy = np.unravel_index(I,simdist.shape,order='F')
    x = xy[0]
    y = xy[1]
    mask[x,y] = 1
    simdist[x,:] = 0
    simdist[:,y] = 0
swapIJ = np.unravel_index(np.where(mask.T),simdist.shape,order='F')
swapI = swapIJ[0][1,:]
swapJ = swapIJ[0][0,:]
clstr2_temp = np.copy(clstr2)
for k in range(swapI.shape[0]):
    swapj = [swapJ[k]==i for i in clstr2]
    clstr2_temp[swapj] = swapI[k]
return clstr2_temp

【讨论】:

    【解决方案3】:

    您必须编写一些代码来解析文件。如果忽略聚类,您应该能够根据缩进区分科、属和种。

    定义named tuple的最简单方法:

    import collections
    Bacterium = collections.namedtuple('Bacterium', ['family', 'genera', 'species'])
    

    你可以像这样创建这个对象的实例:

    b = Bacterium('Brucellaceae', 'Brucella', 'canis')
    

    您的解析器应该逐行读取文件,并设置族和属。如果它随后找到了一个物种,它应该将一个细菌添加到一个列表中;

    with open('cluster0.txt', 'r') as infile:
        lines = infile.readlines()
    family = None
    genera = None
    bacteria = []
    for line in lines:
        # set family and genera.
        # if you detect a bacterium:
        bacteria.append(Bacterium(family, genera, species))
    

    获得每个文件或集群中所有细菌的列表后,您可以从所有细菌中进行选择,如下所示:

    s = [b for b in bacteria if b.genera == 'Streptomycetaceae']
    

    【讨论】:

      【解决方案4】:

      给定:

      file1 = '''Cluster 0:
       giant(2)
        red(2)
         brick(1)
         apple(1)
      Cluster 1:
       tiny(3)
        green(1)
         dot(1)
        blue(2)
         flower(1)
         candy(1)'''.split('\n')
      file2 = '''Cluster 18:
       giant(2)
        red(2)
         brick(1)
         tomato(1)
      Cluster 19:
       tiny(2)
        blue(2)
         flower(1)
         candy(1)'''.split('\n')
      

      这是你需要的吗?

      def parse_file(open_file):
          result = []
      
          for line in open_file:
              indent_level = len(line) - len(line.lstrip())
              if indent_level == 0:
                  levels = ['','','']
              item = line.lstrip().split('(', 1)[0]
              levels[indent_level - 1] = item
              if indent_level == 3:
                  result.append('.'.join(levels))
          return result
      
      data1 = set(parse_file(file1))
      data2 = set(parse_file(file2))
      
      differences = [
          ('common elements', data1 & data2),
          ('missing from file2', data1 - data2),
          ('missing from file1', data2 - data1) ]
      

      查看差异:

      for desc, items in differences:
          print desc
          print 
          for item in items:
              print '\t' + item
          print
      

      打印

      common elements
      
          giant.red.brick
          tiny.blue.candy
          tiny.blue.flower
      
      missing from file2
      
          tiny.green.dot
          giant.red.apple
      
      missing from file1
      
          giant.red.tomato
      

      【讨论】:

      • +1 是一个很好的解决方案,尽管我自己更希望看到 OP 面临更多挑战;)
      • 感谢您发布此答案!这接近我所需要的。但问题是,当我运行它时,所有内容都显示在common elements 下,因为两个文件具有完全相同的内容,只是内容的排序方式很重要(有意义吗?)。例如,在我的第一个文件中,Cluster 4 包含细菌 Sphingomonadaceae.Blastomonas.natatoria 和 Sphingomonadaceae.Sphingomonas.ursincola。在我的第二个文件中,这两种细菌位于不同的簇中(11 和 23)。因此,我希望收到有关此更改的通知。希望这是有道理的!
      【解决方案5】:

      所以只是为了帮助,因为我在评论中看到很多不同的答案,我会给你一个非常非常简单的脚本实现,你可以从它开始。

      请注意,这并没有回答您的全部问题,而是将您指向 cmets 中的一个方向。

      通常,如果您没有经验,我会争辩说先读一读 Python(无论如何我都会这样做,我会在答案底部添加一些链接)

      继续有趣的事情! :)

      class Cluster(object):
        '''
        This is a class that will contain your information about the Clusters.
        '''
        def __init__(self, number):
          '''
          This is what some languages call a constructor, but it's not.
          This method initializes the properties with values from the method call.
          '''
          self.cluster_number = number
          self.family_name = None
          self.bacteria_name = None
          self.bacteria = []
      
      #This part below isn't a part of the class, this is the actual script.
      with open('bacteria.txt', 'r') as file:
        cluster = None
        clusters = []
        for index, line in enumerate(file):
          if line.startswith('Cluster'):
            cluster = Cluster(index)
            clusters.append(cluster)
          else:
            if not cluster.family_name:
              cluster.family_name = line
            elif not cluster.bacteria_name:
              cluster.bacteria_name = line
            else:
              cluster.bacteria.append(line)
      

      我在没有任何花哨的东西和 Python 2.7.2 的情况下尽可能地愚蠢和过于简单 例如,您可以将此文件复制到 .py 文件中并直接从命令行 python bacteria.py 运行它。

      希望这对您有所帮助,如果您有任何问题,请随时来我们的 Python 聊天室! :)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-02-22
        • 1970-01-01
        • 2020-12-14
        • 2020-03-09
        • 2021-12-16
        • 2013-05-17
        • 2018-08-04
        • 2020-09-16
        相关资源
        最近更新 更多