2017-02-14 16 views
3

У меня есть 2 гнездовые словари переменных, которые имеют аналогичные ключи, каждый из которых определяют различные значения:Суммируя 2 уровня вложенности словарей в Python

data1 = { 
"2010":{ 
     'A':2, 
     'B':3, 
     'C':5 
    }, 
"2011":{ 
     'A':1, 
     'B':2, 
     'C':3 
    }, 
"2012":{ 
     'A':1, 
     'B':2, 
     'C':4 
    } 
} 

data2 = { 
"2010":{ 
     'A':4, 
     'B':4, 
     'C':5 
    }, 
"2011":{ 
     'A':1, 
     'B':1, 
     'C':3 
    }, 
"2012":{ 
     'A':3, 
     'B':2, 
     'C':4 
    } 
} 

В моем случае, мне нужно суммировать оба значения словарей на основе того же так что ответ будет таким:

data3 = { 
"2010":{ 
     'A':6, 
     'B':7, 
     'C':10 
    }, 
"2011":{ 
     'A':2, 
     'B':3, 
     'C':6 
    }, 
"2012":{ 
     'A':4, 
     'B':4, 
     'C':8 
    } 
} 

Как я могу это сделать?

+0

Гарантировано, что структура двух словарей одинаковая? –

ответ

2

Учитывая структуру двух словарей то же, вы можете использовать словарь понимание для этого:

data3 = {key:{key2:val1+data2[key][key2] for key2,val1 in subdic.items()} for key,subdic in data1.items()} 

В РЕПЛ:

>>> {key:{key2:val1+data2[key][key2] for key2,val1 in subdic.items()} for key,subdic in data1.items()} 
{'2010': {'B': 7, 'C': 10, 'A': 6}, '2012': {'B': 4, 'C': 8, 'A': 4}, '2011': {'B': 3, 'C': 6, 'A': 2}} 

Осмысление работает следующим образом: в наружный кружок, мы перебираем по key,subdic от data1. Так что в вашем случае key - год, а subdic - это словарь (data1) за этот год.

Теперь для каждого из этих лет, мы перебирать пункты subdic и здесь key2 является 'A', 'B' и 'C'. val1 - это значение, которое мы находим в data1 для этих ключей. Мы получаем другое значение, запрашивая data2[key][key2]. Мы суммируем их и строим для этого новые словари.

+0

Большое спасибо, Виллем, это тоже моя проблема в более сложном состоянии. – Faizalprbw

+0

@Faizalprbw: mind однако, поскольку ответ говорит, что это работает только в том случае, если структура идентична: поэтому оба словаря должны содержать '2010' и' A', 'B' и' C' в '2010' и т. Д. –

+1

Да, я понимаю, на самом деле у меня есть два json-данных, которые содержат ту же структуру с похожими ключами, как и мой пример выше. – Faizalprbw

1

Другое решение :) Вы также можете использовать zip, чтобы получить как data1 и data2 в том же for петли, а затем использовать collections.Counter, чтобы добавить значение каждого dicts.

from collections import Counter 

>> {k1: Counter(v1) + Counter(v2) for (k1, v1), (k2, v2) in zip(sorted(data1.items()), sorted(data2.items()))} 
{'2011': Counter({'C': 6, 'B': 3, 'A': 2}), '2010': Counter({'C': 10, 'B': 7, 'A': 6}), '2012': Counter({'C': 8, 'A': 4, 'B': 4})} 

Вы закончились Counter Dict, но так как он является подклассом dict вы можете использовать тот же метод, как обычный dict.

+0

Это не всегда работает, так как 'k1' может отличаться от' k2'. Обратите внимание, что клавиши ** не ** упорядочены в словаре. Если вы, однако, выполните поиск в 'dict2', он будет работать. –

+0

Ваше замечание неверно, 'zip' всегда будет перебирать те же« ключи »в словарях. Или, может быть, я неправильно понял, что вы сказали. Можете ли вы привести простой пример? Ваше решение тоже хорошо, но проблема заключается в том, что между ними есть словарь 'n'. –

+0

Просто хотите добавить, 'zip' будет перебирать те же« ключи »2-го словаря, если _ и только если они имеют _all_« ключи »вместе, иначе это не сработает. –

1

Если добавить Dict() в хорошем короткое решение Макса Chrétiens' сверху, вы будете в конечном итоге с регулярными словарях:

data3 = {k1: dict(Counter(v1) + Counter(v2)) for (k1, v1), (k2, v2) in 
     zip(data1.items(), data2.items())} 

Это, однако, только работать правильно, если оба словаря разделяют точно так же, ключи, как уже обсуждалось выше. Решение Willem Van Onsem не будет работать, если есть какие-либо ключи, не разделяемые обоими словарями (это приведет к ошибке, тогда как решение Max Chrétiens в этом случае неправильно сменит элементы). Теперь вы упомянули, что используете данные JSON, которые всегда содержат одну и ту же структуру с похожими ключами, поэтому это не должно быть проблемой, и решение Max Chrétien должно работать хорошо.

Если вы хотите убедиться, что используются только ключи, используемые обоими словарями (и их подзаголовками), будет работать следующее. Обратите внимание, как я добавил «X»: 111111 в качестве пары ключевых значений в подзадачу 2012 года и «1999»: {'Z': 999999} как весь подзадач.

