Créer un dictionnaire à partir d'un fichier csv?

153

J'essaye de créer un dictionnaire à partir d'un fichier csv. La première colonne du fichier csv contient des clés uniques et la deuxième colonne contient des valeurs. Chaque ligne du fichier csv représente une paire clé / valeur unique dans le dictionnaire. J'ai essayé d'utiliser les classes csv.DictReaderet csv.DictWriter, mais je ne pouvais que comprendre comment générer un nouveau dictionnaire pour chaque ligne. Je veux un dictionnaire. Voici le code que j'essaye d'utiliser:

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)

Lorsque j'exécute le code ci-dessus, j'obtiens un ValueError: too many values to unpack (expected 2). Comment créer un dictionnaire à partir d'un fichier csv? Merci.

drbunsen
la source
2
Pouvez-vous donner un exemple de fichier d'entrée et la structure de données résultante?
robert
1
Lorsque vous parcourez csv.reader, vous obtenez une seule ligne, pas des lignes. Donc, la forme valide est mydict = {k: v pour k, v dans reader} mais si vous êtes sûr qu'il n'y a que deux colonnes dans le fichier csv, alors mydict = dict (reader) est beaucoup plus rapide.
Alex Laskin

Réponses:

156

Je pense que la syntaxe que vous recherchiez est la suivante:

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}

Alternativement, pour python <= 2.7.1, vous voulez:

mydict = dict((rows[0],rows[1]) for rows in reader)
Nate
la source
2
Bon pour tenir compte des lignes plus longues que prévu; mais ne devrait-il pas lever sa propre exception s'il y a trop d'éléments dans une rangée? Je pense que cela signifierait qu'il y a une erreur avec ses données d'entrée.
machine désirant le
1
Et puis il serait au moins en mesure de réduire l'exception à une entrée défectueuse
machine aspirant le
Cela a un certain mérite, mais je suis fermement convaincu que des exceptions sont là pour vous dire que vous avez programmé quelque chose de manière incorrecte - pas pour le moment où le monde vous donne des citrons. C'est à ce moment que vous imprimez un joli message d'erreur et que vous échouez, ou - plus approprié dans ce cas - un joli message d'avertissement et que vous réussissez.
Nate
Désolé, j'ai regardé le code de l'op, difficile de dire s'il ne voulait que 2 éléments par ligne. J'avais tort!
machine désirant le
1
J'avais plusieurs lignes en csv mais il n'a donné qu'une seule clé: paire de valeurs
Abhilash Mishra
80

Ouvrez le fichier en appelant open puis csv.DictReader.

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

Vous pouvez parcourir les lignes de l'objet lecteur de dict du fichier csv en itérant sur input_file.

for row in input_file:
    print(row)

OU Pour accéder à la première ligne uniquement

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

MISE À JOUR Dans les versions de python 3+, ce code changerait un peu:

reader = csv.DictReader(open('coors.csv'))
dictobj = next(reader) 
Laxmikant Ratnaparkhi
la source
3
Cela fait de l'objet DictReader pas un dictionnaire (et oui pas une paire clé / valeur)
HN Singh
1
@HN Singh - Ouais, je sais - l'intention était que cela aidera quelqu'un d'autre aussi
Laxmikant Ratnaparkhi
1
L'objet 'DictReader' n'a pas d'attribut 'next'
Palak
1
@Palak - il a été répondu pour Python 2.7, essayez next(dictobj)plutôt que dictobj.next()dans les versions 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
la source
6
Style hautement non pythonique.
Alex Laskin
47
@Alex Laskin: Vraiment? Cela ressemble à un python assez lisible pour moi. Quel est votre principe pour étayer cette déclaration? Vous l'avez simplement appelé "poopy head" ...
machine aspire
26
@ machine-aspire, non, je n'ai pas dit que son code était «mauvais». Mais il n'y a pas une seule raison d'écrire for row in reader: k, v = rowsi vous pouvez simplement écrire for k, v in reader, par exemple. Et si vous vous attendez à ce que ce lecteur soit un itérable, produisant des éléments à deux éléments, vous pouvez simplement le transmettre directement à dict pour la conversion. d = dict(reader)est beaucoup plus court et nettement plus rapide sur d'énormes ensembles de données.
Alex Laskin
44
@Alex Laskin: Merci pour la clarification. Personnellement, je suis d'accord avec vous mais je pense que si vous appelez le code de quelqu'un "non pythonique", vous devriez accompagner ce commentaire d'une justification. Je dirais que "plus court" et "plus rapide" ne sont pas nécessairement équivalents à "plus pythonique". La lisibilité / fiabilité est également une préoccupation majeure. S'il est plus facile de travailler dans certaines de nos contraintes dans le for row in readerparadigme ci-dessus , alors cela pourrait (après un développement à long terme) être plus pratique. Je suis d'accord avec vous à court terme, mais méfiez-vous d'une optimisation prématurée.
machine désirant le
30

Ce n'est pas une solution élégante mais une ligne utilisant des pandas.

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

Si vous souhaitez spécifier dtype pour votre index (il ne peut pas être spécifié dans read_csv si vous utilisez l'argument index_col à cause d'un bogue ):

