2009-04-06 13 views
8

¿Cuál es la mejor manera de convertir una lista/tupla en un dict donde las claves son los valores distintos de la lista y los valores son las frecuencias de esos valores distintos?La mejor manera de convertir la lista de palabras en dict de frecuencia

En otras palabras:

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

(he tenido que hacer algo como los anteriores tantas veces, ¿hay algo en el lib estándar que lo hace por usted?)

EDIT:

Jacob Gabrielson señala que hay something coming in the standard lib para la rama 2,7/3,1

+0

Quizás defina lo que quiere decir con mejor? ¿Más eficiente? ¿Menos cantidad de código? Más fácil de entender? – Dana

Respuesta

14

tipo de

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

Por lo general, funciona muy bien.

1

tengo que compartir una interesante pero un poco ridícula manera de hacerlo que sólo ocurrió:

>>> 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) o 0) se puede escribir mejor como self.get (k, 0) –

2

Esta es una abominación, pero:

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

no puedo pensar en una razón por la que uno elegiría este método sobre S.Lott, pero si alguien lo señala, podría ser yo también. :)

+1

puntos para la astucia –

+0

Debo decir que acabo de decir esto y lo probé por su rendimiento (estoy considerando el conteo listas con literalmente millones de objetos) y pensé que esto tenía que ser más rápido que obtener/establecer hash-maps repetidamente ... Pero resulta que esto toma 4 veces más tiempo de CPU para mis pruebas cuando tiene que ordenar la lista, o 2x cuando la lista ya está ordenada Interesante. Sin embargo, es muy inteligente. – iAdjunct

+0

Si maneja millones de objetos, es mejor que utilice una clasificación externa de todos modos (o descargue la clasificación al motor de datos de donde proviene su entrada, si es posible). El 'sort words.txt | uniq -c' castaño con cáscara es difícil de superar. –

22

me parece que la más fácil de entender (aunque podría no ser la más eficiente) forma es hacerlo:

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

+1: Tengo que conseguir algo de ese azúcar sintáctico de Python 3.0. –

+0

Eso está bastante caliente –

+0

¡Hermosa Python! –

7

Sólo una nota de que, a partir de Python 2.7/3.1, esta funcionalidad será incorporado en el módulo collections, consulte this bug para obtener más información. Aquí está el ejemplo 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

parece incluso más simple que eso, parece que puede pasar la cadena al constructor del contador y lo hace por usted –

+2

Simplemente puede hacer 'Contador (lista_de_la_labra)'. –

1

decidí seguir adelante y probar las versiones sugirieron, he encontrado el collections.Counter como se sugiere por Jacob Gabrielson ser el más rápido, seguido de la versión defaultdict por Slott.

Éstos son mis códigos: de colecciones importar defaultdict de colecciones importar Contador

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

Y mis resultados:

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

Do quiero saber cómo puedo mejorar el análisis.

Cuestiones relacionadas