Criando um dicionário a partir de um arquivo csv?


153

Estou tentando criar um dicionário a partir de um arquivo csv. A primeira coluna do arquivo csv contém chaves exclusivas e a segunda coluna contém valores. Cada linha do arquivo csv representa um par exclusivo de chave e valor no dicionário. Tentei usar as classes csv.DictReadere csv.DictWriter, mas só consegui descobrir como gerar um novo dicionário para cada linha. Eu quero um dicionário. Aqui está o código que estou tentando 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)

Quando executo o código acima, recebo um ValueError: too many values to unpack (expected 2). Como crio um dicionário a partir de um arquivo csv? Obrigado.


2
Você pode dar um exemplo de um arquivo de entrada e a estrutura de dados resultante?
21911 Robert

1
Quando você itera sobre csv.reader, obtém uma única linha, não linhas. Portanto, o formulário válido é mydict = {k: v para k, v no reader}, mas se você tiver certeza, há apenas duas colunas no arquivo csv, então mydict = dict (reader) é muito mais rápido.
Alex Laskin

Respostas:


155

Acredito que a sintaxe que você estava procurando é a seguinte:

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}

Como alternativa, para python <= 2.7.1, você deseja:

mydict = dict((rows[0],rows[1]) for rows in reader)

2
É bom contabilizar linhas mais longas do que o esperado; mas ele não deveria criar sua própria exceção se houver muitos itens seguidos? Eu acho que isso significa que há um erro nos dados de entrada dele.
anseio de máquina

1
E então ele pelo menos seria capaz de restringir a exceção a entradas defeituosas
anseio da máquina

Isso tem algum mérito, mas acredito firmemente que existem exceções para dizer que você programou algo incorretamente - não para quando o mundo lhe der limões. É quando você imprime uma bonita mensagem de erro e falha, ou - mais apropriado para este caso - uma bonita mensagem de aviso e obtém êxito.
Nate

Desculpe, olhou o código do op, difícil dizer se ele queria apenas 2 itens por linha. Eu estava errado!
anseio da máquina

1
Eu tive várias linhas em CSV mas deu apenas 1 par chave: valor
Abhilash Mishra

80

Abra o arquivo chamando de abrir e, em seguida csv.DictReader.

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

Você pode iterar nas linhas do objeto leitor de dict do arquivo csv iterando sobre input_file.

for row in input_file:
    print(row)

OU Para acessar apenas a primeira linha

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

ATUALIZAÇÃO Nas versões python 3+, esse código mudaria um pouco:

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

3
Isso faz com que DictReader objeto não um dicionário (e sim não um valor par de chaves)
HN Singh

1
@HN Singh - Sim, eu sei - intenção era ele vai ajudar alguma outra pessoa assim
Laxmikant Ratnaparkhi

1
O objeto 'DictReader' não tem atributo 'next'
Palak

1
@Palak - foi respondido pelo Python 2.7, tente em next(dictobj)vez das dictobj.next()versões do 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

6
Estilo altamente não pitonico.
Alex Laskin

47
Alex Laskin: Sério? Parece um python bastante legível para mim. Qual é o seu princípio para apoiar esta afirmação? Você basicamente só o chamava de "cabeça poopy" ...
máquina anseio

26
@ anseio de máquina, não, eu não disse que o código dele é 'ruim'. Mas não há um único motivo para escrever, for row in reader: k, v = rowse você pode simplesmente escrever for k, v in reader, por exemplo. E se você espera que esse leitor seja iterável, produzindo itens de dois elementos, basta passá-lo diretamente para determinar a conversão. d = dict(reader)é muito mais curto e significativamente mais rápido em grandes conjuntos de dados.
Alex Laskin

44
Alex Laskin: Obrigado pelo esclarecimento. Eu pessoalmente concordei com você, mas acho que, se você quiser chamar o código de alguém de "não-pitonico", deve acompanhar esse comentário com uma justificativa. Eu diria que "mais curto" e "mais rápido" não são necessariamente equivalentes a "mais pitonicos". A legibilidade / confiabilidade também é uma grande preocupação. Se for mais fácil trabalhar com algumas das nossas restrições no for row in readerparadigma acima , ele pode ser mais prático (após um desenvolvimento a longo prazo). Concordo com você a curto prazo, mas cuidado com a otimização prematura.
máquina anseio

30

Isso não é elegante, mas uma solução de uma linha usando pandas.

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

Se você deseja especificar dtype para seu índice (ele não pode ser especificado em read_csv se você usar o argumento index_col devido a um erro ):

import pandas as pd
pd.read_csv('coors.csv', header=None, dtype={0: str}).set_index(0).squeeze().to_dict()

3
no meu livro esta é a melhor resposta
boardtc

