使用Python CSV DictReader创建多级嵌套字典

时间:2012-08-31 18:30:53

标签: python python-2.7 csv dictionary

这里的总Python noob,可能遗漏了一些明显的东西。我到处搜索,还没有找到解决方案,所以我想我会请求一些帮助。

我正在尝试编写一个从大型csv文件构建嵌套字典的函数。输入文件采用以下格式:

Product,Price,Cost,Brand,
blue widget,5,4,sony,
red widget,6,5,sony,
green widget,7,5,microsoft,
purple widget,7,6,microsoft,

等...

我需要的输出字典如下:

projects = { `<Brand>`: { `<Product>`: { 'Price': `<Price>`, 'Cost': `<Cost>` },},}

但很明显,许多不同的品牌都含有不同的产品。在输入文件中,数据按品牌名称按字母顺序排序,但我知道一旦DictReader执行它就会变得无序,所以我肯定需要一种更好的方法来处理重复项。写入的if语句是多余的,不必要。

这是我到目前为止无法使用的无用代码:

def build_dict(source_file):
  projects = {}
  headers = ['Product', 'Price', 'Cost', 'Brand']
  reader = csv.DictReader(open(source_file), fieldnames = headers, dialect = 'excel')
  current_brand = 'None'
  for row in reader:
    if Brand != current_brand:
      current_brand = Brand
    projects[Brand] = {Product: {'Price': Price, 'Cost': Cost}}
  return projects

source_file = 'merged.csv'
print build_dict(source_file)

我当然在文件顶部导入了csv模块。

最好的方法是什么?我觉得我已经离开了,但是关于从CSV创建嵌套dicts的信息很少,而且那里的例子非常具体,并且往往不详细解释为什么解决方案实际工作,所以作为Python的新手,有点难以得出结论。

此外,输入csv文件通常没有标题,但为了尝试获取此函数的工作版本,我手动插入标题行。理想情况下,会有一些代码分配标题。

非常感谢任何帮助/指导/推荐!谢谢!

2 个答案:

答案 0 :(得分:4)

import csv
from collections import defaultdict

def build_dict(source_file):
    projects = defaultdict(dict)
    headers = ['Product', 'Price', 'Cost', 'Brand']
    with open(source_file, 'rb') as fp:
        reader = csv.DictReader(fp, fieldnames=headers, dialect='excel',
                                skipinitialspace=True)
        for rowdict in reader:
            if None in rowdict:
                del rowdict[None]
            brand = rowdict.pop("Brand")
            product = rowdict.pop("Product")
            projects[brand][product] = rowdict
    return dict(projects)

source_file = 'merged.csv'
print build_dict(source_file)

产生

{'microsoft': {'green widget': {'Cost': '5', 'Price': '7'},
               'purple widget': {'Cost': '6', 'Price': '7'}},
 'sony': {'blue widget': {'Cost': '4', 'Price': '5'},
          'red widget': {'Cost': '5', 'Price': '6'}}}

来自您的输入数据(其中merged.csv没有标题,只有数据。)

我在这里使用了defaultdict,这就像字典一样,但是当你引用一个不存在而不是引发异常的键时,它只会产生一个默认值,在这种情况下为{{1 }}。然后我离开 - 并删除 - dictBrand,然后存储剩余部分。

我认为剩下的就是将成本和价格转换为数字而不是字符串。

[已修改为直接使用Product而不是DictReader]

答案 1 :(得分:0)

我在这里提供另一种满足您需求的方式(与DSM不同) 首先,这是我的代码:

import csv

new_dict={}
with open('merged.csv','rb')as csv_file:
    data=csv.DictReader(csv_file,delimiter=",")
    for row in data:
        dict_brand=new_dict.get(row['Brand'],dict())
        dict_brand[row['Product']]={k:row[k] for k in ('Cost','Price')}
        new_dict[row['Brand']]=dict_brand
print new_dict

简而言之,要解决的要点是弄清楚键值对在您的要求中是什么。根据你的要求,它可以被称为 3-level-dict ,这里第一级的键是原始字典中Brand int的值,所以我从中提取它原始csv文件为

dict_brand=new_dict.get(row['Brand'],dict())

将判断是否存在与我们的新词典中的原始词典相同的Brand值,如果是,它只是插入,如果不是,它创建,那么也许最复杂的部分是第二个级别或中级,在这里您将原始字典的Product的值设置为键Brand的新字典的值,Product的值也是该字符串的关键字。将原始字典的PriceCost作为值的第三级字典,在这里我将其提取为:

dict_brand[row['Product']]={k:row[k] for k in ('Cost','Price')}

最后,我们需要做的就是将创建的'中间字典'设置为新字典的值,其中Brand为关键字。 最后,输出是

{'sony': {'blue widget': {'Price': '5', 'Cost': '4'}, 
'red widget': {'Price': '6', 'Cost': '5'}}, 
'microsoft': {'purple widget': {'Price': '7', 'Cost': '6'}, 
'green widget': {'Price': '7', 'Cost': '5'}}}

就是这样。