¿Crear un diccionario a partir de un archivo csv?

153

Estoy tratando de crear un diccionario a partir de un archivo csv. La primera columna del archivo csv contiene claves únicas y la segunda columna contiene valores. Cada fila del archivo csv representa una clave única, un par de valores dentro del diccionario. Traté de usar las clases csv.DictReadery csv.DictWriter, pero solo pude descubrir cómo generar un nuevo diccionario para cada fila. Quiero un diccionario Aquí está el código que estoy tratando de usar:

import csv

with open('coors.csv', mode='r') as infile:
    reader = csv.reader(infile)
    with open('coors_new.csv', mode='w') as outfile:
    writer = csv.writer(outfile)
    for rows in reader:
        k = rows[0]
        v = rows[1]
        mydict = {k:v for k, v in rows}
    print(mydict)

Cuando ejecuto el código anterior, obtengo un ValueError: too many values to unpack (expected 2). ¿Cómo creo un diccionario a partir de un archivo csv? Gracias.

drbunsen
fuente
2
¿Puede dar un ejemplo de un archivo de entrada y la estructura de datos resultante?
robert
1
Cuando itera sobre csv.reader, obtiene una sola fila, no filas. Por lo tanto, la forma válida es mydict = {k: v para k, v en el lector}, pero si está seguro de que solo hay dos columnas en el archivo csv, entonces mydict = dict (lector) es mucho más rápido.
Alex Laskin

Respuestas:

155

Creo que la sintaxis que buscabas es la siguiente:

import csv

with open('coors.csv', mode='r') as infile:
    reader = csv.reader(infile)
    with open('coors_new.csv', mode='w') as outfile:
        writer = csv.writer(outfile)
        mydict = {rows[0]:rows[1] for rows in reader}

Alternativamente, para python <= 2.7.1, desea:

mydict = dict((rows[0],rows[1]) for rows in reader)
Nate
fuente
2
Es bueno tener en cuenta las filas más largas de lo esperado; pero ¿no debería estar planteando su propia excepción si hay demasiados elementos seguidos? Creo que eso significaría que hay un error con sus datos de entrada.
máquina anhelando el
1
Y luego al menos podría reducir la excepción a una entrada defectuosa
máquina anhela el
Eso tiene algún mérito, pero creo firmemente que existen excepciones para decirle que programó algo incorrectamente, no para cuando el mundo le da limones. Es entonces cuando imprime un bonito mensaje de error y falla, o, más apropiado para este caso, un bonito mensaje de advertencia y tiene éxito.
Nate
Lo siento, miré el código de operación, es difícil saber si solo quería 2 artículos por línea. ¡Estaba equivocado!
máquina anhelando el
1
Tenía varias líneas en csv pero solo daba 1 clave: par de valores
Abhilash Mishra
80

Abra el archivo llamando a abrir y luego csv.DictReader.

input_file = csv.DictReader(open("coors.csv"))

Puede iterar sobre las filas del objeto lector de archivos csv dictando iterando sobre input_file.

for row in input_file:
    print(row)

O Para acceder solo a la primera línea

dictobj = csv.DictReader(open('coors.csv')).next() 

ACTUALIZACIÓN En las versiones de Python 3+, este código cambiaría un poco:

reader = csv.DictReader(open('coors.csv'))
dictobj = next(reader) 
Laxmikant Ratnaparkhi
fuente
3
Esto hace que el objeto DictReader no sea un diccionario (y sí, no un par de valores clave)
HN Singh
1
@HN Singh - Sí, lo sé - la intención era ayudar a alguien más también
Laxmikant Ratnaparkhi
1
El objeto 'DictReader' no tiene atributo 'next'
Palak
1
@Palak: se respondió para Python 2.7, intente en next(dictobj)lugar de dictobj.next()en las versiones Python 3+.
Laxmikant Ratnaparkhi
61
import csv
reader = csv.reader(open('filename.csv', 'r'))
d = {}
for row in reader:
   k, v = row
   d[k] = v
