Создание словаря из файла CSV?


153

Я пытаюсь создать словарь из файла CSV. Первый столбец файла csv содержит уникальные ключи, а второй столбец содержит значения. Каждая строка файла CSV представляет собой уникальный ключ, пару значений в словаре. Я пытался использовать csv.DictReaderи csv.DictWriterклассы, но я мог понять только как создать новый словарь для каждой строки. Я хочу один словарь. Вот код, который я пытаюсь использовать:

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)

Когда я запускаю приведенный выше код, я получаю ValueError: too many values to unpack (expected 2). Как мне создать один словарь из файла CSV? Спасибо.


2
Можете ли вы привести пример входного файла и результирующей структуры данных?
— Роберт

1
Когда вы перебираете csv.reader, вы получаете одну строку, а не строки. Итак, допустимая форма: mydict = {k: v для k, v в программе чтения}, но если вы уверены, что в файле csv есть только два столбца, то mydict = dict (программа чтения) будет намного быстрее.
— Алекс Ласкин

Ответы:


156

Я считаю, что синтаксис, который вы искали, выглядит следующим образом:

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}

В качестве альтернативы для python <= 2.7.1 вы хотите:

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

2
Хорошо учитывать строки длиннее, чем ожидалось; но не должен ли он поднять свое собственное исключение, если в строке слишком много предметов? Я думаю, это будет означать ошибку с его входными данными.
— машина тоскует

1
И тогда он, по крайней мере, сможет сузить исключение до ошибочного ввода
— машина жаждет

Это имеет некоторые достоинства, но я твердо верю, что есть исключения, чтобы сказать вам, что вы запрограммировали что-то неправильно - не для тех случаев, когда мир дает вам лимоны. Вот когда вы печатаете симпатичное сообщение об ошибке и терпите неудачу, или - более подходящее для этого случая - симпатичное предупреждающее сообщение и добиваетесь успеха.
— конец

Извините, посмотрел код опа, трудно сказать, хочет ли он только 2 элемента в строке. Я ошибался!
— машина тоскует

1
У меня было несколько строк в CSV, но он дал только 1 пара ключ: значение
— Абхилаш Мишра

80

Откройте файл, вызвав open, а затем csv.DictReader.

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

Вы можете выполнять итерацию по строкам объекта для чтения диктофонов в csv-файле, выполняя итерации по input_file.

for row in input_file:
    print(row)

ИЛИ Для доступа только к первой строке

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

ОБНОВЛЕНИЕ В версиях Python 3+ этот код немного изменится:

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

3
Это делает объект DictReader не словарем (и да, не парой ключ-значение)
— HN Singh

1
@HN Сингх - Да, я знаю - намерение было помочь кому-то еще
— Laxmikant Ratnaparkhi

1
Объект «DictReader» не имеет атрибут «следующий»
— Palak

1
@Palak - ответили для Python 2.7, попробуйте next(dictobj)вместо dictobj.next()версий Python 3+.
— Лаксмикант Ратнапархи

61
import csv
reader = csv.reader(open('filename.csv', 'r'))
d = {}
for row in reader:
   k, v = row
   d[k] = v

6
Очень непитонный стиль.
— Алекс Ласкин

47
@ Алекс Ласкин: Правда? Для меня это выглядит довольно читабельным питоном. Каков твой принцип, подтверждающий это утверждение? Вы просто назвали его "головокружительной головой" ...
— машина жаждет

26
@ машинное желание, нет, я не говорил, что его код «плохой». Но нет единой причины писать, например, for row in reader: k, v = rowесли вы можете просто написать for k, v in reader. И если вы ожидаете, что этот читатель является итеративным, производящим двухэлементные элементы, то вы можете просто передать его непосредственно в dict для преобразования. d = dict(reader)намного короче и значительно быстрее на огромных наборах данных.
— Алекс Ласкин

44
@ Алекс Ласкин: Спасибо за разъяснения. Я лично с тобой согласился, но я думаю, что если ты собираешься назвать чей-то код «непифоническим», ты должен сопровождать этот комментарий обоснованием. Я бы сказал, что «короче» и «быстрее» не обязательно эквивалентны «более питоническим». Читаемость / надежность также является серьезной проблемой. Если с некоторыми из наших ограничений в приведенной выше for row in readerпарадигме легче работать , то это может (после долгосрочного развития) быть более практичным. Я согласен с вами на короткий срок, но остерегайтесь преждевременной оптимизации.
— машина тоскует

30

Это не элегантно, а однолинейное решение с использованием панд.

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

Если вы хотите указать dtype для вашего индекса (он не может быть указан в read_csv, если вы используете аргумент index_col из-за ошибки ):

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

