Python group by

Hel*_*nar 110 python group-by

假设我有一组数据对,其中索引0是值,索引1是类型:

input = [
          ('11013331', 'KAT'), 
          ('9085267',  'NOT'), 
          ('5238761',  'ETH'), 
          ('5349618',  'ETH'), 
          ('11788544', 'NOT'), 
          ('962142',   'ETH'), 
          ('7795297',  'ETH'), 
          ('7341464',  'ETH'), 
          ('9843236',  'KAT'), 
          ('5594916',  'ETH'), 
          ('1550003',  'ETH')
        ]
Run Code Online (Sandbox Code Playgroud)

我想按类型(按第一个索引字符串)对它们进行分组:

result = [ 
           { 
             type:'KAT', 
             items: ['11013331', '9843236'] 
           },
           {
             type:'NOT', 
             items: ['9085267', '11788544'] 
           },
           {
             type:'ETH', 
             items: ['5238761', '962142', '7795297', '7341464', '5594916', '1550003'] 
           }
         ] 
Run Code Online (Sandbox Code Playgroud)

我怎样才能以有效的方式实现这一目标?

ken*_*ytm 140

分2步完成.首先,创建一个字典.

>>> input = [('11013331', 'KAT'), ('9085267', 'NOT'), ('5238761', 'ETH'), ('5349618', 'ETH'), ('11788544', 'NOT'), ('962142', 'ETH'), ('7795297', 'ETH'), ('7341464', 'ETH'), ('9843236', 'KAT'), ('5594916', 'ETH'), ('1550003', 'ETH')]
>>> from collections import defaultdict
>>> res = defaultdict(list)
>>> for v, k in input: res[k].append(v)
...
Run Code Online (Sandbox Code Playgroud)

然后,将该字典转换为预期的格式.

>>> [{'type':k, 'items':v} for k,v in res.items()]
[{'items': ['9085267', '11788544'], 'type': 'NOT'}, {'items': ['5238761', '5349618', '962142', '7795297', '7341464', '5594916', '1550003'], 'type': 'ETH'}, {'items': ['11013331', '9843236'], 'type': 'KAT'}]
Run Code Online (Sandbox Code Playgroud)

使用itertools.groupby也可以,但它需要先输入输入.

>>> sorted_input = sorted(input, key=itemgetter(1))
>>> groups = groupby(sorted_input, key=itemgetter(1))
>>> [{'type':k, 'items':[x[0] for x in v]} for k, v in groups]
[{'items': ['5238761', '5349618', '962142', '7795297', '7341464', '5594916', '1550003'], 'type': 'ETH'}, {'items': ['11013331', '9843236'], 'type': 'KAT'}, {'items': ['9085267', '11788544'], 'type': 'NOT'}]
Run Code Online (Sandbox Code Playgroud)

请注意,这两者都不符合密钥的原始顺序.如果您需要保留订单,则需要OrderedDict.

>>> from collections import OrderedDict
>>> res = OrderedDict()
>>> for v, k in input:
...   if k in res: res[k].append(v)
...   else: res[k] = [v]
... 
>>> [{'type':k, 'items':v} for k,v in res.items()]
[{'items': ['11013331', '9843236'], 'type': 'KAT'}, {'items': ['9085267', '11788544'], 'type': 'NOT'}, {'items': ['5238761', '5349618', '962142', '7795297', '7341464', '5594916', '1550003'], 'type': 'ETH'}]
Run Code Online (Sandbox Code Playgroud)

  • 步骤 1 可以在没有导入的情况下完成:`d= {}; 对于输入中的 k,v: d.setdefault(k, []).append(v)` (4认同)
  • `从操作​​符导入 itemgetter` (2认同)

Pau*_*McG 51

Python的内置itertools模块实际上有一个groupby函数,但为此必须首先对要分组的元素进行排序,使得要分组的元素在列表中是连续的:

from operator import itemgetter
sortkeyfn = itemgetter(1)
input = [('11013331', 'KAT'), ('9085267', 'NOT'), ('5238761', 'ETH'), 
 ('5349618', 'ETH'), ('11788544', 'NOT'), ('962142', 'ETH'), ('7795297', 'ETH'), 
 ('7341464', 'ETH'), ('9843236', 'KAT'), ('5594916', 'ETH'), ('1550003', 'ETH')] 
