【问题标题】:Grouping a dict by a dynamic key name and aggregating some of the keys of a nested dict in Python通过动态键名对字典进行分组并在 Python 中聚合嵌套字典的一些键
【发布时间】:2019-08-05 12:14:55
【问题描述】:

我正在努力通过键(在嵌套字典中)对嵌套字典进行分组,并通过聚合一些嵌套字典的数据。 我希望这里有人能给我一些有用的提示,因为我没有取得任何进展。我正在使用 Python 3.6,我查看了 collections 和 pandas 模块,发现 pandas 模块可能包含我实现目标所需的东西。

提供以下字典:


{
  12345: {
    'date': '2019-07-26',
    'time_spent': 0.5,
    'color': 'yellow',
    'drive_id': 1804
  },
  54321: {
    'date': '2019-07-26',
    'time_spent': 1.5,
    'color': 'yellow',
    'drive_id': 3105
  },
  11561: {
    'date': '2019-07-25',
    'time_spent': 1.25,
    'color': 'red',
    'drive_id': 1449
  },
  12101: {
    'date': '2019-07-25',
    'time_spent': 0.25,
    'color': 'red',
    'drive_id': 2607
  },
  12337: {
    'date': '2019-07-24',
    'time_spent': 2.0,
    'color': 'yellow',
    'drive_id': 3105
  },
  54123: {
    'date': '2019-07-24',
    'time_spent': 1.5,
    'color': 'yellow',
    'drive_id': 4831
  },
  15931: {
    'date': '2019-07-19',
    'time_spent': 3.0,
    'color': 'yellow',
    'drive_id': 3105
  },
  13412: {
    'date': '2019-07-19',
    'time_spent': 1.5,
    'color': 'red',
    'drive_id': 1449
  }
}

将其视为汽车销售商在这些日子里进行的试驾清单,其中包括一次试驾所花费的时间,并通过颜色评估销售机会。现在,我需要对这些数据进行分组:

  • 按日期分组,因此新字典可能包含单个日期作为键
  • 汇总单个日期的 time_spent 并提供该日期的总和
  • 把颜色带上,但如果颜色混了一天(例如红色和黄色),红色总是胜出
  • 对于每个日期,有一个 drive_id 聚合列表,以逗号分隔
  • 扔掉顶层dict的键名

所以当我手动转换数据时它可能看起来像这样:

{
  '2019-07-26':
  {
    'time_spent': '2.0',
    'color': 'yellow',
    'drive_id': '1804, 3105',

  },
  '2019-07-25':
  {
    'time_spent': '1.5',
    'color': 'red',
    'drive_id': '1449, 2607',

  },
  '2019-07-24':
  {
    'time_spent': '3.5',
    'color': 'yellow',
    'drive_id': '3105, 4831',

  },
  '2019-07-19':
  {
    'time_spent': '4.5',
    'color': 'red',
    'drive_id': '1449, 3105',
  }

}

现在我的障碍在哪里?显然,我的 Python 技能有限,而且我在动态生成 dict 键名(例如 13412)时遇到了困难。我在这里 (Group pandas dataframe by a nested dictionary key) 找到了这个解决方案,但我无法将此解决方案应用于我的案例,因为这里事先不知道 dict 键名。所以我基本上尝试创建一个 pandas DataFrame 并首先按日期对原始字典进行分组,但我已经失败了。

如果我可能忽略了 pandas 文档中的某些内容或 StackOverflow 上的问题,我深表歉意。如果有人能给我一个提示并向我解释如何处理这种情况,我将不胜感激。

【问题讨论】:

  • 'date': '2019-07-24'有两个对象,这种情况下怎么办?
  • @OlvinR​​oght:中间的项目符号都解决了这个问题。
  • 你可能想研究dict方法items
  • @Valentin 编辑您的问题并显示您目前拥有的所有代码,以及任何错误消息。
  • 我有一个答案,可以将其放入一个不错的数据框中,非常适合进行额外分析!包括 groupby 的

标签: python dictionary grouping aggregation


【解决方案1】:

通过简单的迭代并使用dict.setdefault:

d = {
  12345: {
    'date': '2019-07-26',
    'time_spent': 0.5,
    'color': 'yellow',
    'drive_id': 1804
  },
  54321: {
    'date': '2019-07-26',
    'time_spent': 1.5,
    'color': 'yellow',
    'drive_id': 3105
  },
  11561: {
    'date': '2019-07-25',
    'time_spent': 1.25,
    'color': 'red',
    'drive_id': 1449
  },
  12101: {
    'date': '2019-07-25',
    'time_spent': 0.25,
    'color': 'red',
    'drive_id': 2607
  },
  12337: {
    'date': '2019-07-24',
    'time_spent': 2.0,
    'color': 'yellow',
    'drive_id': 3105
  },
  54123: {
    'date': '2019-07-24',
    'time_spent': 1.5,
    'color': 'yellow',
    'drive_id': 4831
  },
  15931: {
    'date': '2019-07-19',
    'time_spent': 3.0,
    'color': 'yellow',
    'drive_id': 3105
  },
  13412: {
    'date': '2019-07-19',
    'time_spent': 1.5,
    'color': 'red',
    'drive_id': 1449
  }
}