robert
fuente
66
Estilo altamente no pitónico.
Alex Laskin
47
@Alex Laskin: ¿En serio? A mí me parece una pitón bastante legible. ¿Cuál es su principio para respaldar esta declaración? Es, básicamente, simplemente lo llamó "cabeza caca" ...
máquina anhelo
26
@ anhelo de máquina, no, no dije que su código es 'malo'. Pero no hay una sola razón para escribir for row in reader: k, v = rowsi simplemente puede escribir for k, v in reader, por ejemplo. Y si espera que ese lector sea iterable y produzca elementos de dos elementos, simplemente puede pasarlo directamente a dict para conversión. d = dict(reader)es mucho más corto y significativamente más rápido en grandes conjuntos de datos.
Alex Laskin
44
@ Alex Laskin: Gracias por la aclaración. Personalmente estuve de acuerdo con usted, pero creo que si va a llamar al código de alguien "no pitónico", debe acompañar ese comentario con una justificación. Yo diría que "más corto" y "más rápido" no son necesariamente equivalentes a "más pitónico". La legibilidad / fiabilidad también es una gran preocupación. Si es más fácil trabajar en algunas de nuestras limitaciones dentro del for row in readerparadigma anterior , entonces podría (después del desarrollo a largo plazo) ser más práctico. Estoy de acuerdo con usted a corto plazo, pero tenga cuidado con la optimización prematura.
máquina anhelando el
30

Esto no es elegante, sino una solución de una línea que utiliza pandas.

import pandas as pd
pd.read_csv('coors.csv', header=None, index_col=0, squeeze=True).to_dict()

Si desea especificar dtype para su índice (no se puede especificar en read_csv si usa el argumento index_col debido a un error ):

import pandas as pd
pd.read_csv('coors.csv', header=None, dtype={0: str}).set_index(0).squeeze().to_dict()
mudassirkhan19
fuente
3
en mi libro esta es la mejor respuesta
boardtc
¿Y si hay un encabezado ...?
ndtreviv
@ndtreviv puedes usar skiprows para ignorar los encabezados.
mudassirkhan19
17

Solo tienes que convertir csv.reader a dict:

~ >> cat > 1.csv
key1, value1
key2, value2
key2, value22
key3, value3

~ >> cat > d.py
import csv
with open('1.csv') as f:
    d = dict(filter(None, csv.reader(f)))

print(d)

~ >> python d.py
{'key3': ' value3', 'key2': ' value22', 'key1': ' value1'}
Alex Laskin
fuente
55
esa solución es ordenada y funcionará muy bien si puede estar seguro de que sus entradas nunca tendrán tres o más columnas en alguna fila. Sin embargo, si lo que se ha encontrado alguna vez, una excepción algo como esto será levantado: ValueError: dictionary update sequence element #2 has length 3; 2 is required.
Nate
@machine, a juzgar por el error en la pregunta, el archivo csv tiene más de 2 columnas
John La Rooy
@gnibbler, no, el error en la pregunta se debe al doble desempaquetado de la fila. Primero intenta iterar sobre el lector, obteniendo filas que en realidad son una sola fila . Y cuando intenta iterar sobre esta única fila, obtiene dos elementos, que no se pueden desempaquetar correctamente.
Alex Laskin
Un comentario general: hacer objetos retenidos en la memoria de iterables puede causar un problema de memoria. Sugiera verificar su espacio de memoria y el tamaño del archivo fuente iterable. Una ventaja principal (¿todo el punto?) De los iterables es no guardar cosas grandes en la memoria.
travelingbones
@Nate: Eso se puede arreglar si es necesario envolviendo la filterllamada con map(operator.itemgetter(slice(2)), ...), por lo que sólo se tire de los dos primeros iterms, por lo que es: dict(map(operator.itemgetter(slice(2)), filter(None, csv.reader(f)))). Si se trata de Python 2, asegúrese de hacerlo from future_builtins import map, filter, de modo que dictlea un generador directamente, en lugar de producir varios lists temporales innecesarios primero).
ShadowRanger
12

También puedes usar numpy para esto.

from numpy import loadtxt
key_value = loadtxt("filename.csv", delimiter=",")
mydict = { k:v for k,v in key_value }
Thiru
fuente
5

Sugeriría agregar if rowsen caso de que haya una línea vacía al final del archivo

import csv
with open('coors.csv', mode='r') as infile:
    reader = csv.reader(infile)
    with open('coors_new.csv', mode='w') as outfile:
        writer = csv.writer(outfile)
        mydict = dict(row[:2] for row in reader if row)
John La Rooy
fuente
Tanto bien hecho como bien pensado. Pero como dije anteriormente, ¿debería realmente ignorar el hecho de que su línea de entrada es más larga de lo que esperaba? Yo diría que debería plantear su propia excepción (con un mensaje personalizado) si obtiene una línea con más de dos elementos.
máquina anhelando el
O, mejor dicho, como lo indicó @Nate, al menos imprima un mensaje de advertencia. Esto simplemente no parece algo que quieras ignorar.
máquina anhelando el
su respuesta (frente a la mía) hizo reflexionar sobre algo: ¿hay una diferencia de eficiencia entre cortar e indexar en este caso?
Nate
1
@ máquina, ni idea. Tal vez es un volcado de una tabla de usuario de una base de datos, y él solo quiere un dict de userid: nombre de usuario o algo por ejemplo
John La Rooy
1
Hola chicos, gracias por los comentarios. Su discusión realmente me ayudó con mi problema. Me gusta la idea de levantar una bandera si la entrada es más larga de lo esperado. Mis datos son un volcado de base de datos y tengo más de dos columnas de datos.
drbunsen
5

