2009-04-06 7 views
8

Quelle est la meilleure façon de convertir une liste/un tuple en dict où les clés sont les valeurs distinctes de la liste et les valeurs sont les fréquences de ces valeurs distinctes?Meilleure façon de transformer la liste de mots en dict dict

En d'autres termes:

['a', 'b', 'b', 'a', 'b', 'c'] 
--> 
{'a': 2, 'b': 3, 'c': 1} 

(j'ai dû faire quelque chose comme ci-dessus tant de fois, il n'y a rien dans le répertoire lib standard qui le fait pour vous?)

EDIT:

Jacob Gabrielson indique il est something coming in the standard lib pour la 2.7/3.1 branche

+0

Peut-être définir ce que vous voulez dire par le mieux? Le plus efficace? La moindre quantité de code? Plus facile à comprendre? – Dana

Répondre

14

Type de

from collections import defaultdict 
fq= defaultdict(int) 
for w in words: 
    fq[w] += 1 

Cela fonctionne habituellement bien.

1

je dois partager une intéressante mais un peu ridicule façon de le faire que je viens avec:

>>> class myfreq(dict): 
...  def __init__(self, arr): 
...   for k in arr: 
...    self[k] = 1 
...  def __setitem__(self, k, v): 
...   dict.__setitem__(self, k, self.get(k, 0) + v) 
... 
>>> myfreq(['a', 'b', 'b', 'a', 'b', 'c']) 
{'a': 2, 'c': 1, 'b': 3} 
+0

(self.get (k) ou 0) peut être mieux écrit comme self.get (k, 0) –

2

C'est une abomination, mais:

from itertools import groupby 
dict((k, len(list(xs))) for k, xs in groupby(sorted(items))) 

Je ne peux pas Pensez à une raison pour laquelle on choisirait cette méthode plutôt que celle de S.Lott, mais si quelqu'un va la signaler, ça pourrait aussi bien être moi. :)

+1

points pour l'intelligence –

+0

Je dois dire que je dis juste cela et l'a testé pour la performance (Je regarde le comptage liste avec littéralement des millions d'objets) et a supposé que cela devait être plus rapide que d'obtenir/définir des hash-maps à plusieurs reprises ... Mais il s'avère que cela prend 4 fois plus de temps CPU pour mes tests quand il faut trier la liste lorsque la liste est déjà triée Intéressant. C'est très intelligent cependant. – iAdjunct

+0

Si vous gérez des millions d'objets, il vaut mieux utiliser un tri externe (ou décharger le tri vers le moteur de données d'où provient votre entrée, si possible). Le 'sort words.txt | uniq -c' châtaigne dans la coquille est difficile à battre. –

22

je trouve que le plus facile à comprendre (tout pourrait ne pas être le plus efficace) façon est de faire:

{i:words.count(i) for i in set(words)} 
+2

+1: Je dois me procurer du sucre syntaxique Python 3.0. –

+0

C'est vraiment chaud –

+0

Beautiful Python! –

7

Juste une note, en commençant par Python 2.7/3.1, cette fonctionnalité sera intégré au module collections, voir this bug pour plus d'informations. Voici l'exemple de la release notes:

>>> from collections import Counter 
>>> c=Counter() 
>>> for letter in 'here is a sample of english text': 
... c[letter] += 1 
... 
>>> c 
Counter({' ': 6, 'e': 5, 's': 3, 'a': 2, 'i': 2, 'h': 2, 
'l': 2, 't': 2, 'g': 1, 'f': 1, 'm': 1, 'o': 1, 'n': 1, 
'p': 1, 'r': 1, 'x': 1}) 
>>> c['e'] 
5 
>>> c['z'] 
0 
+2

semble encore plus simple que cela, on dirait que vous pouvez simplement passer la chaîne au constructeur du compteur et il le fait pour vous –

+2

Vous pouvez simplement faire 'Counter (liste_mots)'. –

1

j'ai décidé d'aller de l'avant et de tester les versions proposées, j'ai trouvé le collections.Counter comme suggéré par Jacob Gabrielson être le plus rapide, suivi de la version defaultdict par Slott.

Voici mes codes: des collections importer defaultdict de collections importer Compteur

import random 

# using default dict 
def counter_default_dict(list): 
    count=defaultdict(int) 
    for i in list: 
     count[i]+=1 
    return count 

# using normal dict 
def counter_dict(list): 
    count={} 
    for i in list: 
     count.update({i:count.get(i,0)+1}) 
    return count 

# using count and dict 
def counter_count(list): 
    count={i:list.count(i) for i in set(list)} 
    return count 

# using count and dict 
def counter_counter(list): 
    count = Counter(list) 
    return count 

list=sorted([random.randint(0,250) for i in range(300)]) 


if __name__=='__main__': 
    from timeit import timeit 
    print("collections.Defaultdict ",timeit("counter_default_dict(list)", setup="from __main__ import counter_default_dict,list", number=1000)) 
    print("Dict",timeit("counter_dict(list)",setup="from __main__ import counter_dict,list",number=1000)) 
    print("list.count ",timeit("counter_count(list)", setup="from __main__ import counter_count,list", number=1000)) 
    print("collections.Counter.count "timeit("counter_counter(list)", setup="from __main__ import counter_counter,list", number=1000)) 

Et mes résultats:

collections.Defaultdict 
0.06787874956330614 
Dict 
0.15979115872995675 
list.count 
1.199258431219126 
collections.Counter.count 
0.025896202538920665 

Do me faire savoir comment je peux améliorer l'analyse.

Questions connexes