E se houver um cabeçalho ...?
Ndtreviv 30/05/19

@ndtreviv você pode usar as pistas de esqui para ignorar os cabeçalhos.
mudassirkhan19

17

Você apenas precisa converter csv.reader em 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'}

5
essa solução é organizada e funcionará muito bem se ele tiver certeza de que suas entradas nunca terão três ou mais colunas em alguma linha. No entanto, se isso nunca é encontrado, uma exceção um pouco como isso será levantada: ValueError: dictionary update sequence element #2 has length 3; 2 is required.
Nate

@machine, a julgar pelo erro na questão, o arquivo CSV tem mais de 2 colunas
John La Rooy

@gnibbler, não, o erro na pergunta é devido à dupla descompactação da linha. Primeiro ele tentar iterar leitor, a obtenção de linhas que é realmente única fileira . E quando ele tenta iterar nessa única linha, ele recebe dois itens, que não podem ser descompactados corretamente.
Alex Laskin

Um comentário geral: criar objetos mantidos na memória a partir de iterables pode causar um problema de memória. Sugira verificar o espaço da memória e o tamanho do arquivo de origem iterável. Uma vantagem principal (o ponto principal?) Das iteráveis ​​é não guardar grandes coisas na memória.
travelingbones

@Nate: Isso pode ser corrigido se necessário envolvendo a filterchamada com map(operator.itemgetter(slice(2)), ...), por isso só vai puxar os dois primeiros iterms, tornando-se: dict(map(operator.itemgetter(slice(2)), filter(None, csv.reader(f)))). Se for o Python 2, certifique-se de fazer from future_builtins import map, filter, para que ele dictleia diretamente um gerador, em vez de produzir vários lists temporários desnecessários primeiro).
ShadowRanger

12

Você também pode usar numpy para isso.

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

5

Eu sugiro adicionar if rowscaso haja uma linha vazia no final do arquivo

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)

Bem feito e bem pensado. Mas, como eu disse acima, ele deveria realmente estar ignorando o fato de que sua linha de entrada é mais longa do que ele esperava? Eu diria que ele deveria criar sua própria exceção (com uma mensagem personalizada) se receber uma linha com mais de dois itens.
anseio de máquina

Ou melhor, como declarado acima por @Nate, pelo menos imprima uma mensagem de aviso. Isso simplesmente não parece algo que você gostaria de ignorar.
máquina desejando

sua resposta (versus a minha) fez refletir sobre alguma coisa - existe uma diferença de eficiência entre fatiar e indexar nesse caso?
Nate

1
@machine, não faço ideia. Talvez seja um despejo de uma tabela de usuário de um banco de dados, e ele só quer um dicionário de userid: nome de usuário ou algo assim, por exemplo
John La Rooy

1
Ei pessoal, obrigado pelos comentários. Sua discussão realmente me ajudou com o meu problema. Eu gosto da idéia de levantar uma bandeira se a entrada for maior que o esperado. Meus dados são um despejo de banco de dados e eu tenho mais de duas colunas de dados.
drbunsen

5

Solução de uma linha

import pandas as pd

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

3

Se você estiver bem com o uso do pacote numpy, poderá fazer algo como o seguinte:

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]

3

Para arquivos csv simples, como os seguintes

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

Você pode convertê-lo em um dicionário Python usando apenas built-ins

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)}

Isso deve render o seguinte dicionário

{'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: Os dicionários Python possuem chaves exclusivas; portanto, se o seu arquivo csv duplicar, idsvocê deverá anexar cada linha a uma 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)})

nb, tudo isso pode ser reduzido para usar set_default: csv_dict.set_default (key, []). append ({key: valor para chave, valor em zip (cabeçalho, valores)}))
mdmjsh

A sintaxe ({key: value}) em seu .appendcomando foi muito útil. Acabei usando a mesma sintaxe em uma row.updateiteração e adição a um DictReaderobjeto que foi criado a partir de um arquivo CSV.
Shrout1

1

Você pode usar isso, é bem legal:

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

1

Muitas soluções foram publicadas e eu gostaria de contribuir com a minha, que funciona para um número diferente de colunas no arquivo CSV. Ele cria um dicionário com uma chave por coluna e o valor de cada chave é uma lista com os elementos dessa coluna.

    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])

1

com pandas, é muito mais fácil, por exemplo. supondo que você tenha os seguintes dados como CSV e vamos chamá-lo test.txt/test.csv (você sabe que CSV é um tipo de arquivo de texto)

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

agora usando pandas

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

para cada linha, seria

df.to_dict(orient='records')

e é isso.


0

Tente usar um defaultdicte 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)

Retorna:

{'key1':[value_1, value_2, value_3], 'key2': [value_a, value_b, value_c], 'Key3':[value_x, Value_y, Value_z]}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.