【问题标题】:Every product/combination of nested dictionaries saved to DataFrame嵌套字典的每个产品/组合都保存到 DataFrame
【发布时间】:2019-12-30 23:08:39
【问题描述】:

我是 Python 新手,正在为 X 次模型运行初始化参数。我需要从 N 个字典中创建所有可能的组合,每个字典都有嵌套数据。

我知道我需要以某种方式使用 itertools.product,但我不知道如何浏览字典。也许我什至不应该使用字典,而是使用 json 或其他东西。我也知道这会产生很多参数/运行。

编辑:从评论中添加了澄清。 我想创建一个带有 n 个字典的函数---例如。 def func(dict*) ---- 作为输入,并在所有字典中创建所有这些单独的键/值对的所有可能组合,返回一个包含所有组合的大 DF。

我的数据如下所示:

字典 1
{
    "chisel": [
        {"type": "chisel"},
        {"depth": [152, 178, 203]},
        {"residue incorporation": [0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]},
        {"timing": ["10-nov", "10-apr"]},
    ],
    "disc": [
        {"type": "disc"},
        {"depth": [127, 152, 178, 203]},
        {"residue incorporation": [0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]},
        {"timing": ["10-nov", "10-apr"]},
    ],
    "no_till": [
        {"type": "user_defined"},
        {"depth": [0]},
        {"residue incorporation": [0.0]},
        {"timing": ["10-apr"]},
    ],
}
字典 2
{
    "nh4_n":
        {
            "kg/ha":[110, 115, 120, 125, 130, 135, 140, 145, 150, 155, 160, 165, 170, 175, 180, 185, 190, 195, 200, 205, 210, 215, 220, 225],
            "fertilize_on":"10-apr"
        },
    "urea_n":
        {
            "kg/ha":[110, 115, 120, 125, 130, 135, 140, 145, 150, 155, 160, 165, 170, 175, 180, 185, 190, 195, 200, 205, 210, 215, 220, 225],
            "fertilize_on":"10-apr"
        }
}
字典 3
{
    "maize": {
        "sow_crop": 'maize',
        "cultivar": ['B_105', 'B_110'],
        "planting_dates": [
            '20-apr', '27-apr', '4-may', '11-may', '18-may', '25-may', '1-jun', '8-jun', '15-jun'],
        "sowing_density": [8],
        "sowing_depth": [51],
        "harvest": ['maize'],
    }
}

例如,对于上面的三个字典,我会以某种方式使用字典 'chisel' 和 itertools.product 字典 2 中的每个嵌套字典(例如 'nh4_n')和字典 3 中的每个嵌套字典(在此案例只有一个,所以每个不同的品种、种植日期等)。我还想将每个键值对中的键用作 DF 列标题。