def sum_two_nested_dicts(d1, d2): 
    dicts = [d1, d2] 
    d_sum = {} 
    for topkey in dicts[0]: 
     if topkey in dicts[1]: 
      d_sum[topkey] = {} 
      for key in dicts[0][topkey]: 
       if key in dicts[1][topkey]: 
        new_val = sum([d[topkey][key] for d in dicts]) 
        d_sum[topkey][key] = new_val 
    return d_sum 


data1 = { 
    "2010": { 
     'A': 2, 
     'B': 3, 
     'C': 5 
    }, 
    "2011": { 
     'A': 1, 
     'B': 2, 
     'C': 3 
    }, 
    "2012": { 
     'A': 1, 
     'B': 2, 
     'C': 4, 
     'X': 111111 
    }, 
    "1999": { 
     'Z': 999999 
    } 
} 

data2 = { 
    "2010": { 
     'A': 4, 
     'B': 4, 
     'C': 5 
    }, 
    "2011": { 
     'A': 1, 
     'B': 1, 
     'C': 3 
    }, 
    "2012": { 
     'A': 3, 
     'B': 2, 
     'C': 4 
    } 
} 

data3 = sum_two_nested_dicts(data1, data2) 

print(data3) 

# different order of arguments 

data4 = sum_two_nested_dicts(data2, data1) 

print(data4) 

# {'2010': {'C': 10, 'A': 6, 'B': 7}, '2012': {'C': 8, 'A': 4, 'B': 4}, '2011': {'C': 6, 'A': 2, 'B': 3}} 
# {'2010': {'C': 10, 'A': 6, 'B': 7}, '2012': {'C': 8, 'A': 4, 'B': 4}, '2011': {'C': 6, 'A': 2, 'B': 3}} 

Я понимаю, что это далеко не так лаконичен и элегантен, как может быть, но, как я уже писал это в любом случае, я его здесь в случае, если кто-то пытается достичь этой конкретной функциональности.

Длинные и раздутой версия, которая сохраняет неразделенные ключи/значения, только потому, что я уже написал ... решение

def sum_nested_dicts(dic1, dic2): 
    # create list of both dictionaries 
    dicts = [dic1, dic2] 
    # create a set of all unique keys from both dictionaries 
    topkeys = set(sum([list(dic.keys()) for dic in dicts], [])) 
    # this is the merged dictionary to be returned 
    d_sum = {} 
    for topkey in topkeys: 
     # if topkey is shared by both dictionaries 
     if topkey in dic1 and topkey in dic2: 
      d_sum[topkey] = {} 
      keys = set(sum([list(dic[topkey].keys()) for dic in 
          dicts], [])) 
      for key in keys: 
       # if key is shared by both subdictionaries 
       if key in dic1[topkey] and key in dic2[topkey]: 
        new_val = sum([d[topkey][key] for d in dicts]) 
        d_sum[topkey][key] = new_val 
       # if key is only contained in one subdictionary 
       elif key in dic1[topkey]: 
        d_sum[topkey][key] = dic1[topkey][key] 
       elif key in dic2[topkey]: 
        d_sum[topkey][key] = dic2[topkey][key] 
     # if topkey is only contained in one dictionary 
     elif topkey in dic1: 
      d_sum[topkey] = dic1[topkey] 
     elif topkey in dic2: 
      d_sum[topkey] = dic2[topkey] 
    return d_sum 

знакомства Кристал за то, что, как представляется, наиболее кратким и функциональным решением размещены до сих пор.

+0

Thx для обновления моего решения: D Ваше решение выглядит как адаптированное к любым ситуациям, однако, я думаю, было бы неплохо создать функцию, которая могла бы объединять словари 'n', когда ключи идентичны. Кроме того, добавьте другие значения, даже если они не оба dict (например, ваш '' X ': 111111') –

+0

Добавлена ​​моя версия for-loop-and-if-condition-frenzy выше. Но см. Решение Crystal для того, что кажется самым элегантным решением. :) –

1

Я надеюсь, что это помогает:

data1 = { "2010":{ 'A':2, 'B':3, 'C':5 }, "2011":{ 'A':1, 'B':2, 'C':3 }, "2012":{ 'A':1, 'B':2, 'C':4 } } 
    data2 = { "2010":{ 'A':4, 'B':4, 'C':5 }, "2011":{ 'A':1, 'B':1, 'C':3 }, "2012":{ 'A':3, 'B':2, 'C':4 } } 

    data3 = {} 

    for data in [data1,data2]: 
     for year in data.keys(): 
       for x,y in data[year].items(): 
        if not year in data3.keys(): 
         data3[year] = {x:y} 
        else: 
         if not x in data3[year].keys(): 
          data3[year].update({x:y}) 
         else: 
          data3[year].update({x:data3[year][x] + y}) 
    print data3 

Это работает для любых длин внутренних и внешних словарей.

+0

Это здорово! Он также будет работать, если в одном словаре есть ключи, но не в другом. Кроме того, он будет работать, если один из подключений (ключ вложенного словаря, т. Е. Словарь, который является значением внешнего словаря) не используется обоими словарями. Кажется, охватывают все случаи, которые я тестировал. :-) –