Tworzysz słownik z pliku CSV?


153

Próbuję utworzyć słownik z pliku CSV. Pierwsza kolumna pliku csv zawiera unikalne klucze, a druga kolumna zawiera wartości. Każdy wiersz pliku csv reprezentuje unikalną parę klucz-wartość w słowniku. Próbowałem użyć klas csv.DictReaderi csv.DictWriter, ale mogłem tylko dowiedzieć się, jak wygenerować nowy słownik dla każdego wiersza. Chcę jeden słownik. Oto kod, którego próbuję użyć:

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)

Po uruchomieniu powyższego kodu otrzymuję plik ValueError: too many values to unpack (expected 2). Jak utworzyć jeden słownik z pliku CSV? Dzięki.


2
Czy możesz podać przykład pliku wejściowego i wynikowej struktury danych?
robert

1
Podczas iteracji po csv.reader otrzymujesz pojedynczy wiersz, a nie wiersze. Tak więc poprawna forma to mydict = {k: v dla k, v in reader}, ale jeśli jesteś pewien, że w pliku csv są tylko dwie kolumny, to mydict = dict (reader) jest znacznie szybsze.
Alex Laskin,

Odpowiedzi:


155

Uważam, że składnia, której szukałeś, jest następująca:

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}

Alternatywnie, dla Pythona <= 2.7.1, chcesz:

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

2
Dobrze jest uwzględnić wiersze dłuższe niż oczekiwano; ale czy nie powinien zgłaszać własnego wyjątku, jeśli jest zbyt wiele elementów w rzędzie? Myślę, że oznaczałoby to błąd w jego danych wejściowych.
maszyna tęskni

1
A potem przynajmniej byłby w stanie zawęzić wyjątek do błędnych danych wejściowych
tęsknota za maszyną

Ma to pewne zalety, ale jestem głęboko przekonany, że istnieją wyjątki, które mówią, że zaprogramowałeś coś nieprawidłowo - nie na wypadek, gdy świat daje ci cytryny. Wtedy drukujesz ładny komunikat o błędzie i kończy się niepowodzeniem lub - bardziej odpowiednie w tym przypadku - ładny komunikat ostrzegawczy i udaje się.
Nate,

Przepraszamy, spojrzałem na kod operatora, trudno powiedzieć, czy chciał tylko 2 pozycje w linii. Myliłem się!
maszyna tęskni

1
Miałem wiele linii w csv, ale dało to tylko 1 klucz: parę wartości
Abhilash Mishra

80

Otwórz plik, wywołując otwórz, a następnie csv.DictReader.

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

Możesz iterować po wierszach obiektu czytnika dykt pliku csv, wykonując iterację po pliku input_file.

for row in input_file:
    print(row)

LUB Aby uzyskać dostęp tylko do pierwszej linii

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

AKTUALIZACJA W wersjach Pythona 3+ ten kod by się trochę zmienił:

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

3
To sprawia, że ​​obiekt DictReader nie jest słownikiem (i tak, nie jest parą klucz-wartość)
HN Singh

1
@HN Singh - Tak, wiem - intencją było, że pomoże to
komuś

1
Obiekt „DictReader” nie ma atrybutu „next”
Palak

1
@Palak - odpowiedziano na Python 2.7, spróbuj next(dictobj)zamiast dictobj.next()w wersjach 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
Styl wysoce nie-pythonowy.
Alex Laskin,

47
@Alex Laskin: Naprawdę? Dla mnie wygląda to na całkiem czytelny Python. Jaka jest Twoja zasada na poparcie tego stwierdzenia? Właściwie nazwałeś go „kupą głową” ...
maszyna tęskniąca

26
@ tęsknota za maszyną, nie, nie powiedziałem, że jego kod jest „zły”. Ale nie ma jednego powodu, aby pisać, for row in reader: k, v = rowjeśli for k, v in readerna przykład możesz po prostu pisać . A jeśli spodziewasz się, że czytnik jest iterowalny, wytwarzający elementy dwuelementowe, możesz po prostu przekazać go bezpośrednio do dyktowania do konwersji. d = dict(reader)jest znacznie krótszy i znacznie szybszy w przypadku ogromnych zbiorów danych.
Alex Laskin,

44
@Alex Laskin: Dzięki za wyjaśnienie. Osobiście się z tobą zgodziłem, ale myślę, że jeśli chcesz nazwać czyjś kod „nie-pythonowym”, powinieneś dołączyć do tego komentarza uzasadnienie. Powiedziałbym, że „krótszy” i „szybszy” niekoniecznie jest odpowiednikiem „bardziej pythonowego”. Czytelność / niezawodność również jest poważnym problemem. Jeśli łatwiej jest pracować z niektórymi z naszych ograniczeń w powyższym for row in readerparadygmacie, to może (po długoterminowym opracowaniu) być bardziej praktyczne. Zgadzam się z tobą na krótko, ale uważaj na przedwczesną optymalizację.
maszyna tęskni

30

To nie jest eleganckie, ale jednoprzewodowe rozwiązanie wykorzystujące pandy.

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

Jeśli chcesz określić dtype dla swojego indeksu (nie można go określić w read_csv, jeśli używasz argumentu index_col z powodu błędu ):

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

3
w mojej książce to najlepsza odpowiedź
boardtc

A jeśli jest nagłówek ...?
ndtreviv

@ndtreviv możesz użyć pomiń do ignorowania nagłówków.
mudassirkhan19

