Creazione di un dizionario da un file CSV?


153

Sto cercando di creare un dizionario da un file CSV. La prima colonna del file CSV contiene chiavi univoche e la seconda colonna contiene valori. Ogni riga del file CSV rappresenta una chiave univoca, una coppia di valori all'interno del dizionario. Ho provato a usare le classi csv.DictReadere csv.DictWriter, ma sono riuscito a capire solo come generare un nuovo dizionario per ogni riga. Voglio un dizionario. Ecco il codice che sto cercando di usare:

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 eseguo il codice precedente ottengo un ValueError: too many values to unpack (expected 2). Come faccio a creare un dizionario da un file CSV? Grazie.


2
Puoi dare un esempio di un file di input e della struttura dei dati risultante?
Robert

1
Quando si esegue l'iterazione su csv.reader, si ottiene una riga singola, non righe. Quindi, la forma valida è mydict = {k: v per k, v nel lettore} ma se sei sicuro che ci sono solo due colonne nel file csv, allora mydict = dict (lettore) è molto più veloce.
Alex Laskin,

Risposte:


156

Credo che la sintassi che stavi cercando sia la seguente:

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}

In alternativa, per python <= 2.7.1, vuoi:

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

2
Buono a tenere conto di righe più lunghe del previsto; ma non dovrebbe sollevare la sua eccezione se ci sono troppi elementi di fila? Penserei che significherebbe che c'è un errore con i suoi dati di input.
desiderio di macchina il

1
E poi sarebbe stato almeno in grado di restringere l'eccezione a input errati
macchina bramava il

Questo ha qualche merito, ma sono fermamente convinto che ci siano delle eccezioni per dirti che hai programmato qualcosa in modo errato, non per quando il mondo ti dà i limoni. Questo è quando stampi un messaggio di errore piuttosto e fallisci, o - più appropriato per questo caso - un messaggio di avvertimento piuttosto e riesci.
Nate,

Siamo spiacenti, ho guardato il codice operativo, difficile dire se voleva solo 2 articoli per riga. Mi sbagliavo!
desiderio di macchina il

1
Avevo più righe in csv ma ​​mi ha dato solo 1 chiave: coppia valore
Abhilash Mishra

80

Apri il file chiamando open e quindi csv.DictReader.

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

È possibile scorrere le righe dell'oggetto lettore dict del file CSV ripetendo il file input_file.

for row in input_file:
    print(row)

OPPURE Per accedere solo alla prima riga

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

AGGIORNAMENTO Nelle versioni di Python 3+, questo codice cambierebbe leggermente:

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

3
Questo rende l'oggetto DictReader non un dizionario (e sì, non una coppia chiave-valore)
HN Singh,

1
@HN Singh - Sì, lo so - l'intenzione era di aiutare anche qualcun altro
Laxmikant Ratnaparkhi

1
L'oggetto 'DictReader' non ha alcun attributo 'next'
Palak,

1
@Palak - è stata data una risposta per Python 2.7, provare next(dictobj)invece che dictobj.next()nelle versioni 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
Stile altamente non pitonico.
Alex Laskin,

47
@Alex Laskin: Davvero? A me sembra un pitone piuttosto leggibile. Qual è il tuo principio per sostenere questa affermazione? Fondamentalmente l'hai appena chiamato "testa di cacca" ...
desiderio di macchina il

26
@ Desiderio della macchina, no, non ho detto che il suo codice è "cattivo". Ma non c'è un solo motivo per scrivere for row in reader: k, v = rowse puoi semplicemente scrivere for k, v in reader, per esempio. E se ti aspetti che quel lettore sia iterabile, producendo elementi a due elementi, puoi semplicemente passarlo direttamente a dict per la conversione. d = dict(reader)è molto più breve e significativamente più veloce su enormi set di dati.
Alex Laskin,

44
@Alex Laskin: grazie per il chiarimento. Ho concordato personalmente con te, ma penso che se chiamerai il codice di qualcuno "non pythonic" dovresti accompagnare quel commento con una giustificazione. Direi che "più corto" e "più veloce" non equivalgono necessariamente a "più pitonico". La leggibilità / affidabilità è anche una grande preoccupazione. Se è più facile lavorare in alcuni dei nostri vincoli nel for row in readerparadigma di cui sopra , allora (dopo uno sviluppo a lungo termine) potrebbe essere più pratico. Sono d'accordo con te a breve termine, ma attenzione all'ottimizzazione prematura.
desiderio di macchina il

30

Questa non è elegante ma una soluzione a una linea che usa i panda.

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

