【问题标题】:Writing a Array of Dictionaries to CSV将字典数组写入 CSV
【发布时间】:2014-10-10 01:50:12
【问题描述】:

我正在尝试将字典(程序的第一部分生成)写入 csv,以便我可以对 excel 中的数据执行进一步的操作。我意识到代码效率不高,但此时我只想让它工作。稍后我可以处理加快速度。

import csv
import pprint

raw_data = csv.DictReader(open("/Users/David/Desktop/crimestats/crimeincidentdata.csv", "r"))

neighborhood = []
place_count = {}
stats = []

for row in raw_data:
    neighborhood.append(row["Neighborhood"])

for place in set(neighborhood):
    place_count.update({place:0})

for key,value in place_count.items():
    for place in neighborhood:
        if key == place:
            place_count[key] = place_count[key]+1

for key in place_count:
    stats.append([{"Location":str(key)},{"Volume":str(place_count[key])}])

pp = pprint.PrettyPrinter(indent=4)
pp.pprint(stats)

程序在这里仍然运行良好,从 pprint 输出可以看出

[   [{'Location': 'LINNTON'}, {'Volume': '109'}],
    [{'Location': 'SUNDERLAND'}, {'Volume': '118'}],
    [{'Location': 'KENTON'}, {'Volume': '715'}]  

这是肯定发生错误的地方。程序将标头写入 csv 就好了,然后抛出 ValueError。

fieldnames = ['Location', 'Volume']
with open('/Users/David/Desktop/crimestats/localdata.csv', 'w', newline='') as output_file:
    csvwriter = csv.DictWriter(output_file, delimiter=',', fieldnames=fieldnames, dialect='excel')
    csvwriter.writeheader()
for row in stats:
    csvwriter.writerow(row)
output_file.close()

我花了很多时间寻找这个问题,但我尝试使用的建议都没有奏效。我想我一定是错过了一些东西,所以我非常感谢任何和所有的帮助。

Traceback (most recent call last):
  File "/Users/David/Desktop/crimestats/statsreader.py", line 34, in <module>
    csvwriter.writerow(row)
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/csv.py", line 153, in writerow
    return self.writer.writerow(self._dict_to_list(rowdict))
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/csv.py", line 149, in _dict_to_list
+ ", ".join([repr(x) for x in wrong_fields]))
ValueError: dict contains fields not in fieldnames: {'Location': 'SABIN'}, {'Volume': '247'}

【问题讨论】:

    标签: python csv python-3.x traceback


    【解决方案1】:

    我相信你的问题就在这里:

    for key in place_count:
        stats.append([{"Location":str(key)},{"Volume":str(place_count[key])}])
    

    这是创建两个字典的列表。第一个只有一个“位置”键,第二个只有一个“音量”键。但是,csv.DictWriter 对象期望每行有一个字典,字典中包含所有键。将该代码 sn-p 更改为以下内容,它应该可以工作:

    for key in place_count:
        stats.append({"Location": str(key), "Volume": str(place_count[key])})
    

    这应该可以解决您看到的错误。

    现在,至于为什么错误消息抱怨字段不在字段名中,这完全误导了您遇到的真正问题:writerow() 函数期望将字典作为其行参数,但是您'正在传递一个列表。结果是混乱:它在 for 循环中迭代字典,期望得到字典的键(因为这是你在 Python 中迭代字典时得到的),并将这些键与 @987654327 中的值进行比较@ 列表。它期望看到的是:

    "Location"
    "Volume"
    

    以任意顺序(因为 Python dict 不保证它将返回其键的顺序)。他们希望您传递fieldnames 列表的原因是可以将字段以正确的顺序写入CSV。但是,因为您传入的是一个包含两个字典的列表,所以当它遍历 row 参数时,它会得到以下信息:

    {'Location': 'SABIN'}
    {'Volume': '247'}
    

    现在,字典{'Location': 'SABIN'} 不等于字符串"Location",字典{'Volume': '247'} 不等于字符串"Volume",所以writerow() 函数认为它找到了不在的字典键您提供的fieldnames 列表,它会抛出该异常。 真正发生的事情是“你传递给我一个包含两个 dicts-of-one-key 的列表,而我期望有一个 dict-with-two-keys”,但该函数并未写入检查那个特定的错误。


    现在我将提到一些您可以做的事情来加速您的代码。有一点帮助的事情是将代码开头的三个for 循环减少到一个。您要做的是检查原始数据,并计算每个社区出现的次数。首先,我将向您展示一种更好的方法,然后我将向您展示一种更好的方法,该方法改进了我的第一个解决方案。

    更好的方法是利用 Python 在 collections 模块中提供的美妙的 defaultdict 类。 defaultdict 是 Python 字典类型的子类,第一次访问时会自动创建字典条目。它的构造函数采用单个参数,该函数将在没有参数的情况下调用,并且应该为任何新项目返回所需的默认值。如果您使用 defaultdict 作为您的 place_count 字典,则此代码:

    place_count = {}
    for place in set(neighborhood):
        place_count.update({place:0})
    

    可以简单地变成:

    place_count = defaultdict(int)
    

    这里发生了什么?好吧,int 函数(它实际上不是一个函数,它是 int 类的构造函数,但这有点超出了本说明的范围)如果在没有参数的情况下调用它,它恰好返回 0。因此,与其编写自己的函数def returnzero(): return 0,不如使用现有的int 函数(好的,构造函数)。现在每次你执行place_count["NEW PLACE"]NEW PLACE 键值会自动出现在你的place_count 字典中,值为 0。

    现在,您的计数循环也需要修改:它曾经遍历 place_count 的键,但现在 place_count 在第一次访问它们时会自动创建它的键,您需要一个不同的来源。但是您仍然在原始数据中拥有该来源:每行的 row["Neighborhood"] 值。所以你的for key,value in place_count.items(): 循环可能变成:

    for row in raw_data:
        place = row["Neighborhood"]
        place_count[place] = place_count[place] + 1
    

    现在您使用的是defaultdict,您甚至根本不需要第一个循环(创建neighborhood 列表的那个)!所以我们刚刚将三个循环合二为一。我建议的最终版本如下所示:

    from collections import defaultdict
    place_count = defaultdict(int)
    for row in raw_data:
        place = row["Neighborhood"]
        place_count[place] = place_count[place] + 1
        # Or: place_count[place] += 1
    

    但是,有一种方法可以进一步改进。 collections 模块中的 Counter 对象就是为这种情况而设计的,并且具有一些方便的额外功能,例如检索 N 个最常见项目的能力。所以我建议的 final 最终版本 :-) 是:

    from collections import Counter
    place_count = Counter()
    for row in raw_data:
        place = row["Neighborhood"]
        place_count[place] = place_count[place] + 1
        # Or: place_count[place] += 1
    

    这样,如果您需要检索犯罪率最高的 5 个街区,您只需致电 place_count.most_common(5)

    您可以在documentation for the collections module 中阅读有关Counterdefaultdict 的更多信息。

    【讨论】:

    • 效果很好。谢谢你。我还应该注意任何尝试这样做的人,我必须编辑最后三行,因为最终的 for 循环没有正确缩进。
    • @DavidKatz-Wigmore - 我刚刚添加了一些加快代码速度的建议,在标准库中使用了一些 Python 最有用的函数(和类)。
    猜你喜欢
    • 2017-03-29
    • 1970-01-01
    • 2018-03-23
    • 2021-05-15
    • 1970-01-01
    • 2012-11-04
    • 2018-06-25
    • 2018-03-19
    • 2016-12-12
    相关资源
    最近更新 更多