¿Cómo escribir una fila de encabezado con csv.DictWriter?

114

Supongamos que tengo un csv.DictReaderobjeto y quiero escribirlo como un archivo CSV. ¿Cómo puedo hacer esto?

Sé que puedo escribir las filas de datos de esta manera:

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)

Pero, ¿cómo puedo incluir los nombres de campo?

martineau
fuente

Respuestas:

149

Editar:
En 2.7 / 3.2 hay un nuevo writeheader()método . Además, la respuesta de John Machin proporciona un método más simple para escribir la fila del encabezado.
Ejemplo simple de usar el writeheader()método ahora disponible en 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 creación de instancias de DictWriter requiere un argumento de nombres de campo.
De la documentación :

El parámetro fieldnames identifica el orden en que los valores del diccionario pasados ​​al método writerow () se escriben en el archivo csv.

Dicho de otra manera: el argumento Nombres de campo es necesario porque los dictados de Python están inherentemente desordenados.
A continuación se muestra un ejemplo de cómo escribiría el encabezado y los datos en un archivo.
Nota: la withdeclaración se agregó en 2.6. Si usa 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)

Como @FM menciona en un comentario, puede condensar la escritura del encabezado en una sola línea, por ejemplo:

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)
carne_mecánica
fuente
12
1 Sin embargo, otra manera de escribir el encabezado: dw.writerow( dict((f,f) for f in dr.fieldnames) ).
FMc
2
@ Adam: para una frase más corta, vea mi respuesta.
John Machin
2
@John: +1 a tu respuesta; El simple hecho de utilizar "la instancia del escritor subyacente" es ciertamente preferible a un "laborioso mapeo de identidad".
mechanical_meat
1
@endolith: gracias por los comentarios. Se movió esa parte a la parte superior de la respuesta.
mechanical_meat
1
Como también está usando un dictReader, es fácil agregar los campos con dw = csv.DictWriter(fou, delimiter='\t', fieldnames=dr.fieldnames). De esa manera, si sus campos cambian, no necesita ajustar el dictWriter.
Spencer Rathbun
29

Algunas opciones:

(1) Haga laboriosamente un dictado de mapeo de identidad (es decir, no hacer nada) con sus nombres de campo para que csv.DictWriter pueda convertirlo de nuevo en una lista y pasarlo a una instancia de csv.writer.

(2) La documentación menciona "la writerinstancia subyacente " ... así que úsela (ejemplo al final).

dw.writer.writerow(dw.fieldnames)

(3) Evite la sobrecarga de csv.Dictwriter y hágalo usted mismo con csv.writer

Escribiendo datos:

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

o

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

En lugar de la extrasaction"funcionalidad", prefiero codificarlo yo mismo; de esa forma puede informar TODOS los "extras" con las claves y valores, no solo la primera clave adicional. Lo que es una verdadera molestia con DictWriter es que si ha verificado las claves usted mismo a medida que se creaba cada dictado, debe recordar usar extrasaction = 'ignore', de lo contrario, va a LENTAMENTE (los nombres de campo son una lista) repita la verificación:

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
fuente
Actualmente en Python 3.6, la extrasactionfuncionalidad parece implementarse mejor. Ahora wrong_fields = rowdict.keys() - self.fieldnames so it's effectively a es la operación establecida.
martineau
Estoy votando esta respuesta por el comentario 'evitar DictWriter': no ​​he visto ninguna ventaja en su uso, y parece más rápido estructurar sus datos y usar csv.writer
neophytte
8

Otra forma de hacer esto sería agregar antes de agregar líneas en su salida, la siguiente línea:

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

El zip devolvería una lista de doblete que contiene el mismo valor. Esta lista podría usarse para iniciar un diccionario.

Rafael Pr
fuente