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.