Come scrivere una riga di intestazione con csv.DictWriter?


114

Supponiamo che io abbia un csv.DictReaderoggetto e voglio scriverlo come file CSV. Come posso fare questo?

So che posso scrivere le righe di dati in questo modo:

dr = csv.DictReader(open(f), delimiter='\t')
# process my dr object
# ...
# write out object
output = csv.DictWriter(open(f2, 'w'), delimiter='\t')
for item in dr:
    output.writerow(item)

Ma come posso includere i nomi dei campi?

Risposte:


149

Modifica:
in 2.7 / 3.2 c'è un nuovo writeheader()metodo . Inoltre, la risposta di John Machin fornisce un metodo più semplice per scrivere la riga di intestazione.
Semplice esempio di utilizzo del writeheader()metodo ora disponibile in 2.7 / 3.2:

from collections import OrderedDict
ordered_fieldnames = OrderedDict([('field1',None),('field2',None)])
with open(outfile,'wb') as fou:
    dw = csv.DictWriter(fou, delimiter='\t', fieldnames=ordered_fieldnames)
    dw.writeheader()
    # continue on to write data

La creazione di istanze di DictWriter richiede un argomento fieldnames.
Dalla documentazione :

Il parametro fieldnames identifica l'ordine in cui i valori nel dizionario passati al metodo writerow () vengono scritti nel file csv.

In altre parole: l'argomento Fieldnames è richiesto perché i dict di Python sono intrinsecamente non ordinati.
Di seguito è riportato un esempio di come scrivere l'intestazione e i dati in un file.
Nota: la withdichiarazione è stata aggiunta in 2.6. Se si utilizza 2.5:from __future__ import with_statement

with open(infile,'rb') as fin:
    dr = csv.DictReader(fin, delimiter='\t')

# dr.fieldnames contains values from first row of `f`.
with open(outfile,'wb') as fou:
    dw = csv.DictWriter(fou, delimiter='\t', fieldnames=dr.fieldnames)
    headers = {} 
    for n in dw.fieldnames:
        headers[n] = n
    dw.writerow(headers)
    for row in dr:
        dw.writerow(row)

Come menzionato da @FM in un commento, puoi condensare la scrittura dell'intestazione in una riga, ad esempio:

with open(outfile,'wb') as fou:
    dw = csv.DictWriter(fou, delimiter='\t', fieldnames=dr.fieldnames)
    dw.writerow(dict((fn,fn) for fn in dr.fieldnames))
    for row in dr:
        dw.writerow(row)

12
+1 Un altro modo per scrivere l'intestazione: dw.writerow( dict((f,f) for f in dr.fieldnames) ).
FMc

2
@ Adam: per una battuta più breve, vedi la mia risposta.
John Machin

2
@ John: +1 alla tua risposta; utilizzare semplicemente "l'istanza del writer sottostante" è certamente preferibile alla "laboriosa mappatura dell'identità".
Mechanical_meat

1
@endolith: grazie per il feedback. Questa parte è stata spostata all'inizio della risposta.
Mechanical_meat

1
Poiché stai utilizzando anche un dictReader, è facile aggiungere i campi con dw = csv.DictWriter(fou, delimiter='\t', fieldnames=dr.fieldnames). In questo modo, se i tuoi campi cambiano, non è necessario modificare dictWriter.
Spencer Rathbun

29

Alcune opzioni:

(1) Crea laboriosamente una mappatura dell'identità (cioè non fare nulla) dai tuoi nomi di campo in modo che csv.DictWriter possa riconvertirlo in un elenco e passarlo a un'istanza csv.writer.

(2) La documentazione menziona "l' writeristanza sottostante " ... quindi usatela (esempio alla fine).

dw.writer.writerow(dw.fieldnames)

(3) Evita l'overhead di csv.Dictwriter e fallo da solo con csv.writer

Scrittura dati:

w.writerow([d[k] for k in fieldnames])

o

w.writerow([d.get(k, restval) for k in fieldnames])

Invece della extrasaction"funzionalità", preferirei codificarla io stesso; in questo modo puoi segnalare TUTTI gli "extra" con le chiavi e i valori, non solo la prima chiave aggiuntiva. Ciò che è un vero fastidio con DictWriter è che se hai verificato tu stesso le chiavi mentre ogni dict veniva costruito, devi ricordarti di usare extrasaction = 'ignore' altrimenti andrà LENTAMENTE (i nomi dei campi è un elenco) ripeti il ​​controllo:

wrong_fields = [k for k in rowdict if k not in self.fieldnames]

============

>>> f = open('csvtest.csv', 'wb')
>>> import csv
>>> fns = 'foo bar zot'.split()
>>> dw = csv.DictWriter(f, fns, restval='Huh?')
# dw.writefieldnames(fns) -- no such animal
>>> dw.writerow(fns) # no such luck, it can't imagine what to do with a list
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\python26\lib\csv.py", line 144, in writerow
    return self.writer.writerow(self._dict_to_list(rowdict))
  File "C:\python26\lib\csv.py", line 141, in _dict_to_list
    return [rowdict.get(key, self.restval) for key in self.fieldnames]
AttributeError: 'list' object has no attribute 'get'
>>> dir(dw)
['__doc__', '__init__', '__module__', '_dict_to_list', 'extrasaction', 'fieldnam
es', 'restval', 'writer', 'writerow', 'writerows']
# eureka
>>> dw.writer.writerow(dw.fieldnames)
>>> dw.writerow({'foo':'oof'})
>>> f.close()
>>> open('csvtest.csv', 'rb').read()
'foo,bar,zot\r\noof,Huh?,Huh?\r\n'
>>>

Attualmente in Python 3.6, la extrasactionfunzionalità sembra essere implementata meglio. Ora è l' wrong_fields = rowdict.keys() - self.fieldnames so it's effectively a operazione set`.
martineau

Voterò questa risposta per il commento 'evita DictWriter' - Non ho visto alcun vantaggio nell'usarlo e sembra più veloce nel strutturare i tuoi dati e usare csv.writer
neophytte

8

Un altro modo per farlo sarebbe aggiungere prima di aggiungere righe nell'output, la riga seguente:

output.writerow(dict(zip(dr.fieldnames, dr.fieldnames)))

Lo zip restituirà un elenco di doppietti contenenti lo stesso valore. Questo elenco potrebbe essere utilizzato per avviare un dizionario.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.