将CSV值读入字典列表的最Pythonic方法

10

我有一个CSV文件,文件顶部是数据列的标题:

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

我需要将其读入一个字典的列表中:

desired_result = {'a': [1, 4, 7], 'b': [2, 5, 8], 'c': [3, 6, 9]}

使用DictReader读取时,我使用嵌套循环将项目添加到列表中:

f = 'path_to_some_csv_file.csv'
dr = csv.DictReader(open(f))
dict_of_lists = dr.next()
for k in dict_of_lists.keys():
    dict_of_lists[k] = [dict_of_lists[k]]
for line in dr:
    for k in dict_of_lists.keys():
        dict_of_lists[k].append(line[k])
第一个循环将字典中的所有值设置为空列表。下一个循环遍历从CSV文件中读取的每一行,DictReader从中创建键值对字典。内部循环将值附加到与相应键匹配的列表中,因此我最终得到所需的字典列表。我经常不得不写这个。
我的问题是,是否有一种更pythonic的方式可以使用内置函数完成此操作,而无需嵌套循环,或者使用更好的惯用语,或者以替代方式存储此数据结构,以便通过查询关键字返回可索引的列表?如果有,是否还有一种方法来预先格式化列被摄入的数据?

for k in dict_of_lists.keys(): 翻译为 for k in dict_of_lists: - Cristian Ciupitu
4个回答

13

根据您存储的数据类型以及是否可以使用numpy,一个很好的方法是使用numpy.genfromtxt函数:

import numpy as np
data = np.genfromtxt('data.csv', delimiter=',', names=True)

这将创建一个numpy 结构化数组,它提供了一个很好的接口,可以通过标题名称查询数据(如果有标题行,请确保使用names=True)。

例如,给定包含数据的data.csv

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

你可以使用以下方式访问元素:

>>> data['a']        # Column with header 'a'
array([ 1.,  4.,  7.])
>>> data[0]          # First row
(1.0, 2.0, 3.0)
>>> data['c'][2]     # Specific element
9.0
>>> data[['a', 'c']] # Two columns
array([(1.0, 3.0), (4.0, 6.0), (7.0, 9.0)],
      dtype=[('a', '<f8'), ('c', '<f8')])

genfromtext还提供了一种按列“格式化被摄入数据”的方法,正如您所请求的那样。

converters:可选变量,一组将列数据转换为值的函数。这些转换器也可用于为缺失数据提供默认值:converters = {3:lambda s:float(s or 0)}


2

如果您愿意使用第三方库,那么来自Toolzmerge_with函数可以将整个操作变为一行代码:

dict_of_lists = merge_with(list, *csv.DictReader(open(f)))

只使用stdlib,defaultdict可以使代码更简洁:

from collections import defaultdict
import csv

f = 'test.csv'

dict_of_lists = defaultdict(list)
for record in DictReader(open(f)):
    for key, val in record.items():    # or iteritems in Python 2
        dict_of_lists[key].append(val)

如果您需要经常执行此操作,请将其转换为函数,例如transpose_csv

1

Ford的回答没有问题,我在这里补充一下我的回答(使用了csv库)。

with open(f,'r',encoding='latin1') as csvf:
    dialect = csv.Sniffer().sniff(csvf.readline()) # finds the delimiters automatically
    csvf.seek(0)
    # read file with dialect
    rdlistcsv = csv.reader(csvf,dialect)
    # save to list of rows
    rowslist  = [list(filter(None,line)) for line in rdlistcsv]
    header = rowslist[0]
    data = {}
    for i,key in enumerate(header):
        ilist = [row[i] for row in rowslist]
        data.update({key: ilist})

编辑: 实际上,如果您不介意使用Pandas,那么用它会更加容易:

  1. import pandas

    import pandas as pd
    
  2. import file and save it as pandas dataframe

    df = pd.read_csv(inputfile)
    
  3. turn df into a dictionary

    mydict = df.to_dict(orient='list')
    
这种方式使用CSV头来定义键,对于每个键,您都有一个元素列表(类似于将Excel列转换为列表)。

-2
你可以使用字典和集合推导式来使你的意图更加明确:
dr=csv.DictReader(f)
data={k:[v] for k, v in dr.next().items()}             # create the initial dict of lists
for line_dict in dr:
    {data[k].append(v) for k, v in line_dict.items()}  # append to each

您可以使用Alex Martelli的方法来将Python中的列表列表扁平化为迭代器,进一步将第一种形式缩小为:
dr=csv.DictReader(f)
data={k:[v] for k, v in dr.next().items()}
{data[k].append(v) for line_dict in dr for k, v in line_dict.items()}

在Python 2.X中,如果你的CSV文件很大,请考虑使用{}.iteritems而不是{}.items()

更进一步的例子:

假设这是一个csv文件:

Header 1,Header 2,Header 3
1,2,3
4,5,6
7,8,9

现在假设您想要一个字典,其中包含每个值转换为浮点数或整数的列表。您可以执行以下操作:

def convert(s, converter):
    try:
        return converter(s)
    except Exception:
        return s    

dr=csv.DictReader(f)
data={k:[convert(v, float)] for k, v in dr.next().items()}
{data[k].append(convert(v, float)) for line_dict in dr for k, v in line_dict.items()}

print data
# {'Header 3': [3.0, 6.0, 9.0], 'Header 2': [2.0, 5.0, 8.0], 'Header 1': [1.0, 4.0, 7.0]}

2
通常不建议使用推导式(在这种情况下,是集合推导式)来执行循环,这不符合 Pythonic 风格。最好直接编写显式的循环语句,这样更自然且更清晰地表达你的意图。 - Blckknght

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接