【问题标题】:Creating a dictionary from a csv file?从 csv 文件创建字典?
【发布时间】:2011-10-08 03:04:50
【问题描述】:

我正在尝试从 csv 文件创建字典。 csv 文件的第一列包含唯一键,第二列包含值。 csv 文件的每一行代表字典中唯一的键值对。我尝试使用csv.DictReadercsv.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 文件创建一个字典?谢谢。

【问题讨论】:

  • 你能举一个输入文件和结果数据结构的例子吗?
  • 当您遍历 csv.reader 时,您会得到单行,而不是行。因此,有效形式是 mydict = {k:v for k,v in reader} 但如果您确定 csv 文件中只有两列,那么 mydict = dict(reader) 会快得多。

标签: python csv dictionary list-comprehension


【解决方案1】:

假设您有一个这种结构的 CSV:

"a","b"
1,2
3,4
5,6

你希望输出是:

[{'a': '1', ' "b"': '2'}, {'a': '3', ' "b"': '4'}, {'a': '5', ' "b"': '6'}]

zip 功能(尚未提及)非常简单且非常有用。

def read_csv(filename):
    with open(filename) as f:
        file_data=csv.reader(f)
        headers=next(file_data)
        return [dict(zip(headers,i)) for i in file_data]

如果你更喜欢 pandas,它也可以很好地做到这一点:

import pandas as pd
def read_csv(filename):
    return pd.read_csv(filename).to_dict('records')

