比较2个字典列表并返回更改的值作为嵌套字典

时间:2020-03-28 13:45:30

标签: python dictionary

我有2个字典,如下所示

last_data = [{'country': 'USA', 'cases': 10425, 'deaths': 1704, 'recovered': 2525, 'active': 100027},
            {'country': 'Australia', 'cases': 3045, 'deaths': 1704, 'recovered': 2525, 'active': 100027},
            {'country': 'Germany', 'cases': 6025, 'deaths': 1704, 'recovered': 2525, 'active': 100027}]

current_data = [{'country': 'USA', 'cases': 10425, 'deaths': 1704, 'recovered': 2525, 'active': 100027},
                {'country': 'Australia', 'cases': 3046, 'deaths': 1704, 'recovered': 2525, 'active': 100028},
                {'country': 'Germany', 'cases': 6026, 'deaths': 1706, 'recovered': 2525, 'active': 100026}]

我正在尝试通过比较这2个来实现以下目标

expected_output = [{'Australia':{'last_cases_value': 3045,'updated_cases_value':3046,'change':1,
                                {'last_active_value':100027,'updated_active_value':10028,'change':1}
                   {'Germany':{'last_cases_value': 6025,'updated_cases_value':6026,'change':1},
                              {'last_death_value':1704,'updated_death_value':1706,'change':2},
                              {'last_active_value':100027,'updated_active_value':10026,'change':-1}]

我尝试了很多事情,但是让我有点亲近的是

pairs = zip(last_data, current_data)
print([(x, y) for x, y in pairs if x != y])

以上代码的输出如下

[({'country': 'Australia', 'cases': 3045, 'deaths': 1704, 'recovered': 2525, 'active': 100027}, {'country': 'Australia', 'cases': 3046, 'deaths': 1704, 'recovered': 2525, 'active': 100028}), ({'country': 'Germany', 'cases': 6025, 'deaths': 1704, 'recovered': 2525, 'active': 100027}, {'country': 'Germany', 'cases': 6026, 'deaths': 1704, 'recovered': 2525, 'active': 100027})]

仍然无法弄清楚如何转换为我的预期输出

我正在使用最新版本的python3.8

任何帮助将不胜感激

3 个答案:

答案 0 :(得分:2)

您的预期输出是无效有效。我可能会改用这样的嵌套字典:

{
    "Germany": {
        "cases": {
            "last_cases_value": 6025,
            "updated_cases_value": 6026,
            "change": 1
        },
        "active": {
            "last_active_value": 100027,
            "updated_active_value": 100026,
            "change": -1
        },
        "deaths": {
            "last_deaths_value": 1704,
            "updated_deaths_value": 1706,
            "change": 2
        }
    },
    "Australia": {
        "cases": {
            "last_cases_value": 3045,
            "updated_cases_value": 3046,
            "change": 1
        },
        "active": {
            "last_active_value": 100027,
            "updated_active_value": 100028,
            "change": 1
        }
    }
}

要达到上述目的,我首先将您的词典列表转换为以'country'为键的嵌套词典:

last_data = [{'country': 'USA', 'cases': 10425, 'deaths': 1704, 'recovered': 2525, 'active': 100027},
             {'country': 'Australia', 'cases': 3045, 'deaths': 1704, 'recovered': 2525, 'active': 100027},
             {'country': 'Germany', 'cases': 6025, 'deaths': 1704, 'recovered': 2525, 'active': 100027}]

current_data = [{'country': 'USA', 'cases': 10425, 'deaths': 1704, 'recovered': 2525, 'active': 100027},
                {'country': 'Australia', 'cases': 3046, 'deaths': 1704, 'recovered': 2525, 'active': 100028},
                {'country': 'Germany', 'cases': 6026, 'deaths': 1706, 'recovered': 2525, 'active': 100026}]

def list_dicts_to_nested_dict(key, lst):
    return {dic[key]: {k: v for k, v in dic.items() if k != key} for dic in lst}

last_data_dict = list_dicts_to_nested_dict('country', last_data)
# {'USA': {'cases': 10425, 'deaths': 1704, 'recovered': 2525, 'active': 100027}, 'Australia': {'cases': 3045, 'deaths': 1704, 'recovered': 2525, 'active': 100027}, 'Germany': {'cases': 6025, 'deaths': 1704, 'recovered': 2525, 'active': 100027}}

current_data_dict = list_dicts_to_nested_dict('country', current_data)
# {'USA': {'cases': 10425, 'deaths': 1704, 'recovered': 2525, 'active': 100027}, 'Australia': {'cases': 3046, 'deaths': 1704, 'recovered': 2525, 'active': 100028}, 'Germany': {'cases': 6026, 'deaths': 1706, 'recovered': 2525, 'active': 100026}}

这也是一个好主意,因为从扫描整个词典中搜索特定国家/地区的数据将是 O(1),而不是 O(N)。这也使将来与这些国家相交变得更加容易,我将在下面显示。

然后将更改的数据添加到dict的嵌套的二次深度 collections.defaultdict中,因为它可以为您初始化新密钥。您可以查看此Nested defaultdict of defaultdict答案,以了解更多信息和其他方式。

result = defaultdict(lambda: defaultdict(dict))

# Get the intersecting keys.
# Avoids Key Errors in the future, if both dictionaries don't have the same key
for country in last_data_dict.keys() & current_data_dict.keys():

    # Only deal with dictionaries that have changed
    if last_data_dict[country] != current_data_dict[country]:

        # Get intersecting keys between both dictionaries
        for key in last_data_dict[country].keys() & current_data_dict[country].keys():

            # Calculate the change between updated and previous data
            change = current_data_dict[country][key] - last_data_dict[country][key]

            # We only care about data that has changed
            # Insert data into dictionary
            if change != 0:
                result[country][key][f"last_{key}_value"] = last_data_dict[country][key]
                result[country][key][f"updated_{key}_value"] = current_data_dict[country][key]
                result[country][key]["change"] = change

然后,您可以使用json.dumps将上述数据序列化并输出为JSON格式的字符串,因为以这种方式更容易输出嵌套的defaultdict而不是将整个数据结构转换为{{1}递归或其他方法。 dict仍然是defaultdict的子类,因此可以将其视为普通词典。

dict

另外,如果您不关心输出,那么直接打印print(dumps(result, indent=4)) 也是一个简单的选择:

defaultdict

作为一个额外的可选步骤,但又不需要 步骤,如上所述,我们可以创建一个递归函数,将嵌套的print(result) # defaultdict(<function <lambda> at 0x000002355BC3AA60>, {'Australia': defaultdict(<class 'dict'>, {'cases': {'last_cases_value': 3045, 'updated_cases_value': 3046, 'change': 1}, 'active': {'last_active_value': 100027, 'updated_active_value': 100028, 'change': 1}}), 'Germany': defaultdict(<class 'dict'>, {'deaths': {'last_deaths_value': 1704, 'updated_deaths_value': 1706, 'change': 2}, 'cases': {'last_cases_value': 6025, 'updated_cases_value': 6026, 'change': 1}, 'active': {'last_active_value': 100027, 'updated_active_value': 100026, 'change': -1}})}) 转换为子类型为{{1 }}:

defaultdict

可按预期工作:

dict

您可以查看ideone.com上的完整实现。

答案 1 :(得分:2)

您可以使用列表和字典理解:

l = {l['country']: {'v': l['cases'], 'a': l['active']} for l in last_data}
c = {l['country']: {'v': l['cases'], 'a': l['active']} for l in current_data}

result = [{k: [{'last_cases_value': l[k]['v'], 
               'updated_cases_value': c[k]['v'],
               'change': c[k]['v'] - l[k]['v']},
               {'last_active_value': l[k]['a'], 
               'updated_active_value': c[k]['a'],
               'change': c[k]['a'] - l[k]['a']}]} for k in c.keys()]

输出:

[{'USA': [{'last_cases_value': 10425,
    'updated_cases_value': 10425,
    'change': 0},
   {'last_active_value': 100027,
    'updated_active_value': 100027,
    'change': 0}]},
 {'Australia': [{'last_cases_value': 3045,
    'updated_cases_value': 3046,
    'change': 1},
   {'last_active_value': 100027,
    'updated_active_value': 100028,
    'change': 1}]},
 {'Germany': [{'last_cases_value': 6025,
    'updated_cases_value': 6026,
    'change': 1},
   {'last_active_value': 100027,
    'updated_active_value': 100026,
    'change': -1}]}]

如果您只想保留结果已更改的国家/地区,则保留结果:

result = [{k: [{'last_cases_value': l[k]['v'], 
               'updated_cases_value': c[k]['v'],
               'change': c[k]['v'] - l[k]['v']},
               {'last_active_value': l[k]['a'], 
               'updated_active_value': c[k]['a'],
               'change': c[k]['a'] - l[k]['a']}]}
          for k in c.keys() if  c[k]['a'] - l[k]['a'] and c[k]['v'] - l[k]['v']]

输出:

[{'Australia': [{'last_cases_value': 3045,
    'updated_cases_value': 3046,
    'change': 1},
   {'last_active_value': 100027,
    'updated_active_value': 100028,
    'change': 1}]},
 {'Germany': [{'last_cases_value': 6025,
    'updated_cases_value': 6026,
    'change': 1},
   {'last_active_value': 100027,
    'updated_active_value': 100026,
    'change': -1}]}]

答案 2 :(得分:0)

您可以使用pandas

import pandas as pd

curr_df=pd.DataFrame(current_data)
last_df=pd.DataFrame(last_data)

df=curr_df.merge(last_df, on="country", suffixes=["_updated", "_last"])
res=df[sorted(df.columns)].rename(columns={c: "_".join(c.split("_")[::-1])+"_value" for c in df.columns if c!="country"}).set_index('country').to_dict('index')

res=[dict([(k, [dict(p) for p in list(zip(v.items(), list(v.items())[1:]))[::2]])]) for k, v in res.items()]

输出:

[{'USA': [{'last_active_value': 100027, 'updated_active_value': 100027}, {'last_cases_value': 10425, 'updated_cases_value': 10425}, {'last_deaths_value': 1704, 'updated_deaths_value': 1704}, {'last_recovered_value': 2525, 'updated_recovered_value': 2525}]}, {'Australia': [{'last_active_value': 100027, 'updated_active_value': 100028}, {'last_cases_value': 3045, 'updated_cases_value': 3046}, {'last_deaths_value': 1704, 'updated_deaths_value': 1704}, {'last_recovered_value': 2525, 'updated_recovered_value': 2525}]}, {'Germany': [{'last_active_value': 100027, 'updated_active_value': 100026}, {'last_cases_value': 6025, 'updated_cases_value': 6026}, {'last_deaths_value': 1704, 'updated_deaths_value': 1706}, {'last_recovered_value': 2525, 'updated_recovered_value': 2525}]}]

注意-默认情况下,merge进行内部联接,如果要完整的外部/左侧外部,请查看其how参数:

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.merge.html

zip公式-用于成对地整形列表-取自此处:https://stackoverflow.com/a/5394908/11610186