【问题标题】:Python list sort by size of groupPython列表按组大小排序
【发布时间】:2013-06-21 13:16:25
【问题描述】:

我有一组标记为item_labels = [('a', 3), ('b', 2), ('c', 1), ('d', 3), ('e', 2), ('f', 3)]的项目

我想按组的大小对它们进行排序。例如,在上例中,标签 3 的尺寸为 3,标签 2 的尺寸为 2。

我尝试使用groupbysorted 的组合,但没有奏效。

In [162]: sil = sorted(item_labels, key=op.itemgetter(1))

In [163]: sil
Out[163]: [('c', 1), ('b', 2), ('e', 2), ('a', 3), ('d', 3), ('f', 3)]

In [164]: g = itt.groupby(sil,)
Display all 465 possibilities? (y or n)

In [164]: g = itt.groupby(sil, key=op.itemgetter(1))

In [165]: for k, v in g:
   .....:     print k, list(v)
   .....:
   .....:
1 [('c', 1)]
2 [('b', 2), ('e', 2)]
3 [('a', 3), ('d', 3), ('f', 3)]

In [166]: sg = sorted(g, key=lambda x: len(list(x[1])))

In [167]: sg
Out[167]: [] # not exactly know why I got an empty list here

我总是可以编写一些乏味的 for 循环来做到这一点,但我宁愿找到更优雅的东西。有什么建议吗?如果有有用的库,我很乐意使用它。例如,pandasscipy

【问题讨论】:

    标签: python python-2.6 itertools sorted


    【解决方案1】:

    在python2.7及以上,使用Counter:

    from collections import Counter
    c = Counter(y for _, y in item_labels)
    item_labels.sort(key=lambda t : c[t[1]])
    

    在python2.6中,为了我们的目的,这个Counter构造函数可以使用defaultdict(如@perreal所建议的)这样实现:

    from collections import defaultdict
    def Counter(x):
        d = defaultdict(int)
        for v in x: d[v]+=1
        return d
    

    由于我们只使用数字,并且假设数字与您的示例中的数字一样低,我们实际上可以使用列表(它将与更旧版本的 Python 兼容):

    def Counter(x):
        lst = list(x)
        d = [0] * (max(lst)+1)
        for v in lst: d[v]+=1
        return d
    

    没有计数器,你可以简单地这样做:

    item_labels.sort(key=lambda t : len([x[1] for x in item_labels if x[1]==t[1] ]))
    

    速度较慢,但​​在短名单上是合理的。


    你有一个空列表的原因是g 是一个生成器。您只能对其进行一次迭代。

    【讨论】:

    • 不幸的是我使用的是python 2.6所以不能真正使用Counter
    • 谢谢。这行item_labels.sort(key=lambda t : c[t[0]]) 应该是item_labels.sort(key=lambda t : c[t[1]])
    【解决方案2】:
    from collections import defaultdict
    import operator
    l=[('c', 1), ('b', 2), ('e', 2), ('a', 3), ('d', 3), ('f', 3)]
    d=defaultdict(int)
    for p in l: d[p[1]] += 1
    print [ p for i in sorted(d.iteritems(), key=operator.itemgetter(1))
            for p in l if p[1] == i[1] ]
    

    【讨论】:

    • 您正在使用defaultdict 有效地实现Counter
    • @Elazar:在许多情况下,这实际上比默认字典更快。试试吧。 +1
    • @drewk: defaultdict can be faster than Counter 虽然在这种情况下并不重要
    【解决方案3】:

    itertools.groupby 返回一个迭代器,所以这个 for 循环:for k, v in g: 实际上消耗了那个迭代器。

    >>> it = iter([1,2,3])
    >>> for x in it:pass
    >>> list(it)          #iterator already consumed by the for-loop
    []
    

    代码:

    >>> lis = [('a', 3), ('b', 2), ('c', 1), ('d', 3), ('e', 2), ('f', 3)]
    >>> from operator import itemgetter
    >>> from itertools import groupby
    >>> lis.sort(key = itemgetter(1) )
    >>> new_lis = [list(v) for k,v in groupby(lis, key = itemgetter(1) )]
    >>> new_lis.sort(key = len)
    >>> new_lis
    [[('c', 1)], [('b', 2), ('e', 2)], [('a', 3), ('d', 3), ('f', 3)]]
    

    要获得扁平化列表,请使用itertools.chain:

    >>> from itertools import chain
    >>> list( chain.from_iterable(new_lis))
    [('c', 1), ('b', 2), ('e', 2), ('a', 3), ('d', 3), ('f', 3)]
    

    【讨论】:

      【解决方案4】:

      @perreal's@Elazar's 答案相同,但名称更好:

      from collections import defaultdict
      
      size = defaultdict(int)
      for _, group_id in item_labels:
         size[group_id] += 1
      
      item_labels.sort(key=lambda (_, group_id): size[group_id])
      print item_labels
      # -> [('c', 1), ('b', 2), ('e', 2), ('a', 3), ('d', 3), ('f', 3)]
      

      【讨论】:

        【解决方案5】:

        这是另一种方式:

        example=[('a', 3), ('b', 2), ('c', 1), ('d', 3), ('e', 2), ('f', 3)]
        
        out={}
        for t in example:
            out.setdefault(t[1],[]).append(t)
        
        print sorted(out.values(),key=len)
        

        打印:

        [[('c', 1)], [('b', 2), ('e', 2)], [('a', 3), ('d', 3), ('f', 3)]]
        

        如果你想要一个平面列表:

        print [l for s in sorted(out.values(),key=len) for l in s]
        [('c', 1), ('b', 2), ('e', 2), ('a', 3), ('d', 3), ('f', 3)]
        

        【讨论】:

          猜你喜欢
          • 2010-12-25
          • 2013-01-12
          • 1970-01-01
          • 2014-03-11
          • 2012-08-05
          • 2019-03-11
          • 1970-01-01
          • 2018-11-17
          • 2013-06-28
          相关资源
          最近更新 更多