【问题讨论】:

    标签: python python-3.x pandas


    【解决方案1】:

    问题:

    • 主要问题是数据字典格式的不一致:

      1. 与 dict 1 & 3 不同,dict 2 的顶部键不是子键的值
      2. 不像 dict 2 和 3,它将 dicts 作为顶部键的值,dict 1 有一个顶级值的字典列表。
      3. 有些二级值是字符串,有些是列表

    第 1 步:修复数据:

    功能:

    fix_list_dicts:

    def fix_list_dicts(data: dict) -> dict:
        """
        Given a dict where the values are a list of dicts:
        (1) convert the value to a dict of dicts
        (2) if any second level value is a str, convert it to a list
        """
        data_new = dict()
        for k, v in data.items():
            v_new = dict()
            for x in v:
                for k1, v1 in x.items():
                    if type(v1) != list:
                        x[k1] = [v1]
                v_new.update(x)
            data_new[k] = v_new
        return data_new
    

    add_top_key_as_value:

    def add_top_key_as_value(data: dict, new_key: str) -> dict:
        """
        Given a dict of dicts, where top key is not a 2nd level value:
        (1) add new key: value pair to second level
        """
        for k, v in data.items():
            v.update({new_key: k})
            data[k] = v
        return data
    

    str_value_to_list:

    def str_value_to_list(data: dict) -> dict:
        """
        Given a dict of dicts:
        (1) Convert any second level value from str to list
        """    
        for k, v in data.items():
            for k2, v2 in v.items():
                if type(v2) != list:
                    data[k][k2] = [v2]
        return data 
    

    实施:

    from pprint import pprint as pp
    

    字典 1:

    d1 = fix_list_dicts(d1)
    pp(d1)
    
    {'chisel': {'depth': [152, 178, 203],
                'residue incorporation': [0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0],
                'timing': ['10-nov', '10-apr'],
                'type': ['chisel']},
     'disc': {'depth': [127, 152, 178, 203],
              'residue incorporation': [0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0],
              'timing': ['10-nov', '10-apr'],
              'type': ['disc']},
     'no_till': {'depth': [0],
                 'residue incorporation': [0.0],
                 'timing': ['10-apr'],
                 'type': ['user_defined']}}
    

    字典 2:

    d2 = add_top_key_as_value(d2, 'fertilizer')
    d2 = str_value_to_list(d2)
    
    {'nh4_n': {'fertilize_on': ['10-apr'],
               'fertilizer': ['nh4_n'],
               'kg/ha': [110, 115, 120, 125, 130, 135, 140, 145, 150, 155, 160, 165, 170, 175, 180, 185, 190, 195, 200, 205, 210, 215, 220, 225]},
     'urea_n': {'fertilize_on': ['10-apr'],
                'fertilizer': ['urea_n'],
                'kg/ha': [110, 115, 120, 125, 130, 135, 140, 145, 150, 155, 160, 165, 170, 175, 180, 185, 190, 195, 200, 205, 210, 215, 220, 225]}}
    

    字典 3:

    d3 = str_value_to_list(d3)
    
    {'maize': {'cultivar': ['B_105', 'B_110'],
               'harvest': ['maize'],
               'planting_dates': ['20-apr', '27-apr', '4-may', '11-may', '18-may', '25-may', '1-jun', '8-jun', '15-jun'],
               'sow_crop': ['maize'],
               'sowing_density': [8],
               'sowing_depth': [51]}}
    

    第 2 步:将数据合并到 DataFrames 中:

    功能:

    import pandas as pd
    

    combine_the_data:

    def combine_the_data(data: list) -> dict:
        """
        Given a list of dicts:
        (1) convert each dict into DataFrame
        (2) set the indices to 0
        (3) add each DataFrame to df_dict
        """
        df_dict = dict()
        for i, d in enumerate(data):
            df = pd.DataFrame.from_dict(d, orient='index')
            df.index = [0 for _ in range(len(df))]
            df_dict[f'd_{i}'] = df
    
        return df_dict
    

    merge_df_dict:

    def merge_df_dict(data: dict) -> pd.DataFrame:
        """
        Given a dict of DataFrames
        (1) merge them on the index
        """
        df = pd.DataFrame()
        for _, v in data.items():
            df = df.merge(v, how='outer', left_index=True, right_index=True)
        return df
    

    实施:

    data = [d1, d2, d3]
    df_dict = combine_the_data(data)
    
    df_dict['d_0']
    

    df_dict['d_1']
    

    df_dict['d_2']
    

    df = merge_df_dict(df_dict)
    


    第三步:使用pd.DataFrame.explodeEXPLODE所有列表:

    • 我不知道pandas v0.25 还包含哪些其他新功能,但explode 是其中最好的。
    • 没有pandas v0.25?那就来吧!
    df.reset_index(drop=True, inplace=True)  # the DataFrame must have a unique 0...x index
    
    for col in df.columns:
        df = df.explode(col).reset_index(drop=True)
    

    所有组合的最终输出:

    价值计数和期望:

    鉴于:

    • len(kg/ha) = 24
    • len(cultivar) = 2
    • len(plantint_dates) = 9
    • user_defined 行数 = 2

    • user_defined 的总组合数 = 864

    • 我没有手动计算其他两个types,但由于user_defined 的组合数量正确,我希望其他的也可以。

    df.type.value_counts()
    
    disc            48384
    chisel          36288
    user_defined      864
    Name: type, dtype: int64
    

    【讨论】:

    • 这是一个有趣的问题。像大多数数据科学问题一样,我归结为将数据重塑为可用的格式。我们不应该发表感谢,但谢谢。
    • 非常感谢!我不知道 Pandas 爆炸,并且正在考虑类似的技术,但不知道如何实现。这太棒了!
    • 很高兴为您提供帮助。两个重要的部分是将索引设置为 0 以进行合并,然后在爆炸前重置索引。
    猜你喜欢
    • 2019-01-09
    • 2022-01-01
    • 2019-07-24
    • 2020-10-22
    • 2018-08-13
    • 2022-01-02
    • 2020-02-13
    • 2017-12-26
    • 2021-05-14
    相关资源
    最近更新 更多