2009-04-06 15 views
8

Qual è il modo migliore per convertire una lista/tupla in un dict in cui le chiavi sono i valori distinti della lista ei valori sono le frequenze di quei valori distinti?Il modo migliore per trasformare la lista delle parole in frequenza dic

In altre parole:

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

(ho dovuto fare qualcosa di simile quanto sopra tante volte, c'è qualcosa nella lib standard che fa per voi?)

EDIT:

Jacob Gabrielson sottolinea c'è something coming in the standard lib per il ramo 2,7/3,1

+0

Forse definisci cosa intendi per meglio? Più efficiente? Minima quantità di codice? Più facile da capire? – Dana

risposta

14

Tipo di

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

Questo di solito funziona bene.

1

devo condividere un interessante ma tipo di modo ridicolo di farlo che ho appena si avvicinò con:

>>> 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) può essere scritto meglio come self.get (k, 0) –

2

Questo è un abominio, ma:

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

non posso Pensa a una ragione per cui scegliere questo metodo su S. Lott, ma se qualcuno lo indicherà, potrebbe anche essere io. :)

+1

punti per l'intelligenza –

+0

Devo dire che ho appena detto questo e testato per le prestazioni (sto guardando il conteggio elenca letteralmente milioni di oggetti) e ha calcolato che doveva essere più veloce di ottenere/impostare ripetutamente hash-maps ... Ma siccome si scopre questo richiede 4 volte tanto CPU per i miei test quando deve ordinare l'elenco, o 2x quando la lista è già ordinata. Interessante. È molto intelligente però. – iAdjunct

+0

Se gestisci milioni di oggetti, è meglio usare comunque un ordinamento esterno (o scaricare l'ordinamento dal motore dati da cui proviene l'input, se possibile). Il 'sort words.txt | uniq -c' castagna in guscio è difficile da battere. –

22

trovo che il più facile da capire (mentre potrebbe non essere la più efficace) modo è quello di fare:

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

+1: Devo procurarmi un po 'di quello zucchero sintattico di Python 3.0. –

+0

Questo è abbastanza caldo –

+0

Beautiful Python! –

7

Solo una nota che, a partire da Python 2.7/3.1, questa funzionalità sarà integrato nel modulo collections, vedere this bug per ulteriori informazioni. Ecco l'esempio della 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

sembra ancora più semplice, sembra che tu possa semplicemente passare la stringa al costruttore Counter e lo fa per te. –

+2

Puoi semplicemente fare "Counter (word_list)". –

1

ho deciso di andare avanti e di provare le versioni suggerito, ho trovato il collections.Counter come suggerito da Jacob Gabrielson essere il più veloce, seguita dalla versione defaultdict da Slott.

Qui ci sono i miei codici: da collezioni importare defaultdict da collezioni importare contatore

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

E i miei risultati:

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

Do farmi sapere come posso migliorare l'analisi.

Problemi correlati