input.sort(key=sortkeyfn)
Run Code Online (Sandbox Code Playgroud)

现在输入如下:

[('5238761', 'ETH'), ('5349618', 'ETH'), ('962142', 'ETH'), ('7795297', 'ETH'),
 ('7341464', 'ETH'), ('5594916', 'ETH'), ('1550003', 'ETH'), ('11013331', 'KAT'),
 ('9843236', 'KAT'), ('9085267', 'NOT'), ('11788544', 'NOT')]
Run Code Online (Sandbox Code Playgroud)

groupby返回表单的2元组序列(key, values_iterator).我们想要的是将它变成一个dicts列表,其中'type'是键,'items'是values_iterator返回的元组的第0个元素的列表.像这样:

from itertools import groupby
result = []
for key,valuesiter in groupby(input, key=sortkeyfn):
    result.append(dict(type=key, items=list(v[0] for v in valuesiter)))
Run Code Online (Sandbox Code Playgroud)

现在result包含您所需的词典,如您的问题中所述.

但是,您可能会考虑使用单个dict,按类型键入,每个值包含值列表.在当前形式中,要查找特定类型的值,您必须遍历列表以查找包含匹配的"类型"键的dict,然后从中获取"items"元素.如果您使用单个dict而不是单项dicts列表,则可以使用单个键入查找到主dict中查找特定类型的项目.使用groupby,这看起来像:

result = {}
for key,valuesiter in groupby(input, key=sortkeyfn):
    result[key] = list(v[0] for v in valuesiter)
Run Code Online (Sandbox Code Playgroud)

result现在包含这个dict(这类似于res@ KennyTM的答案中的中间默认指令):

{'NOT': ['9085267', '11788544'], 
 'ETH': ['5238761', '5349618', '962142', '7795297', '7341464', '5594916', '1550003'], 
 'KAT': ['11013331', '9843236']}
Run Code Online (Sandbox Code Playgroud)

(如果您想将其减少为单行,您可以:

result = dict((key,list(v[0] for v in valuesiter)
              for key,valuesiter in groupby(input, key=sortkeyfn))
Run Code Online (Sandbox Code Playgroud)

或使用新奇的字典理解形式:

result = {key:list(v[0] for v in valuesiter)
              for key,valuesiter in groupby(input, key=sortkeyfn)}
Run Code Online (Sandbox Code Playgroud)


aki*_*iva 7

我也喜欢熊猫简单分组。它功能强大,简单,最适合大数据集

result = pandas.DataFrame(input).groupby(1).groups


ron*_*nen 7

这个答案类似于@PaulMcG 的答案,但不需要对输入进行排序。

对于那些进入函数式编程的人,groupBy可以写在一行中(不包括导入!),不像itertools.groupby它不需要对输入进行排序:

from functools import reduce # import needed for python3; builtin in python2
from collections import defaultdict

def groupBy(key, seq):
 return reduce(lambda grp, val: grp[key(val)].append(val) or grp, seq, defaultdict(list))
Run Code Online (Sandbox Code Playgroud)

(原因... or grplambda是,为了这个reduce()工作中,lambda需要返回它的第一个参数,因为list.append()总是返回Noneor总是会返回grp。也就是说,它是一个黑客绕过Python的限制,即在拉姆达只能计算一个表达式。)

这将返回一个字典,其键是通过评估给定函数找到的,其值是原始顺序中原始项目的列表。对于 OP 的示例,调用 asgroupBy(lambda pair: pair[1], input)将返回此字典:

{'KAT': [('11013331', 'KAT'), ('9843236', 'KAT')],
 'NOT': [('9085267', 'NOT'), ('11788544', 'NOT')],
 'ETH': [('5238761', 'ETH'), ('5349618', 'ETH'), ('962142', 'ETH'), ('7795297', 'ETH'), ('7341464', 'ETH'), ('5594916', 'ETH'), ('1550003', 'ETH')]}
Run Code Online (Sandbox Code Playgroud)

根据@PaulMcG 的回答,可以通过将其包装在列表理解中来找到 OP 请求的格式。所以这会做到:

result = {key: [pair[0] for pair in values],
          for key, values in groupBy(lambda pair: pair[1], input).items()}
Run Code Online (Sandbox Code Playgroud)