如何递归地从提供的数据创建字典结构?

时间:2018-02-09 10:23:46

标签: python dictionary recursion

我有下面提到的元组列表,其中元组中的第一个元素是基于我想要以特定格式生成字典的键。

# list variable
x = [  
(('Fresh Vegetables', 'Hamel', '2006', "Jimmy's Ebony Sweet Peas"), 209034.60000000001), 

(('Fresh Vegetables', 'Hamel', '2006', "Sue's Ebony Asparagus"), 217158.67000000001), 

(('Fresh Vegetables', 'Mentor', '2007', "Jimmy's Ebony Sweet Peas"), 225630.60000000001), 

(('Fresh Vegetables', 'Mentor', '2007',"Sue's Ebony Asparagus"), 233201.47), 

(('Fresh Vegetables', 'Hamel', '2006', "Bubba's Ebony Corn on the Cob"), 317588.95000000001), 

(('Fresh Vegetables','Mentor', '2007', "Bubba's Ebony Corn on the Cob"), 343151.59000000003), 

(('Fresh Vegetables', 'Sioux Center', '2007', "John's Ebony Squash"), 401308.01 000000001)
]

我想要一个下面给出的字典结构。其中“”键是该字典中每个字典元素总数的总和。

finalX = {
   'Fresh Vegetables':{

       'Hamel':{

           '2006':{

                 "Jimmy's Ebony Sweet Peas":209034.60000000001,
                 "Sue's Ebony Asparagus":217158.67000000001,
                 "Bubba's Ebony Corn on the Cob":317588.95000000001,
                 "total": 743782.22
            }
           "total":743782.22
       },   

       'Mentor':{

           '2007':{

               "Jimmy's Ebony Sweet Peas":225630.60000000001,
               "Sue's Ebony Asparagus":233201.47,
               "Bubba's Ebony Corn on the Cob":343151.59000000003,
               "total": 801983.66
           }
           "total":801983.66
       },

       'Sioux Center':{

           '2007':{

               "John's Ebony Squash":401308.01000000001
               "total":401308.01
           }
           "total":401308.01
       }
       "total":1947073.89
   }
}

我知道没有递归就不可能,但我被困在那里。我该如何实现它?我尝试了一些东西,但却无法做到。

4 个答案:

答案 0 :(得分:2)

这是一种方式:

from collections import defaultdict

d = defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(float))))

for k, v in x:
    d[k[0]][k[1]][k[2]][k[3]] = v

for k1 in d:
    d[k1]['total'] = sum(d[k1][j][k][l] for j in d[k1] for k in d[k1][j] for l in d[k1][j][k])
    for k2 in set(d[k1])-{'total'}:
        d[k1][k2]['total'] = sum(d[k1][k2][k][l] for k in d[k1][k2] for l in d[k1][k2][k])
        for k3 in set(d[k1][k2])-{'total'}:
            d[k1][k2][k3]['total'] = sum(d[k1][k2][k3][l] for l in d[k1][k2][k3])

答案 1 :(得分:2)

迭代方法:首先使它成为一个字典,然后添加小计:

import pprint

x = [
(('Fresh Vegetables', 'Hamel', '2006', "Jimmy's Ebony Sweet Peas"), 209034.60000000001),    
(('Fresh Vegetables', 'Hamel', '2006', "Sue's Ebony Asparagus"), 217158.67000000001),    
(('Fresh Vegetables', 'Mentor', '2007', "Jimmy's Ebony Sweet Peas"), 225630.60000000001),     
(('Fresh Vegetables', 'Mentor', '2007',"Sue's Ebony Asparagus"), 233201.47),    
(('Fresh Vegetables', 'Hamel', '2006', "Bubba's Ebony Corn on the Cob"), 317588.95000000001),     
(('Fresh Vegetables','Mentor', '2007', "Bubba's Ebony Corn on the Cob"), 343151.59000000003),     
(('Fresh Vegetables', 'Sioux Center', '2007', "John's Ebony Squash"), 401308.01000000001)]

创建字典:

XX = {}
for n in x:
  what = n[0][0]
  wher = n[0][1]
  when = n[0][2]
  at   = n[0][3]
  amou = n[1]

  XX.setdefault(what,{}).setdefault(wher,{}).setdefault(when,{}).setdefault(at,0.0)    
  XX[what][wher][when][at] += amou

添加所有小计(有关较短版本,请参阅下面的编辑)