out = {}
for item in d.values():
    out.setdefault(item['date'], {})
    out[item['date']].setdefault('time_spent', 0.0)
    out[item['date']].setdefault('color', 'yellow')
    out[item['date']].setdefault('drive_id', [])

    out[item['date']]['time_spent'] += item['time_spent']
    if item['color'] == 'red':
        out[item['date']]['color'] = 'red'
    out[item['date']]['drive_id'].append(item['drive_id'])

#post-processing
for k in out.values():
    k['drive_id'] = ', '.join(str(i) for i in k['drive_id'])
    k['time_spent'] = str(k['time_spent'])

from pprint import pprint
pprint(out)

打印:

{'2019-07-19': {'color': 'red', 'drive_id': '3105, 1449', 'time_spent': '4.5'},
 '2019-07-24': {'color': 'yellow',
                'drive_id': '3105, 4831',
                'time_spent': '3.5'},
 '2019-07-25': {'color': 'red', 'drive_id': '1449, 2607', 'time_spent': '1.5'},
 '2019-07-26': {'color': 'yellow',
                'drive_id': '1804, 3105',
                'time_spent': '2.0'}}

【讨论】:

  • 谢谢。所以你说不需要像熊猫这样的特殊东西?这实际上已经帮助了我很多。将尝试查看 dict.setdefault。编辑:我看到你也发布了代码。也谢谢你。将尝试逆向工程并了解您的样本。
  • @Valentin 在这个简单的案例中不需要特殊模块。不过,我会将drive_id 保留为list,将time_spent 保留为float,以便之后进行更好的操作。
  • 谢谢。我没想到有人会给我一个可行的解决方案,只是一些关于如何取得进展的提示。但我会研究您的代码并考虑您关于将其保持为浮点数并使用 drive_id 列表的提示。
  • 哦,天哪.. 这太简单了。我以类似的方式尝试过,但是 a)我不知道“setdefault”在这里有意义,b)“日期”有各种关键错误,可能是因为“日期”的类型。无论如何,这个解决方案很简单,只需要基本的 Python 理解。再次感谢这个。
【解决方案2】:

我没有进行任何库检查,但我创建了以下脚本来完成您的任务。此脚本中的预定义变量是data,即您的dict。这是在脚本中编辑的。

脚本如下:

for i in data.values():

    # Get the date, which will be the key for the replacement entry
    date = data[i]['date']

    # Splits the track. Is this date already defined in the dict?
    try:

        # This is the line that does it. If this line succeeds, there is aleady
        # a key in this dict for this date. We must do some appending things.
        data[date]

        # Color: red wins if it comes between red or yellow.
        data[date]['color'] = 'red' if data[date]['color'] == 'red' or \
                data[i]['color'] == 'red' else 'yellow'

        # Time spent: sum of the two
        data[date]['time_spent'] = data[date]['time_spent'] + \
                data[i]['time_spent']

        # Drive ID: append
        data[date]['drive_id'] = str(data[date]['drive_id']) + ', ' + \
                str(data[i]['drive_id'])

    # If the date fails to get, we catch the error and add a new date entry.
    except KeyError:

        # Adds the new date entry
        data.update({date: data[i]})

        # Removes the duplicate date entry
        data[date].pop('date')

    # Removes the old entry
    data.pop(i)

运行这个,假设 datedate_old 被定义将 data 转换为以下字典:

{'2019-07-26': {'time_spent': 2.0, 'color': 'yellow', 'drive_id': '1804, 3105'}, '2019-07-25': {'time_spent': 1.5, 'color': 'red', 'drive_id': '1449, 2607'}, '2019-07-24': {'time_spent': 3.5, 'color': 'yellow', 'drive_id': '3105, 4831'}, '2019-07-19': {'time_spent': 4.5, 'color': 'red', 'drive_id': '3105, 1449'}}

