【问题标题】:Group list by values按值分组列表
【发布时间】:2011-08-07 09:39:29
【问题描述】:

假设我有一个这样的列表:

mylist = [["A",0], ["B",1], ["C",0], ["D",2], ["E",2]]

我怎样才能最优雅地将其分组以在 Python 中获得此列表输出:

[["A", "C"], ["B"], ["D", "E"]]

所以这些值按第二个值分组,但顺序被保留...

【问题讨论】:

  • list是Python中的数据类型,不建议用它作为变量名
  • 我编辑了这个问题,所以它不会影响内置的 list 关键字

标签: python list grouping


【解决方案1】:
values = set(map(lambda x:x[1], mylist))
newlist = [[y[0] for y in mylist if y[1]==x] for x in values]

【讨论】:

  • set() 不一定是排序的(尽管它用于小整数值),如果你有一个长范围使用 values = sorted(set(...
  • @sverre 毕竟不需要排序
  • 除非set没有订单。碰巧的是,对于低整数,哈希函数是恒等式。我也不确定OP是否打算同时订购(组顺序和组顺序); this 和 sverre 的示例按键对组进行排序(他也假设 0..N 连续范围)。
  • lambda x:x[1] 可以替换为 operator.itemgetter(1)
  • 组可以简单地在O(n) 中完成,但是在这段代码中,如果mylist 中的元素都是不同的,则需要花费O(n ^ 2)
【解决方案2】:
from operator import itemgetter
from itertools import groupby

lki = [["A",0], ["B",1], ["C",0], ["D",2], ["E",2]]
lki.sort(key=itemgetter(1))

glo = [[x for x,y in g]
       for k,g in  groupby(lki,key=itemgetter(1))]

print glo

.

编辑

另一个不需要 import 的解决方案,更易读,保持顺序,并且比前一个解决方案短 22 %:

oldlist = [["A",0], ["B",1], ["C",0], ["D",2], ["E",2]]

newlist, dicpos = [],{}
for val,k in oldlist:
    if k in dicpos:
        newlist[dicpos[k]].extend(val)
    else:
        newlist.append([val])
        dicpos[k] = len(dicpos)

print newlist

【讨论】:

  • +1 用于使用itemgetter。但请注意,由于您正在迭代 groupby 返回的迭代器,因此您不需要 list(g)
  • @Robert Rossney Eagle 的眼睛。 +1。顺便说一句,在您的代码中,我发现“数据”这个词太常见了,无法说明它是什么类型的数据,这很遗憾。
【解决方案3】:

霍华德的回答简洁优雅,但在最坏的情况下也是 O(n^2)。对于包含大量分组键值的大型列表,您需要先对列表进行排序,然后使用itertools.groupby

>>> from itertools import groupby
>>> from operator import itemgetter
>>> seq = [["A",0], ["B",1], ["C",0], ["D",2], ["E",2]]
>>> seq.sort(key = itemgetter(1))
>>> groups = groupby(seq, itemgetter(1))
>>> [[item[0] for item in data] for (key, data) in groups]
[['A', 'C'], ['B'], ['D', 'E']]

编辑:

在看到 eyequem 的回答后我改变了这个:itemgetter(1) is nice than lambda x: x[1].

【讨论】:

  • 但它需要导入。它真的比使用 lambda 更好吗?我想知道。无论如何,为了便于阅读,我认为 itemgetter 更好
  • 我也这么认为。此外,提醒您operator 模块的存在总是好的。
  • 我更喜欢 lambda。
  • 我认为 lambda 更好。不需要提醒稀有模块总是好的!
  • 我也更喜欢 lambda,但我认为导入的数量不是一个重要的考虑因素,因为 operator 模块是标准库的一部分。依赖关系不好,导入不是。
【解决方案4】:
>>> import collections
>>> D1 = collections.defaultdict(list)
>>> for element in L1:
...     D1[element[1]].append(element[0])
... 
>>> L2 = D1.values()
>>> print L2
[['A', 'C'], ['B'], ['D', 'E']]
>>> 

【讨论】:

    【解决方案5】:

    我不知道优雅,但它肯定是可行的:

    oldlist = [["A",0], ["B",1], ["C",0], ["D",2], ["E",2]]
    # change into: list = [["A", "C"], ["B"], ["D", "E"]]
    
    order=[]
    dic=dict()
    for value,key in oldlist:
      try:
        dic[key].append(value)
      except KeyError:
        order.append(key)
        dic[key]=[value]
    newlist=map(dic.get, order)
    
    print newlist
    

    这会保留每个键第一次出现的顺序,以及每个键的项目顺序。它要求键是可散列的,但不赋予它意义。

    【讨论】:

      【解决方案6】:
      len = max(key for (item, key) in list)
      newlist = [[] for i in range(len+1)]
      for item,key in list:
        newlist[key].append(item)
      

      您可以在单个列表推导中做到这一点,也许更优雅但 O(n**2):

      [[item for (item,key) in list if key==i] for i in range(max(key for (item,key) in list)+1)]
      

      【讨论】:

        【解决方案7】:
        >>> xs = [["A",0], ["B",1], ["C",0], ["D",2], ["E",2]]
        >>> xs.sort(key=lambda x: x[1])
        >>> reduce(lambda l, x: (l.append([x]) if l[-1][0][1] != x[1] else l[-1].append(x)) or l, xs[1:], [[xs[0]]]) if xs else []
        [[['A', 0], ['C', 0]], [['B', 1]], [['D', 2], ['E', 2]]]
        

        基本上,如果列表已排序,则可以通过查看前面步骤构建的最后一个组来reduce - 您可以判断是否需要启动一个新组,或修改现有组。 ... or l 位是一个技巧,它使我们能够在 Python 中使用 lambda。 (append 返回None。返回比None 更有用的东西总是更好,但是,唉,Python 就是这样。)

        【讨论】:

          【解决方案8】:

          如果使用convtools库,它提供了大量的数据处理原语并在后台生成临时代码,那么:

          from convtools import conversion as c
          
          my_list = [["A", 0], ["B", 1], ["C", 0], ["D", 2], ["E", 2]]
          
          # store the converter somewhere because this is where code generation
          # takes place
          converter = (
              c.group_by(c.item(1))
              .aggregate(c.ReduceFuncs.Array(c.item(0)))
              .gen_converter()
          )
          assert converter(my_list) == [["A", "C"], ["B"], ["D", "E"]]
          

          【讨论】:

            猜你喜欢
            • 2019-10-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-05-26
            • 2014-07-24
            • 2020-02-24
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多