2010-10-27 8 views
8

Sto elaborando un file CSV e contando i valori univoci della colonna 4. Finora ho codificato questi tre modi. Uno usa "if key in dictionary", il secondo intercetta il KeyError e il terzo usa "DefaultDictionary". Per esempio (dove x [3] è il valore dal file e "a" è un dizionario):Aggiungere nuove chiavi a un dizionario mentre si incrementano i valori esistenti

Primo modo:

if x[3] in a: 
    a[x[3]] += 1 
else: 
    a[x[3]] = 1 

Secondo modo:

try: 
    b[x[3]] += 1 
except KeyError: 
    b[x[3]] = 1 

Terzo modo:

from collections import defaultdict 
c = defaultdict(int) 
c[x[3]] += 1 

la mia domanda è: in che modo è più efficiente ... pulito ... meglio ... ecc O c'è un modo migliore. Entrambe le modalità funzionano e danno la stessa risposta, ma ho pensato che avrei toccato la mente dell'alveare come un caso di apprendimento.

Grazie -

+0

Ulteriori modifiche: Sto eseguendo la versione 2.7. Dovrebbe averlo aggiunto prima! –

+0

'Counter' ** è più lento ** ma ha molte più funzionalità di' defaultdict (int) '- guarda la mia risposta. –

+0

è possibile aumentare più valori? – Mohsin

risposta

6

Utilizzare collections.Counter. Counter è zucchero sintattico per defaultdict(int), ma ciò che è cool a questo proposito è che si accetta un iterabile nel costruttore, risparmiando così un passo in più (presumo tutti i tuoi esempi di cui sopra sono avvolti in un ciclo for.)

from collections import Counter 
count = Counter(x[3] for x in my_csv_reader) 

Prima dell'introduzione di collections.Counter, collections.defaultdict era il più idiomatico per questa attività, quindi per gli utenti < 2.7, utilizzare defaultdict.

from collections import defaultdict 
count = defaultdict(int) 
for x in my_csv_reader: 
    count[x[3]] += 1 
+0

Ho intenzione di rivendicare lo stato di Newbie su questo chiarimento. "X" nella parte "per x" si riferisce alla singola riga restituita da csv.reader? I "penso" Ho implementato il csv reader: my_csv_reader = csv.reader (aperto ('c: /Test.csv', 'rb'), delimitatore = ',', quotechar = '|') d = Contatore (x [3] per x in my_csv_reader) ma d è più del doppio rispetto agli altri tre metodi. –

+0

Sì, 'x' si riferisce alla riga restituita dal lettore csv. Cosa intendi per essere più del doppio? Sta dando i conteggi sbagliati? Si prega di precisare. –

+0

Precisazione: il mio codice non è corretto e la mia domanda non è stata completata! Esiste un'istruzione if che circonda i primi tre metodi che rimuovevano i valori duplicati facendo corrispondere la linea corrente alla riga precedente prima che il valore venga aggiunto al dizionario. Il nuovo metodo non è in quella condizione, quindi ho ottenuto un totale di tutte le linee, non quelle uniche! –

1
from collections import Counter 
Counter(a) 
+0

"a" è il mio dizionario. "Counter (x [3])? –

+0

Non è" contatore "nuovo in 3.1? Sto correndo 2.7 –

+0