【讨论】:

    【解决方案3】:

    你可以这样做:

    input = {
        12345: {
            'date': '2019-07-26',
            'time_spent': 0.5,
            'color': 'yellow',
            'drive_id': 1804
        },
        54321: {
            'date': '2019-07-26',
            'time_spent': 1.5,
            'color': 'yellow',
            'drive_id': 3105
        },
        11561: {
            'date': '2019-07-25',
            'time_spent': 1.25,
            'color': 'red',
            'drive_id': 1449
        },
        12101: {
            'date': '2019-07-25',
            'time_spent': 0.25,
            'color': 'red',
            'drive_id': 2607
        },
        12337: {
            'date': '2019-07-24',
            'time_spent': 2.0,
            'color': 'yellow',
            'drive_id': 3105
        },
        54123: {
            'date': '2019-07-24',
            'time_spent': 1.5,
            'color': 'yellow',
            'drive_id': 4831
        },
        15931: {
            'date': '2019-07-19',
            'time_spent': 3.0,
            'color': 'yellow',
            'drive_id': 3105
        },
        13412: {
            'date': '2019-07-19',
            'time_spent': 1.5,
            'color': 'red',
            'drive_id': 1449
        }
    }
    
    output = {}
    for value in input.values():
        obj = output.get(value['date'], None)
        if not obj:
            obj = {
                'time_spent': str(value['time_spent']),
                'color': value['color'],
                'drive_id': str(value['drive_id'])
            }
            output[value['date']] = obj
        else:
            obj['time_spent'] = str(float(obj['time_spent']) + value['time_spent'])
            if value['color'] == 'red':
                obj['color'] = value['color']
            obj['drive_id'] += ', ' + str(value['drive_id'])
    

    输出:

    {
        '2019-07-26': {
            'time_spent': '2.0',
            'color': 'yellow',
            'drive_id': '1804, 3105'
        },
        '2019-07-25': {
            'time_spent': '1.5',
            'color': 'red',
            'drive_id': '1449, 2607'
        },
        '2019-07-24': {
            'time_spent': '3.5',
            'color': 'yellow',
            'drive_id': '3105, 4831'
        },
        '2019-07-19': {
            'time_spent': '4.5',
            'color': 'red',
            'drive_id': '3105, 1449'
        }
    }
    

    【讨论】:

    • 谢谢。我没想到有人会发布可以使用的代码,我已经很高兴提示下一步该去哪里看。不过,我感谢您的努力,并将尝试理解您的代码。
    • @Valentin,这似乎很明显,但如果您有任何问题,请随时提问。
    【解决方案4】:

    我们来了! pandas 将使您的所有聚合变得更加容易!

    代码

    import pandas as pd
    
    d = {
      12345: {
        'date': '2019-07-26',
        'time_spent': 0.5,
        'color': 'yellow',
        'drive_id': 1804
      },
      54321: {
        'date': '2019-07-26',
        'time_spent': 1.5,
        'color': 'yellow',
        'drive_id': 3105
      },
      11561: {
        'date': '2019-07-25',
        'time_spent': 1.25,
        'color': 'red',
        'drive_id': 1449
      },
      12101: {
        'date': '2019-07-25',
        'time_spent': 0.25,
        'color': 'red',
        'drive_id': 2607
      },
      12337: {
        'date': '2019-07-24',
        'time_spent': 2.0,
        'color': 'yellow',
        'drive_id': 3105
      },
      54123: {
        'date': '2019-07-24',
        'time_spent': 1.5,
        'color': 'yellow',
        'drive_id': 4831
      },
      15931: {
        'date': '2019-07-19',
        'time_spent': 3.0,
        'color': 'yellow',
        'drive_id': 3105
      },
      13412: {
        'date': '2019-07-19',
        'time_spent': 1.5,
        'color': 'red',
        'drive_id': 1449
      }
    }
    
    dd = {str(k): v for k, v in d.items()}
    
    pd.read_json(json.dumps(dd), orient='records').transpose()
    df['date'] = pd.to_datetime(df['date'])
    df['drive_id'] = df['drive_id'].astype(str)
    df = df.reset_index()
    

    输出:

        index   color   date    drive_id    time_spent
    0   12345   yellow  2019-07-26  1804    0.5
    1   54321   yellow  2019-07-26  3105    1.5
    2   11561   red 2019-07-25  1449    1.25
    3   12101   red 2019-07-25  2607    0.25
    4   12337   yellow  2019-07-24  3105    2
    5   54123   yellow  2019-07-24  4831    1.5
    6   15931   yellow  2019-07-19  3105    3
    7   13412   red 2019-07-19  1449    1.5
    

    按日期分组并获取 ID 列表

    df.pivot_table(index=['date'], values=['drive_id'], aggfunc=lambda x: ','.join(x)).reset_index()
    
        date    drive_id
    0   2019-07-19  3105,1449
    1   2019-07-24  3105,4831
    2   2019-07-25  1449,2607
    3   2019-07-26  1804,3105
    

    【讨论】:

      猜你喜欢
      • 2011-05-13
      • 1970-01-01
      • 2019-06-12
      • 2022-01-15
      • 2016-12-17
      • 2020-11-12
      • 2015-03-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多