147 votes

Python groupe par

Supposons que j'ai un tel ensemble de paire de données où l'indice 0 est la valeur et l'indice 1 est du type:

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

Je veux les regrouper selon leur type(par la 1ère chaîne indexée) en tant que tel:

result = [ 
           { 
             type:'KAT', 
             items: ['11013331', '9843236'] 
           },
           {
             type:'NOT', 
             items: ['9085267', '11788544'] 
           },
           {
             type:'ETH', 
             items: ['5238761', '962142', '7795297', '7341464', '5594916', '1550003'] 
           }
         ] 

Comment puis-je y parvenir d'une manière efficace?

Merci

190voto

KennyTM Points 232647

Le faire en 2 étapes. Tout d'abord, créez un dictionnaire.

>>> 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)
...

Puis, de convertir ce dictionnaire dans le format attendu.

>>> [{'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'}]

Il est également possible avec itertools.groupby, mais il nécessite la saisie d'être triés en premier lieu.

>>> 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'}]

Remarque ces deux ne respectent pas l'ordre original des touches. Vous avez besoin d'un OrderedDict si vous avez besoin de garder l'ordre.

>>> 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'}]

62voto

Paul McGuire Points 24790

Python intégré dans l' itertools module a fait un groupby de la fonction que vous pourriez utiliser, mais les éléments regroupés doit d'abord être triés, tels que les éléments à être regroupés sont contigus dans la liste:

sortkeyfn = key=lambda s:s[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)

Maintenant entrée ressemble à:

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

groupby retourne une séquence de 2-tuples de la forme (key, values_iterator). Ce que nous voulons, c'est de transformer cela en une liste des dicts où le "type" est la clé, et les "objets") est une liste de le 0 ième éléments de la n-uplets renvoyé par la values_iterator. Comme ceci:

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)))

Maintenant, result contient votre choix dict, comme indiqué dans votre question.

Vous pourriez penser, cependant, faire une seule dict, gérés par type et chaque valeur contenant la liste des valeurs. Dans votre formulaire, pour trouver les valeurs d'un type particulier, vous aurez pour parcourir la liste pour trouver le dict contenant la correspondance de "type" de la clé, puis obtenir le 'items' élément. Si vous utilisez un seul dict au lieu d'une liste de 1-point dicts, vous pouvez trouver les éléments d'un type particulier avec une clé unique de recherche à la maîtrise, la dict. À l'aide de groupby, cela ressemblerait à:

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

result contient maintenant ce dict (ceci est similaire à l'intermédiaire res defaultdict dans @KennyTM de la réponse):

{'NOT': ['9085267', '11788544'], 
 'ETH': ['5238761', '5349618', '962142', '7795297', '7341464', '5594916', '1550003'], 
 'KAT': ['11013331', '9843236']}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X