Solución de una línea

import pandas as pd

dict = {row[0] : row[1] for _, row in pd.read_csv("file.csv").iterrows()}
Trideep Rath
fuente
3

Si está de acuerdo con el uso del paquete numpy, puede hacer algo como lo siguiente:

import numpy as np

lines = np.genfromtxt("coors.csv", delimiter=",", dtype=None)
my_dict = dict()
for i in range(len(lines)):
   my_dict[lines[i][0]] = lines[i][1]
nublado
fuente
3

Para archivos csv simples, como los siguientes

id,col1,col2,col3
row1,r1c1,r1c2,r1c3
row2,r2c1,r2c2,r2c3
row3,r3c1,r3c2,r3c3
row4,r4c1,r4c2,r4c3

Puede convertirlo en un diccionario de Python utilizando solo elementos integrados.

with open(csv_file) as f:
    csv_list = [[val.strip() for val in r.split(",")] for r in f.readlines()]

(_, *header), *data = csv_list
csv_dict = {}
for row in data:
    key, *values = row   
    csv_dict[key] = {key: value for key, value in zip(header, values)}

Esto debería producir el siguiente diccionario

{'row1': {'col1': 'r1c1', 'col2': 'r1c2', 'col3': 'r1c3'},
 'row2': {'col1': 'r2c1', 'col2': 'r2c2', 'col3': 'r2c3'},
 'row3': {'col1': 'r3c1', 'col2': 'r3c2', 'col3': 'r3c3'},
 'row4': {'col1': 'r4c1', 'col2': 'r4c2', 'col3': 'r4c3'}}

Nota: Los diccionarios de Python tienen claves únicas, por lo que si su archivo csv tiene duplicados ids, debe agregar cada fila a una lista.

for row in data:
    key, *values = row

    if key not in csv_dict:
            csv_dict[key] = []

    csv_dict[key].append({key: value for key, value in zip(header, values)})
yellow01
fuente
set_defaultNota: todo esto se puede acortar para usar : csv_dict.set_default (key, []). append ({key: value for key, value in zip (header, values)}))
mdmjsh
La sintaxis ({key: value}) en su .appendcomando fue muy útil. Terminé usando la misma sintaxis en un row.updateal iterar y agregar a un DictReaderobjeto que se hizo a partir de un archivo CSV.
Shrout1
1

Puedes usar esto, es genial:

import dataconverters.commas as commas
filename = 'test.csv'
with open(filename) as f:
      records, metadata = commas.parse(f)
      for row in records:
            print 'this is row in dictionary:'+rowenter code here
Hammed
fuente
1

Se han publicado muchas soluciones y me gustaría contribuir con la mía, que funciona para un número diferente de columnas en el archivo CSV. Crea un diccionario con una clave por columna, y el valor de cada clave es una lista con los elementos en dicha columna.

    input_file = csv.DictReader(open(path_to_csv_file))
    csv_dict = {elem: [] for elem in input_file.fieldnames}
    for row in input_file:
        for key in csv_dict.keys():
            csv_dict[key].append(row[key])
Alejandro Villegas
fuente
1

con pandas, es mucho más fácil, por ejemplo. asumiendo que tiene los siguientes datos como CSV y llamémoslo test.txt/ test.csv(usted sabe que CSV es una especie de archivo de texto)

a,b,c,d
1,2,3,4
5,6,7,8

ahora usando pandas

import pandas as pd
df = pd.read_csv("./text.txt")
df_to_doct = df.to_dict()

para cada fila, sería

df.to_dict(orient='records')

y eso es.

TheTechGuy
fuente
0

Trate de usar una defaultdicty DictReader.

import csv
from collections import defaultdict
my_dict = defaultdict(list)

with open('filename.csv', 'r') as csv_file:
    csv_reader = csv.DictReader(csv_file)
    for line in csv_reader:
        for key, value in line.items():
            my_dict[key].append(value)

Vuelve:

{'key1':[value_1, value_2, value_3], 'key2': [value_a, value_b, value_c], 'Key3':[value_x, Value_y, Value_z]}
Paulo Henrique Zen
fuente