如何将带有嵌套字典的列表写入 csv 文件?
Posted
技术标签:
【中文标题】如何将带有嵌套字典的列表写入 csv 文件?【英文标题】:How to write a list with a nested dictionary to a csv file? 【发布时间】:2013-11-23 10:32:41 【问题描述】:我是 Python 新手,尝试了所有我能想到的方法,但找不到解决方案。 我有一个列表,其中包含一个字典作为其最后一项,具有不同数量的键,看起来像。
l = [('Apple', 1, 2, 'gala': (2, 1.0)),
('Grape ', 2, 4, 'malbec': (4, 0.25), 'merlot': (4, 0.75)),
('Pear', 4, 5, 'anjou': (5, 0.2), 'bartlet': (5, 0.4), 'seckel': (5, 0.2)),
('Berry', 5, 5, 'blueberry': (5, 0.2), 'blackberry': (5, 0.2), 'straw': (5, 0.2))]
当我尝试从当前列表中写入 .csv 文件时,我使用了:
test_file = ()
length = len(l[0])
with open('test1.csv', 'w', encoding = 'utf-8') as test_file:
csv_writer = csv.writer(test_file, delimiter=',')
for y in range(length):
csv_writer.writerow([x[y] for x in l])
它使列表中的最后一个元素,即字典,成为输出文件中的一个字符串:
Apple 1 2 'gala': (2, 1.0)
Grape 2 4 'malbec': (4, 0.25), 'merlot': (4, 0.75)
Pear 4 5 'anjou': (5, 0.2), 'bartlet': (5, 0.4), 'seckel': (5, 0.2), 'bosc': (5, 0.2)
Berry 5 5 'blueberry': (5, 0.2), 'blackberry': (5, 0.2), 'straw': (5, 0.2)
这使得对最后一项内的值的任何操作都无法进行。
我试图展平嵌套字典,这样我就可以得到一个简单的列表,但结果并没有保留项目之间的关系。我需要的是拆分字典并得到一个看起来有点像这样的输出:
Apple 1 2 gala 2 1.0
Grape 2 4 malbec 4 0.25
merlot 4 0.75
Pear 4 5 anjou 5 0.2
bartlet 5 0.4
seckel 5 0.2
bosc 5 0.2
Berry 5 5 blueberry 5 0.2
blackberry 5 0.2
straw 5 0.2
我的意思有点像这样,因为我不致力于这种格式,而是相信字典的层次关系不会在输出文件中丢失。有没有办法做到这一点?我对python真的很陌生,感谢任何帮助。谢谢!
【问题讨论】:
你在这里混合了苹果和橘子。有很多方法可以像 csv 一样以平面格式表示树,但除非您有充分的理由坚持使用 csv,否则您应该将其保存为允许您保存 dicts 的格式。 JSON 文件可能是一种选择。 【参考方案1】:这是一个快速函数,我对其进行了修改以获取列表、元组或字典并将其展平。它将展平所有嵌套的部分。
我修改了您的代码并在 python 2.7 中进行了测试。这应该会生成您正在寻找的输出:
def flatten(l):
'''
flattens a list, dict or tuple
'''
ret = []
for i in l:
if isinstance(i, list) or isinstance(i, tuple):
ret.extend(flatten(i))
elif isinstance(i, dict):
ret.extend(flatten(i.items()))
else:
ret.append(i)
return ret
l = [('Apple', 1, 2, 'gala': (2, 1.0)),
('Grape ', 2, 4, 'malbec': (4, 0.25), 'merlot': (4, 0.75)),
('Pear', 4, 5, 'anjou': (5, 0.2), 'bartlet': (5, 0.4), 'seckel': (5, 0.2)),
('Berry', 5, 5, 'blueberry': (5, 0.2), 'blackberry': (5, 0.2), 'straw': (5, 0.2))]
test_file = ()
length = len(l[0])
with open('test1.csv', 'wb') as test_file:
csv_writer = csv.writer(test_file, delimiter=',')
for y in range(length):
line = flatten(l[y])
csv_writer.writerow([x for x in line])
【讨论】:
感谢您的建议和时间。我不断收到错误消息:TypeError:'str' 不支持缓冲区接口。知道为什么吗? 在最后一行:csv_writer.writerow([x for x in line])
,这可能与 Python 3 中的编码“utf-8”有关。但我自己无法解决。有任何想法吗?再次感谢。
我把整数写成整数,而不是字符串。如果您将最后一行更改为csv_writer.writerow([str(x) for x in line])
是否有效?如果您想要 UTF-8 格式的字符串,您应该可以将其更改为 [str(x).encode('utf-8')
@emf。不幸的是,没有。同样的错误:TypeError: 'str' does not support the buffer interface
.
听起来像是 Python 3 的问题。检查这个问题,它可能会有所帮助:***.com/questions/5471158/…【参考方案2】:
如果您坚持使用 CSV/TSV,则应记住它是表格的表示,但您希望它看起来像结构化文件 (XML/JSON/YAML)。我建议使用 CSV/TSV 将数据存储为关系表,否则您可能会遇到一些混乱的输出。在您的情况下,选择的选项将输出如下:
标题:
SuperSpecieName,SpecieName,Value1,Value2
数据:
"",Apple,1,2
Apple,gala,2,1.0
"",Grape,2,4
Grape,malbec,4,0.25
Grape,merlot,4,0.75
...
【讨论】:
【参考方案3】:假设您必须将其存储在 CSV 中,dict 中每个项目一行,以下显示了您可以如何编写和读取它。如果您有大量数据,这既不是有效的也不是最优的,因为它在每一行中重复数据,但是它会很好地压缩。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""csv_dict.py
"""
import csv
import pprint
from collections import namedtuple
Row = namedtuple('Row', [
'name',
'value_1',
'value_2',
'extra_name',
'extra_value_1',
'extra_value_2'
])
l = [
('Apple', 1, 2, 'gala': (2, 1.0)),
('Grape ', 2, 4, 'malbec': (4, 0.25), 'merlot': (4, 0.75)),
('Pear', 4, 5,
'anjou': (5, 0.2),
'bartlet': (5, 0.4),
'seckel': (5, 0.2)
),
('Berry', 5, 5,
'blueberry': (5, 0.2),
'blackberry': (5, 0.2),
'straw': (5, 0.2)
)
]
print('List before writing: ')
pprint.pprint(l)
# Writing the data.
with open('test1.csv', 'wb') as fout:
writer = csv.writer(fout)
for row in l:
for k, v in row[3].iteritems():
writer.writerow(row[0:3] + (k,) + v)
# Reading the data.
format_extra = lambda row: (int(row.extra_value_1), float(row.extra_value_2))
with open('test1.csv', 'rU') as fin:
reader = csv.reader(fin)
ll = []
hl =
for row in (Row(*r) for r in reader):
if row.name in hl:
ll[hl[row.name]][3][row.extra_name] = format_extra(row)
continue
ll.append(row[0:3] + (
row.extra_name: format_extra(row)
,))
hl[row.name] = len(ll) - 1
pprint.pprint(ll)
【讨论】:
感谢您花时间帮助我。我喜欢你的解决方案,但它对我不起作用,因为我得到一个 AttributeError: 'dict' object has no attribute 'iterates' after: for row in l: for k, v in row[3].iteritems(): What do你认为这可能是造成这种情况的原因吗? @user2962024 你错字了“iteritems”吗?你在 Python 3 上运行吗?在 py3k 中,iteritems()
已被替换为 items()
。我可以确认我上面的示例在 py2.7 上运行。
我使用的是 Python 3.2,我将 iteritems()
替换为 items()
,我应该多加注意。但现在我在同一点得到TypeError: 'str' does not support the buffer interface
。
@user2962024 将with open('test1.csv', 'wb') as fout
更改为with open('test1.csv', 'w', newline='') as fout
应该可以解决这个问题。如果可以,请在帖子中提及您正在使用的 python 版本,或者在将来作为标签。让它更容易。
我知道问题与 python 3 不允许将“字符串”序列化为字节而不显式转换为某些编码有关。我尝试使用encode = 'utf-8'
,但它不适用于列表。关于如何进行的任何建议?【参考方案4】:
看来你已经很接近了。几点——你不需要初始化test_file
,你可以把length
放在迭代器中。
如果我将其写入 csv,我可能会使用
with open('test1.csv', 'w', encoding = 'utf-8') as test_file:
for row in l:
species_data = row[:3]
for subspecies, subspecies_data in row[4].iter_items():
write_row = species_data + [subspecies] + list(subspecies_data)
test_file.write(','.join([str(j) for j in write_row]))
如果列表很大,或者您非常担心重复信息,您当然可以进行优化。
【讨论】:
以上是关于如何将带有嵌套字典的列表写入 csv 文件?的主要内容,如果未能解决你的问题,请参考以下文章