【讨论】:

    【解决方案2】:

    通过调用 open 然后使用 csv.DictReader 打开文件。

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

    您可以通过遍历 input_file 来遍历 csv 文件 dict reader 对象的行。

    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) 
    

    【讨论】:

    • 这使得 DictReader 对象不是字典(而且不是键值对)
    • @HN Singh - 是的,我知道 - 目的是它也会帮助其他人
    • 'DictReader' 对象没有属性 'next'
    • @Palak - Python 2.7 已回答,请在 Python 3+ 版本中尝试使用 next(dictobj) 而不是 dictobj.next()
    【解决方案3】:

    如果你有:

    1. 您的 csv 中只有 1 个键和 1 个值作为键、值
    2. 不想导入其他包
    3. 想一次性创建一个字典

    这样做:

    mydict = {y[0]: y[1] for y in [x.split(",") for x in open('file.csv').read().split('\n') if x]}
    

    它有什么作用?

    它使用列表解析来拆分行,最后一个“if x”用于忽略空行(通常在末尾),然后使用字典解析将其解压缩为字典。

    【讨论】:

      【解决方案4】:

      我相信您正在寻找的语法如下:

      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

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

      【讨论】:

      • 很好地考虑了比预期更长的行;但是如果连续的项目太多,他不应该提出自己的例外吗?我认为这意味着他的输入数据有错误。
      • 然后他至少可以将异常缩小到错误输入
      • 这有一些优点,但我坚信例外情况会告诉你你的程序有错误——而不是当世界给你柠檬的时候。那是您打印一条漂亮的错误消息并失败的时候,或者 - 更适合这种情况 - 一条漂亮的警告消息并成功。
      • 抱歉,查看了 op 的代码,很难判断他是否只需要每行 2 个项目。我错了!
      • 我在 csv 中有多行,但它只给出了 1 个键:值对
      【解决方案5】:

      例如,使用 pandas 就容易多了。 假设您有以下 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')
      

      就是这样。

      【讨论】:

        【解决方案6】:

        对于简单的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)})
        

        【讨论】:

        • n.b.这都可以缩短为使用set_default: csv_dict.set_default(key, []).append({key: value for key, value in zip(header, values)}))
        • .append 命令中的 ({key: value}) 语法非常有用。我最终在 row.update 中使用了相同的语法,然后迭代并添加到由 CSV 文件制成的 DictReaderobject。
        【解决方案7】:

        已经发布了许多解决方案,我想为我的解决方案做出贡献,它适用于 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])
        

        【讨论】:

          【解决方案8】:

          尝试使用defaultdictDictReader

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

          【讨论】:

            【解决方案9】:

            这并不优雅,而是使用 pandas 的单行解决方案。

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

            如果您想为索引指定 dtype(如果您使用 index_col 参数,则无法在 read_csv 中指定,因为bug):

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

            【讨论】:

            • 在我的书中这是最好的答案
            • 如果有标题...?
            • @ndtreviv 你可以使用skirows来忽略标题。
            【解决方案10】:

            单线解决方案

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

            【讨论】:

              【解决方案11】:

              你可以用这个,很酷:

              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
              

              【讨论】:

                【解决方案12】:

                如果您可以使用 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]
                

                【讨论】:

                  【解决方案13】:

                  您也可以为此使用 numpy。

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

                  【讨论】:

                    【解决方案14】:

                    您只需将 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'}
                    

                    【讨论】:

                    • 该解决方案很简洁,如果他能够确定他的输入在某行中永远不会包含三列或更多列,那么该解决方案将非常有效。但是,如果遇到这种情况,将会引发类似这样的异常:ValueError: dictionary update sequence element #2 has length 3; 2 is required
                    • @machine,从题中的错误来看,csv文件多于2列
                    • @gnibbler,不,问题中的错误是由于行的双重解包。首先,他尝试遍历 reader,获得实际上是单个 rowrows。当他尝试迭代这一行时,他得到了两个项目,无法正确解包。
                    • 一般性评论:将对象从可迭代对象保存在内存中可能会导致内存问题。建议检查您的内存空间和可迭代源文件的大小。可迭代对象的一个​​主要优点(重点?)是不在内存中保存大的东西。
                    • @Nate:如有必要,可以通过用map(operator.itemgetter(slice(2)), ...) 包装filter 调用来解决此问题,因此它只会拉取前两个项,使其成为:dict(map(operator.itemgetter(slice(2)), filter(None, csv.reader(f))))。如果是 Python 2,请确保执行from future_builtins import map, filter,这样dict 会直接读取生成器,而不是先生成多个不必要的临时lists。
                    【解决方案15】:

                    如果文件末尾有空行,我建议添加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 上面所说的,至少打印一条警告消息。这似乎不是您想要忽略的事情。
                    • 您的回答(与我的回答相比)令人深思——在这种情况下,切片和索引之间是否存在效率差异?
                    • @machine,不知道。也许它是数据库中用户表的转储,他只想要一个用户 ID:用户名或其他内容的字典
                    • 大家好,感谢 cmets。你的讨论真的帮助我解决了我的问题。如果输入比预期的长,我喜欢关于升旗的想法。我的数据是一个数据库转储,我确实有两列以上的数据。
                    【解决方案16】:
                    import csv
                    reader = csv.reader(open('filename.csv', 'r'))
                    d = {}
                    for row in reader:
                       k, v = row
                       d[k] = v
                    

                    【讨论】:

                    • 高度非 Python 风格。
                    • @Alex Laskin:真的吗?对我来说,它看起来像是一些非常易读的 python。你支持这个说法的原则是什么?你基本上只是称他为“poopy head”......
                    • @machine-yearning,不,我没有说他的代码“不好”。但是,例如,如果您可以简单地写for k, v in reader,那么写for row in reader: k, v = row 就没有单一的理由。如果你期望,那个 reader 是一个可迭代的,产生两个元素的项目,那么你可以简单地将它直接传递给 dict 进行转换。 d = dict(reader) 在大型数据集上更短且速度更快。
                    • @Alex Laskin:感谢您的澄清。我个人同意你的观点,但我认为如果你要称某人的代码为“非pythonic”,你应该在评论中附上理由。我会说“更短”和“更快”不一定等同于“更多pythonic”。可读性/可靠性也是一个巨大的问题。如果在上述for row in reader 范式中更容易处理我们的一些约束,那么它可能(在长期发展之后)更实用。我同意你的短期观点,但要提防过早的优化。
                    • @robert:谢谢老兄!真的有帮助。其他代码太难读了。
                    猜你喜欢
                    • 2016-09-03
                    • 2012-12-15
                    • 2012-01-02
                    • 2016-11-22
                    • 2015-09-16
                    相关资源
                    最近更新 更多