# create the "total" sums in between:
for whatKey in XX:
  for wherKey in XX[whatKey]:
    for whenKey in  XX[whatKey][wherKey]:
      XX[whatKey][wherKey][whenKey]["total"] = 0.0
      for atKey in XX[whatKey][wherKey][whenKey]:
        if atKey != "total":
          XX[whatKey][wherKey][whenKey]["total"] += XX[whatKey][wherKey][whenKey][atKey]

for whatKey in XX:
  for wherKey in XX[whatKey]:
    for whenKey in  XX[whatKey][wherKey]:
      XX[whatKey][wherKey][whenKey]["total"] = 0.0
      for atKey in XX[whatKey][wherKey][whenKey]:
        if atKey != "total":
          XX[whatKey][wherKey][whenKey]["total"] += XX[whatKey][wherKey][whenKey][atKey]

for whatKey in XX:
  for wherKey in XX[whatKey]:
    XX[whatKey][wherKey]["total"] = 0
    for whenKey in  XX[whatKey][wherKey]:
      if whenKey != "total":
        XX[whatKey][wherKey]["total"] += XX[whatKey][wherKey][whenKey]["total"]

for whatKey in XX:
  XX[whatKey]["total"] = 0
  for wherKey in XX[whatKey]:
      if wherKey!= "total":
        XX[whatKey]["total"] += XX[whatKey][wherKey]["total"] 

pprint.pprint(XX)

输出(重新格式化):

{'Fresh Vegetables': 
    {'Hamel': 
        {'2006': {"Bubba's Ebony Corn on the Cob": 317588.95,
                  "Jimmy's Ebony Sweet Peas": 209034.6,
                  "Sue's Ebony Asparagus": 217158.67,
                  'total': 743782.22},
         'total': 743782.22},

     'Mentor': 
         {'2007': {"Bubba's Ebony Corn on the Cob": 343151.59,
                   "Jimmy's Ebony Sweet Peas": 225630.6,
                   "Sue's Ebony Asparagus": 233201.47,
                   'total': 801983.66},
          'total': 801983.66},

     'Sioux Center': 
         {'2007': {"John's Ebony Squash": 401308.01,
                   'total': 401308.01},
          'total': 401308.01},

     'total': 1947073.89}}

编辑避免多次完整for - 迭代:

# create the "total" sums in between:
for whatKey in XX: 
  if whatKey == "total":
    continue
  XX[whatKey].setdefault("total",0)

  for wherKey in XX[whatKey]:
    if wherKey == "total":
      continue
    XX[whatKey][wherKey].setdefault("total",0)

    for whenKey in  XX[whatKey][wherKey]: 
      if whenKey == "total":
        continue      
      XX[whatKey][wherKey][whenKey].setdefault("total",0)

      for atKey in XX[whatKey][wherKey][whenKey]:
        if atKey == "total":
          continue

        XX[whatKey][wherKey][whenKey]["total"] += XX[whatKey][wherKey][whenKey][atKey]
        XX[whatKey][wherKey]["total"] += XX[whatKey][wherKey][whenKey][atKey]
        XX[whatKey]["total"] += XX[whatKey][wherKey][whenKey][atKey]

答案 2 :(得分:2)

更具人性化的解决方案。

这里只是主要的词典部分。我要说明的一点是,即使使用简单的递归,也可以实现一些通用的解决方案。

但是,对于小计,它需要一些额外的操作,这在此解决方案中尚未涉及。

from functools import reduce
import collections

# function to convert a list/tuple to dictionary tree
def totree(keys, value):
  if len(keys) == 1:
    return {keys[0]: value}
  else:
    return {keys[0]: totree(keys[1:], value)}

# function to update nested dictionary
def update(d, u):
    for k, v in u.items():
        if isinstance(v, collections.Mapping):
            d[k] = update(d.get(k, {}), v)
        else:
            d[k] = v
    return d

# the expected dictionary is saved to d
d = reduce(lambda d1, d2: update(d1, d2), map(lambda sub:totree(*sub), x)))

print(d)

答案 3 :(得分:0)

local_dict = {}
for veg in x:
    item,center, year, dish = veg[0]
    amount  = veg[1]
    if item in local_dict:
        data = local_dict[item]
        if center in data:
            data_center = data[center]
            data_center['total'] += amount
            if year in data_center:
                get_dict = data_center[year]
                if dish in get_dict or (dish not in get_dict):
                    get_dict[dish] = amount
                get_dict["total"] += amount
            else:
                data_center[year] = {dish:amount,"total":amount}
                data_center["total"] = amount
        else:
            data[center] = {year:{dish:amount,"total":amount},"total":amount}
    else:
        local_dict.setdefault(item,{center:{year:{dish:amount,"total":amount},"total":amount}})
print(local_dict)