Se vuoi specificare dtype per il tuo indice (non può essere specificato in read_csv se usi l'argomento index_col a causa di un bug ):

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

3
nel mio libro questa è la risposta migliore
boardtc

E se c'è un'intestazione ...?
ndtreviv,

@ndtreviv puoi usare i skiprows per ignorare le intestazioni.
mudassirkhan19

17

Devi solo convertire csv.reader in 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
tale soluzione è ordinata e funzionerà alla grande se può essere sicuro che i suoi input non avranno mai tre o più colonne in una riga. Tuttavia, se questo è mai incontrato, un'eccezione un po 'come questo sarà sollevato: ValueError: dictionary update sequence element #2 has length 3; 2 is required.
Nate,

@machine, a giudicare dall'errore nella domanda, il file csv ha più di 2 colonne
John La Rooy,

@gnibbler, no, l'errore nella domanda è dovuto al doppio spacchettamento della riga. Per prima cosa cerca di scorrere il lettore, ottenendo righe che sono in realtà singole righe . E quando prova a scorrere su questa singola riga, ottiene due elementi, che non possono essere decompressi correttamente.
Alex Laskin,

Un commento generale: creare oggetti tenuti in memoria da iterabili può causare un problema di memoria. Suggerisci di controllare lo spazio di memoria e le dimensioni del file sorgente iterabile. Un vantaggio principale (il punto??) Degli iterabili è di non tenere grandi cose in memoria.
travelingbones

@Nate: fissabile eventualmente avvolgendo la filterchiamata con map(operator.itemgetter(slice(2)), ...), quindi tirerà solo le prime due iterms, rendendolo: dict(map(operator.itemgetter(slice(2)), filter(None, csv.reader(f)))). Se è Python 2, assicurati di farlo from future_builtins import map, filter, quindi dictlegge direttamente un generatore, invece di produrre prima più messaggi temporanei non necessari list).
ShadowRanger,

12

Puoi anche usare numpy per questo.

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

5

Suggerirei di aggiungere if rowsnel caso in cui ci sia una riga vuota alla fine del file

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)

Sia ben fatto che ben congegnato. Ma come ho detto sopra, dovrebbe davvero ignorare il fatto che la sua linea di input è più lunga di quanto si aspettasse? Direi che dovrebbe sollevare la sua eccezione (con un messaggio personalizzato) se ottiene una riga con più di due elementi.
desiderio di macchina il

O meglio, come affermato sopra da @Nate, almeno stampare un messaggio di avviso. Questo non sembra qualcosa che vorresti ignorare.
desiderio di macchina il

la tua risposta (contro la mia) ha fatto riflettere su qualcosa - c'è una differenza di efficienza tra il taglio e l'indicizzazione in questo caso?
Nate,

1
@machine, non ne ho idea. Forse è un dump di una tabella utente da un database e vuole solo un motto di userid: nome utente o qualcosa del genere
John La Rooy,

1
Ciao ragazzi, grazie per i commenti. La tua discussione mi ha davvero aiutato con il mio problema. Mi piace l'idea di alzare una bandiera se l'input è più lungo del previsto. I miei dati sono un dump del database e ho più di due colonne di dati.
drbunsen,

5

Soluzione a una fodera

import pandas as pd

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

3

Se stai bene usando il pacchetto numpy, puoi fare qualcosa del tipo:

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

Per file CSV semplici, come i seguenti

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

Puoi convertirlo in un dizionario Python usando solo i built-in

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

Questo dovrebbe produrre il seguente dizionario

{'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: i dizionari Python hanno chiavi univoche, quindi se il tuo file CSV ha duplicati idsdovresti aggiungere ogni riga a un elenco.

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 tutto ciò può essere abbreviato usando set_default: csv_dict.set_default (chiave, []). append ({chiave: valore per chiave, valore in zip (intestazione, valori)}))
mdmjsh

La sintassi ({chiave: valore}) nel tuo .appendcomando è stata molto utile. Ho finito per usare la stessa sintassi in a row.updatedurante l'iterazione e l'aggiunta a un DictReaderoggetto creato da un file CSV.
Shrout1

1

Puoi usarlo, è abbastanza bello:

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

Sono state pubblicate molte soluzioni e vorrei contribuire con la mia, che funziona con un numero diverso di colonne nel file CSV. Crea un dizionario con una chiave per colonna e il valore per ciascuna chiave è un elenco con gli elementi in tale colonna.

    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

con i panda, ad esempio, è molto più semplice. supponendo che tu abbia i seguenti dati come CSV e chiamiamoli test.txt/ test.csv(sai che CSV è una sorta di file di testo)

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

ora usando i panda

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

per ogni riga, sarebbe

df.to_dict(orient='records')

e basta.


0

Prova a usare un 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)

Restituisce:

{'key1':[value_1, value_2, value_3], 'key2': [value_a, value_b, value_c], 'Key3':[value_x, Value_y, Value_z]}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.