Wie schreibe ich eine Kopfzeile mit csv.DictWriter?

113

Angenommen, ich habe ein csv.DictReaderObjekt und möchte es als CSV-Datei ausschreiben. Wie kann ich das machen?

Ich weiß, dass ich die Datenzeilen wie folgt schreiben kann :

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)

Aber wie kann ich die Feldnamen einfügen?

Martineau
quelle

Antworten:

148

Bearbeiten:
In 2.7 / 3.2 gibt es eine neue writeheader()Methode . Die Antwort von John Machin bietet auch eine einfachere Methode zum Schreiben der Kopfzeile.
Einfaches Beispiel für die Verwendung der writeheader()jetzt in 2.7 / 3.2 verfügbaren Methode:

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

Das Instanziieren von DictWriter erfordert ein Argument für Feldnamen.
Aus der Dokumentation :

Der Parameter fieldnames gibt die Reihenfolge an, in der Werte im Wörterbuch, die an die writerow () -Methode übergeben werden, in die csv-Datei geschrieben werden.

Anders ausgedrückt: Das Argument Feldnamen ist erforderlich, da Python-Dikte von Natur aus ungeordnet sind.
Unten finden Sie ein Beispiel dafür, wie Sie den Header und die Daten in eine Datei schreiben.
Hinweis: Die withAnweisung wurde in 2.6 hinzugefügt. Bei Verwendung von 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)

Wie @FM in einem Kommentar erwähnt, können Sie das Schreiben von Headern auf einen Einzeiler komprimieren, z.

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)
mechanisches Fleisch
quelle
12
+1 Noch eine andere Möglichkeit, den Header zu schreiben : dw.writerow( dict((f,f) for f in dr.fieldnames) ).
FMc
2
@Adam: Für einen kürzeren Einzeiler siehe meine Antwort.
John Machin
2
@ John: +1 auf deine Antwort; Die einfache Verwendung der "zugrunde liegenden Writer-Instanz" ist der "mühsamen Identitätszuordnung" sicherlich vorzuziehen.
mechanisches
1
@ Endolith: Danke für das Feedback. Dieser Teil wurde an den Anfang der Antwort verschoben.
mechanisches
1
Da Sie auch einen dictReader verwenden, ist es einfach, die Felder mit hinzuzufügen dw = csv.DictWriter(fou, delimiter='\t', fieldnames=dr.fieldnames). Auf diese Weise müssen Sie den dictWriter nicht anpassen, wenn sich Ihre Felder ändern.
Spencer Rathbun
29

Einige Optionen:

(1) Erstellen Sie mühsam ein Identitätszuordnungsdiktat (dh nichts tun) aus Ihren Feldnamen, damit csv.DictWriter es wieder in eine Liste konvertieren und an eine csv.writer-Instanz übergeben kann.

(2) In der Dokumentation wird "die zugrunde liegende writerInstanz" erwähnt. Verwenden Sie sie einfach (Beispiel am Ende).

dw.writer.writerow(dw.fieldnames)

(3) Vermeiden Sie den Aufwand für csv.Dictwriter und machen Sie es selbst mit csv.writer

Daten schreiben:

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

oder

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

Anstelle der extrasaction"Funktionalität" würde ich es vorziehen, sie selbst zu codieren. Auf diese Weise können Sie ALLE "Extras" mit den Schlüsseln und Werten melden, nicht nur den ersten zusätzlichen Schlüssel. Ein echtes Ärgernis bei DictWriter ist, dass Sie, wenn Sie die Schlüssel selbst überprüft haben, während jedes Diktat erstellt wurde, daran denken müssen, extrasaction = 'ignore' zu verwenden, da es sonst langsam wird (Feldnamen sind eine Liste). Wiederholen Sie die Prüfung:

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'
>>>
John Machin
quelle
Derzeit extrasactionscheint die Funktionalität in Python 3.6 besser implementiert zu sein. Es ist jetzt wrong_fields = rowdict.keys() - self.fieldnames so it's effectively a eingestellt.
Martineau
Ich stimme diese Antwort für den Kommentar "DictWriter vermeiden" ab - ich habe keinen Vorteil bei der Verwendung gesehen und scheine Ihre Daten schneller zu strukturieren und csv.writer
neophytte
8

Eine andere Möglichkeit, dies zu tun, besteht darin, vor dem Hinzufügen von Zeilen in Ihrer Ausgabe die folgende Zeile hinzuzufügen:

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

Die Zip würde eine Liste von Dubletten zurückgeben, die denselben Wert enthalten. Diese Liste kann verwendet werden, um ein Wörterbuch zu initiieren.

Raphael Pr
quelle