将字典数组写入CSV

时间:2014-10-10 01:50:13

标签: python csv python-3.x traceback

我试图将字典(程序的第一部分生成)写入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'}

1 个答案:

答案 0 :(得分:1)

我相信你的问题在这里:

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

这是创建两个词典的列表。第一个只有一个&#34;位置&#34;键,第二个只有一个&#34;卷&#34;键。但是,csv.DictWriter对象期望每行一个字典,并且字典中包含所有键。将该代码段更改为以下内容,它应该可以正常工作:

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

这应该照顾你所看到的错误。

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

"Location"
"Volume"

以任何顺序(因为Python dict不保证它将返回其键的顺序)。他们希望您传入fieldnames列表的原因是这些字段可以按正确的顺序写入CSV。但是,因为您在两个词典的列表中传递,当它遍历row参数时,它会得到以下内容:

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

现在,字典{'Location': 'SABIN'}不等于字符串"Location",而字典{'Volume': '247'}不等于字符串"Volume",因此writerow()函数认为它发现你提供的fieldnames列表中没有dict键,它会引发异常。 真正正在发生的是&#34;当我预期单个dict-with-two-key&#34;时,你传给我一个两个单键的列表,但是这个功能没有写过来检查那个特定的错误。


现在我要提到一些可以加快代码速度的事情。有一点可以帮助我们将代码开头的三个for循环减少到只有一个。您尝试做的是浏览原始数据,并计算每个邻域出现的次数。首先,我将向您展示一种更好的方法,然后我会向您展示一种更好的方式,以改善我的第一个解决方案。

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

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

然而,有一种方法可以进一步改善这一点。来自Counter模块的collections对象仅适用于此情况,并具有一些方便的额外功能,例如检索N个最常见项目的功能。所以最终最终版本:-)我所建议的是:

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