import pandas as pd
pd.read_csv('coors.csv', header=None, dtype={0: str}).set_index(0).squeeze().to_dict()
mudassirkhan19
la source
3
dans mon livre c'est la meilleure réponse
boardtc
Et s'il y a un en-tête ...?
ndtreviv
@ndtreviv vous pouvez utiliser skiprows pour ignorer les en-têtes.
mudassirkhan19
17

Vous devez simplement convertir csv.reader en 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
la source
5
cette solution est ordonnée et fonctionnera très bien s'il peut être sûr que ses entrées n'auront jamais trois colonnes ou plus dans une ligne. Cependant, si cela est jamais rencontré, une exception un peu comme celui - ci sera relevé: ValueError: dictionary update sequence element #2 has length 3; 2 is required.
Nate
@machine, à en juger par l'erreur dans la question, le fichier csv a plus de 2 colonnes
John La Rooy
@gnibbler, non, l'erreur dans la question est due au double déballage de la ligne. Il essaie d'abord de parcourir le lecteur, obtenant des lignes qui sont en fait une seule ligne . Et quand il essaie d'itérer sur cette seule ligne, il obtient deux éléments qui ne peuvent pas être déballés correctement.
Alex Laskin
Un commentaire général: la création d'objets conservés en mémoire à partir d'itérables peut provoquer un problème de mémoire. Suggérer de vérifier votre espace mémoire et la taille du fichier source itérable. Un avantage principal (tout l'intérêt?) Des itérables est de ne pas garder de grandes choses en mémoire.
travellingbones
@Nate: Cela peut être fixé si nécessaire en enveloppant l' filterappel avec map(operator.itemgetter(slice(2)), ...), donc il ne tirer les deux premiers iterms, ce qui en fait: dict(map(operator.itemgetter(slice(2)), filter(None, csv.reader(f)))). Si c'est Python 2, assurez-vous de le faire from future_builtins import map, filter, de sorte que le dictlit un générateur directement, au lieu de produire d'abord plusieurs temporaires inutiles list).
ShadowRanger
12

Vous pouvez également utiliser numpy pour cela.

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

Je suggère d'ajouter if rowsau cas où il y aurait une ligne vide à la fin du fichier

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
la source
À la fois bien fait et bien pensé. Mais comme je l'ai dit plus haut, devrait-il vraiment ignorer le fait que sa ligne d'entrée est plus longue que prévu? Je dirais qu'il devrait lever sa propre exception (avec un message personnalisé) s'il obtient une ligne avec plus de deux éléments.
machine désirant le
Ou plutôt, comme indiqué ci-dessus par @Nate, affichez au moins un message d'avertissement. Cela ne semble pas être quelque chose que vous voudriez ignorer.
machine désirant le
votre réponse (par rapport à la mienne) a fait réfléchir quelque chose - y a-t-il une différence d'efficacité entre le découpage et l'indexation dans ce cas?
Nate
1
@machine, aucune idée. C'est peut-être un vidage d'une table utilisateur à partir d'une base de données, et il veut juste un dict de userid: nom d'utilisateur ou quelque chose par exemple
John La Rooy
1
Salut les gars, merci pour les commentaires. Votre discussion m'a vraiment aidé avec mon problème. J'aime l'idée de lever un drapeau si l'entrée est plus longue que prévu. Mes données sont un vidage de base de données et j'ai plus de deux colonnes de données.
drbunsen
5

Solution à une doublure

import pandas as pd

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

Si vous êtes d'accord avec l'utilisation du package numpy, vous pouvez faire quelque chose comme ceci:

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]
nuageuxBlues
la source
3

Pour les fichiers csv simples, tels que les suivants

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

Vous pouvez le convertir en dictionnaire Python en utilisant uniquement des éléments intégrés

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

Cela devrait donner le dictionnaire suivant

{'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'}}

Remarque: les dictionnaires Python ont des clés uniques, donc si votre fichier csv a un doublon, idsvous devez ajouter chaque ligne à une liste.

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)})
jaune01
la source
nb tout cela peut être abrégé en utilisant set_default: csv_dict.set_default (clé, []). append ({clé: valeur pour clé, valeur dans zip (en-tête, valeurs)}))
mdmjsh
La syntaxe ({key: value}) de votre .appendcommande était très utile. J'ai fini par utiliser la même syntaxe dans un row.updatelors de l'itération et de l'ajout à un DictReaderobjet créé à partir d'un fichier CSV.
Shrout1
1

Vous pouvez utiliser ceci, c'est plutôt cool:

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
haché
la source
1

De nombreuses solutions ont été publiées et j'aimerais contribuer avec la mienne, qui fonctionne pour un nombre différent de colonnes dans le fichier CSV. Il crée un dictionnaire avec une clé par colonne, et la valeur de chaque clé est une liste avec les éléments de cette colonne.

    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
la source
1

avec les pandas, c'est beaucoup plus facile, par exemple. en supposant que vous avez les données suivantes au format CSV et appelons-les test.txt/ test.csv(vous savez que CSV est une sorte de fichier texte)

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

utilise maintenant des pandas

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

pour chaque ligne, ce serait

df.to_dict(orient='records')

et c'est tout.

TheTechGuy
la source
0

Essayez d'utiliser un defaultdictet 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)

Il renvoie:

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