17

Musisz po prostu przekonwertować csv.reader na dyktowanie:

~ >> 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
to rozwiązanie jest uporządkowane i będzie działać świetnie, jeśli będzie miał pewność, że jego dane wejściowe nigdy nie będą miały trzech lub więcej kolumn w jakimś wierszu. Jednak jeśli kiedykolwiek napotkał wyjątek trochę jak ten zostanie podniesiony: ValueError: dictionary update sequence element #2 has length 3; 2 is required.
Nate,

@machine, sądząc po błędzie w pytaniu, plik csv ma ​​więcej niż 2 kolumny
John La Rooy,

@gnibbler, nie, błąd w pytaniu wynika z podwójnego rozpakowania wiersza. Najpierw próbuje iterować po czytniku, uzyskując wiersze, które są w rzeczywistości jednym wierszem . A kiedy próbuje powtórzyć ten pojedynczy wiersz, otrzymuje dwa elementy, których nie można poprawnie rozpakować.
Alex Laskin,

Ogólny komentarz: tworzenie obiektów przechowywanych w pamięci z elementów iteracyjnych może spowodować problem z pamięcią. Zaproponuj sprawdzenie miejsca w pamięci i rozmiaru iterowalnego pliku źródłowego. Główną zaletą (o co chodzi?) Iterowalnych jest to, że nie przechowują w pamięci dużych rzeczy.
travellingbones

@Nate: To może być ustalona w razie potrzeby poprzez owijanie filterpołączenia z map(operator.itemgetter(slice(2)), ...), więc będzie to ciągnąć tylko dwa pierwsze iterms, czyniąc go: dict(map(operator.itemgetter(slice(2)), filter(None, csv.reader(f)))). Jeśli jest to Python 2, upewnij się from future_builtins import map, filter, że tak, więc dictczyta generator bezpośrednio, zamiast listnajpierw tworzyć wiele niepotrzebnych tymczasowych znaków).
ShadowRanger,

12

Możesz również użyć do tego numpy.

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

5

Sugerowałbym dodanie if rowsw przypadku, gdy na końcu pliku znajduje się pusta linia

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)

Dobrze zrobione i przemyślane. Ale jak powiedziałem powyżej, czy naprawdę powinien ignorować fakt, że jego wiersz wejściowy jest dłuższy niż oczekiwał? Powiedziałbym, że powinien zgłosić swój własny wyjątek (z niestandardową wiadomością), jeśli otrzyma wiersz z więcej niż dwoma przedmiotami.
maszyna tęskni

Lub raczej, jak stwierdzono powyżej przez @Nate, przynajmniej wydrukuj ostrzeżenie. To po prostu nie wydaje się czymś, co chciałbyś zignorować.
maszyna tęskni

Twoja odpowiedź (w porównaniu z moją) sprawiła, że ​​zastanowisz się nad czymś - czy w tym przypadku istnieje różnica wydajności między wycinaniem a indeksowaniem?
Nate,

1
@machine, nie mam pojęcia. Być może jest to zrzut tabeli użytkowników z bazy danych, a on chce tylko dyktować identyfikator użytkownika: nazwę użytkownika lub coś takiego, na przykład
John La Rooy

1
Cześć, dzięki za komentarze. Twoja dyskusja naprawdę pomogła mi rozwiązać mój problem. Podoba mi się pomysł podniesienia flagi, jeśli dane wejściowe są dłuższe niż oczekiwano. Moje dane to zrzut bazy danych i mam więcej niż dwie kolumny danych.
drbunsen

5

Rozwiązanie jednowarstwowe

import pandas as pd

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

3

Jeśli nie masz nic przeciwko korzystaniu z pakietu numpy, możesz zrobić coś takiego:

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

W przypadku prostych plików csv, takich jak poniższe

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

Możesz przekonwertować go na słownik Pythona, używając tylko wbudowanych

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

Powinno to dać następujący słownik

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

Uwaga: słowniki Pythona mają unikalne klucze, więc jeśli plik csv zawiera duplikaty ids, należy dołączyć każdy wiersz do listy.

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 to wszystko można skrócić do set_default: csv_dict.set_default (klucz, []). append ({klucz: wartość klucza, wartość w zip (nagłówek, wartości)}))
mdmjsh

Składnia ({klucz: wartość}) w Twoim .appendpoleceniu była bardzo przydatna. Skończyło się na tym, że użyłem tej samej składni row.updatepodczas iteracji i dodawania do DictReaderobiektu, który został utworzony z pliku CSV.
Shrout 1

1

Możesz tego użyć, jest całkiem fajny:

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

Zostało opublikowanych wiele rozwiązań i chciałbym wnieść swój wkład w moje, które działa dla innej liczby kolumn w pliku CSV. Tworzy słownik z jednym kluczem na kolumnę, a wartością dla każdego klucza jest lista z elementami w takiej kolumnie.

    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

na przykład z pandami jest znacznie łatwiej. zakładając, że masz następujące dane jako CSV i nazwijmy to test.txt/ test.csv(wiesz, że CSV to rodzaj pliku tekstowego)

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

teraz za pomocą pand

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

byłoby to dla każdego rzędu

df.to_dict(orient='records')

i to wszystko.


0

Spróbuj użyć defaultdicti 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)

Zwraca:

{'key1':[value_1, value_2, value_3], 'key2': [value_a, value_b, value_c], 'Key3':[value_x, Value_y, Value_z]}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.