3
в моей книге это лучший ответ
— boardtc

А если есть заголовок ...?
— ndtreviv

@ndtreviv вы можете использовать skiprows для игнорирования заголовков.
— mudassirkhan19

17

Вы должны просто преобразовать csv.reader в 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
это решение опрятно, и оно будет прекрасно работать, если он может быть уверен, что на его входах никогда не будет трех или более столбцов в некоторой строке. Однако, если это когда - либо сталкивались, исключение несколько , как это будет поднято: ValueError: dictionary update sequence element #2 has length 3; 2 is required.
— конец

@machine, судя по ошибке в вопросе, файл CSV имеет более 2 столбцов
— Джон Ла Рой

@gnibbler, нет, ошибка в вопросе связана с двойной распаковкой строки. Сначала он пытается перебрать читателя, получая строки, которые на самом деле являются одной строкой . И когда он пытается перебрать эту единственную строку, он получает два элемента, которые нельзя распаковать правильно.
— Алекс Ласкин

Общий комментарий: создание объектов, хранящихся в памяти, из итераций может вызвать проблемы с памятью. Предложите проверить объем памяти и размер повторяемого исходного файла. Основное преимущество (весь смысл?) Итераций в том, что они не хранят большие объекты в памяти.
— путевые

@Nate: Это может быть исправлено при необходимости, обернув filterвызов map(operator.itemgetter(slice(2)), ...), поэтому он будет тянуть только первые два iterms, что делает его: dict(map(operator.itemgetter(slice(2)), filter(None, csv.reader(f)))). Если это Python 2, обязательно сделайте from future_builtins import map, filterтак, чтобы он dictсчитывал генератор напрямую, вместо того, чтобы listсначала создавать несколько ненужных временных s).
— ShadowRanger

12

Вы также можете использовать NumPy для этого.

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

5

Я бы предложил добавить if rowsв случае, если в конце файла есть пустая строка

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)

И хорошо, и хорошо продумано. Но, как я сказал выше, должен ли он действительно игнорировать тот факт, что его строка ввода длиннее, чем он ожидал? Я бы сказал, что он должен поднять свое собственное исключение (с пользовательским сообщением), если он получит строку с более чем двумя элементами.
— машина тоскует

Вернее, как сказано выше @Nate, хотя бы напечатать предупреждающее сообщение. Это просто не похоже на то, что вы хотели бы игнорировать.
— машина тоскует

Ваш ответ (против моего) заставил задуматься - есть ли разница в эффективности между нарезкой и индексацией в этом случае?
— конец

1
@ машина, понятия не имею. Возможно, это дамп пользовательской таблицы из базы данных, и он просто хочет указать идентификатор пользователя: например, имя пользователя или что-то в этом роде
— John La Rooy

1
Привет, ребята, спасибо за комментарии. Ваше обсуждение действительно помогло мне с моей проблемой. Мне нравится идея о поднятии флага, если ввод длиннее, чем ожидалось. Мои данные - это дамп базы данных, и у меня есть более двух столбцов данных.
— drbunsen


3

Если вы согласны с использованием numpy пакета, вы можете сделать что-то вроде следующего:

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

Для простых файлов CSV, таких как следующие

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

Вы можете преобразовать его в словарь Python, используя только встроенные модули

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

Это должно привести к следующему словарю

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

Примечание. Словари Python имеют уникальные ключи, поэтому, если ваш CSV-файл содержит дубликаты, idsвы должны добавить каждую строку в список.

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

Но все это можно сократить, используя set_default: csv_dict.set_default (ключ, []). append ({ключ: значение для ключа, значение в zip (заголовок, значения)}))
— mdmjsh

Синтаксис ({key: value}) в вашей .appendкоманде был очень полезен. В итоге я использовал тот же синтаксис в row.updateитерации и добавлении к DictReaderобъекту, который был сделан из файла CSV.
— Shrout1

1

Вы можете использовать это, это довольно круто:

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

Было опубликовано много решений, и я хотел бы внести свой вклад в мое, которое работает для другого числа столбцов в файле CSV. Он создает словарь с одним ключом на столбец, а значением для каждого ключа является список с элементами в этом столбце.

    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

например, с пандами это намного проще. при условии, что у вас есть следующие данные в виде CSV, и давайте назовем их test.txt/ test.csv(вы знаете, CSV - это своего рода текстовый файл)

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

теперь с помощью панд

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

для каждого ряда это будет

df.to_dict(orient='records')

и это все.


0

Попробуйте использовать defaultdictи 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)

Возвращает:

{'key1':[value_1, value_2, value_3], 'key2': [value_a, value_b, value_c], 'Key3':[value_x, Value_y, Value_z]}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.