2010-08-09 22 views
7

Pour illustrer, je commence par une liste de 2-tuples:Comment utiliser itertools.groupby lorsque la valeur de clé est dans les éléments de l'itérable?

import itertools 
import operator 

raw = [(1, "one"), 
     (2, "two"), 
     (1, "one"), 
     (3, "three"), 
     (2, "two")] 

for key, grp in itertools.groupby(raw, key=lambda item: item[0]): 
    print key, list(grp).pop()[1] 

rendements:

1 one 
2 two 
1 one 
3 three 
2 two 

Pour tenter de déterminer pourquoi:

for key, grp in itertools.groupby(raw, key=lambda item: item[0]): 
    print key, list(grp) 

# ---- OUTPUT ---- 
1 [(1, 'one')] 
2 [(2, 'two')] 
1 [(1, 'one')] 
3 [(3, 'three')] 
2 [(2, 'two')] 

Même cela me donnera la même sortie:

for key, grp in itertools.groupby(raw, key=operator.itemgetter(0)): 
    print key, list(grp) 

Je veux obtenir quelque chose comme:

1 one, one 
2 two, two 
3 three 

Je pense que c'est parce que la clé est dans le tuple dans la liste, alors que le tuple est déplacé autour comme un. Y a-t-il un moyen d'atteindre la sortie désirée? Peut-être que groupby() ne convient pas à cette tâche?

Répondre

9

groupby clusters consécutifs éléments de l'itérable qui ont la même clé. Pour produire la sortie désirée, vous devez d'abord trier raw.

for key, grp in itertools.groupby(sorted(raw), key=operator.itemgetter(0)): 
    print key, map(operator.itemgetter(1), grp) 

# 1 ['one', 'one'] 
# 2 ['two', 'two'] 
# 3 ['three'] 
+0

Je pensais que 'grp' est un objet' itertool._grouper'. Quels autres types d'actions 'builtin' puis-je faire avec un _grouper'? Je vois que vous l'avez traité comme un itérable? Soigné! – Kit

+0

@Kit: Je crois que le principal fait utile à propos de 'grp' est que c'est un' itérable '. Jusqu'à ce que vous le mentionniez, je ne savais pas que c'était un objet 'itertools._grouper'. Cela semble être un bon exemple de la commodité de la dactylographie. Nous n'avons pas besoin de connaître le type de 'grp', seulement qu'il implémente l'interface' iterable'. – unutbu

+0

+1 pour le 'itemgetter' – Krastanov

2

De l'docs:

Le fonctionnement de groupby() est similaire au filtre uniq sur Unix. Il génère une pause ou un nouveau groupe chaque temps la valeur de la fonction clé change (c'est pourquoi il est généralement nécessaire pour avoir trié les données en utilisant la même fonction clé). Ce comportement diffère de SQL GROUP BY qui agrège les éléments communs indépendamment de leur ordre d'entrée.

Puisque vous triez les tuples lexicographique de toute façon, vous pouvez simplement appeler sorted:

for key, grp in itertools.groupby(sorted(raw), key = operator.itemgetter(0)): 
    print(key, list(map(operator.itemgetter(1), list(grp)))) 
+6

Enlever les espaces autour des parenthèses me ferait sentir chaud et flou à l'intérieur;) –

+1

Je suis un croyant dans \ t \ n \ n, le Seigneur des espaces blancs.Il me dit que PEP-8 est faux, et le monde a besoin de plus d'espace! – katrielalex

6

Je pense que d'une manière plus propre pour obtenir le résultat souhaité est la suivante.

>>> from collections import defaultdict 
>>> d=defaultdict(list) 
>>> for k,v in raw: 
... d[k].append(v) 
... 
>>> for k,v in sorted(d.items()): 
... print k, v 
... 
1 ['one', 'one'] 
2 ['two', 'two'] 
3 ['three'] 

construction d est O (n), et maintenant sorted() est un peu plus les clés uniques au lieu de l'ensemble des données