@Count: [esiste in 2.7] (http://docs.python.org/library /collections.html#collections.Counter) – SilentGhost

0

Dal momento che non si ha accesso al Counter, la soluzione migliore è il tuo terzo approccio. È molto più pulito e facile da leggere. Inoltre, non ha il test perpetuo (e la ramificazione) che hanno i primi due approcci, il che lo rende più efficiente.

6

Hai chiesto quale era più efficiente. Supponendo che tu stia parlando della velocità di esecuzione: se i tuoi dati sono piccoli, non importa. Se è grande e tipico, il caso "esiste già" avverrà molto più spesso del caso "non in dict". Questa osservazione spiega alcuni dei risultati.

Di seguito è riportato un codice che può essere utilizzato con il modulo timeit per esplorare la velocità senza sovraccarico di lettura file. Mi sono permesso di aggiungere un quinto metodo, che non è incompleto e verrà eseguito su qualsiasi Python da almeno 1.5.2 [testato] in poi.

from collections import defaultdict, Counter 

def tally0(iterable): 
    # DOESN'T WORK -- common base case for timing 
    d = {} 
    for item in iterable: 
     d[item] = 1 
    return d 

def tally1(iterable): 
    d = {} 
    for item in iterable: 
     if item in d: 
      d[item] += 1 
     else: 
      d[item] = 1 
    return d 

def tally2(iterable): 
    d = {} 
    for item in iterable: 
     try: 
      d[item] += 1 
     except KeyError: 
      d[item] = 1 
    return d 

def tally3(iterable): 
    d = defaultdict(int) 
    for item in iterable: 
     d[item] += 1 

def tally4(iterable): 
    d = Counter() 
    for item in iterable: 
     d[item] += 1 

def tally5(iterable): 
    d = {} 
    dg = d.get 
    for item in iterable: 
     d[item] = dg(item, 0) + 1 
    return d 

tipico run (in Windows XP "Prompt dei comandi" finestra):

prompt>\python27\python -mtimeit -s"t=1000*'now is the winter of our discontent made glorious summer by this son of york';import tally_bench as tb" "tb.tally1(t)" 
10 loops, best of 3: 29.5 msec per loop 

Ecco i risultati (msec per anello):

0 base case 13.6 
1 if k in d 29.5 
2 try/except 26.1 
3 defaultdict 23.4 
4 Counter  79.4 
5 d.get(k, 0) 29.2 

Un altro studio tempistica:

prompt>\python27\python -mtimeit -s"from collections import defaultdict;d=defaultdict(int)" "d[1]+=1" 
1000000 loops, best of 3: 0.309 usec per loop 

prompt>\python27\python -mtimeit -s"from collections import Counter;d=Counter()" "d[1]+=1" 
1000000 loops, best of 3: 1.02 usec per loop 

La velocità di Counter è probabilmente dovuto al fatto che è stato implementato in parte in codice Python mentre defaultdict è interamente in C (in 2.7, almeno).

noti che Counter() non è solo "zucchero sintattico" per defaultdict(int) - si attua una completa bag aka multiset oggetto - vedi la documentazione per i dettagli; potrebbero salvarti dal reinventare la ruota se hai bisogno di qualche elaborata post-elaborazione. Se tutto quello che vuoi è contare le cose, usa defaultdict.

Aggiornamento in risposta ad una domanda da @Steven Rumbalski: """ Sono curioso, che cosa succede se si sposta il iterabile nel costruttore Contatore:? D = Counter (iterable) (ho Python 2.6 e non può testarlo) """

tally6:. semplicemente non d = Count(iterable); return d, prende 60.0 msec

si potrebbe guardare alla fonte (collections.py nel repository SVN) ... ecco cosa mia Python27\Lib\collections.py fa quando iterable non è un'istanza di mappatura:

  self_get = self.get 
      for elem in iterable: 
       self[elem] = self_get(elem, 0) + 1 

Hai visto quel codice prima? C'è molto bagaglio a mano solo per chiamare il codice eseguibile in Python 1.5.2 :-O

+0

Bel tempo.Sono curioso, cosa succede se sposti l'iterabile nel costruttore Counter: 'd = Counter (iterable)'? (Ho python 2.6 e non posso testarlo.) –

+0

@Steven Rumbalski: guarda la mia risposta aggiornata. –

0

Utilizzare setdefault.

a[x[3]] = a.setdefault(x[3], 0) + 1 

setdefault ottiene il valore della chiave specificata (x[3] in questo caso), o se non esiste, il valore specificato (0 in questo caso).

+0

Si prega di spiegare perché si pensa che 'd [k] = d.setdefault (k, 0) + 1' sarebbe meglio che' d [k] = d.get (k, 0) + 1' –

+0

Sì, sei destra. Ci stavo pensando perché avrebbe impostato il valore, ma lo stai facendo comunque. – kindall

Problemi correlati