【问题标题】:Python: Grouping values of different columns into time bucketsPython:将不同列的值分组到时间桶中
【发布时间】:2017-12-05 10:52:21
【问题描述】:

假设你有这个 DataFrame:

Name    Item    Date    value1  value2
Marc    bike    21-Dec-17   7   1000
Marc    bike    05-Jan-18   9   2000
Marc    bike    27-Jul-18   4   500
John    house   14-Dec-17   4   500
John    house   02-Feb-18   6   500
John    house   07-Feb-18   8   1000
John    house   16-Feb-18   2   1000
John    house   05-Dec-21   7   1000
John    house   27-Aug-25   8   500
John    car     17-Apr-18   4   500

我想将 value1 和 value2 放入每个 name-item-combination 的每月存储桶中(接下来 48 个月的每个第三个星期三)。

因此,每个组合有 49 个时间桶,每个月的 value1 和 value2 之和:Marc/bike、John/house、John/car、...

John/house 的解决方案如下所示:

Name    Item    TimeBucket  value1  value2
John    house   20-Dec-17   4   500
John    house   17-Jan-18   0   0
John    house   21-Feb-18   16  2500
John    house   21-Mar-18   0   0
John    house   18-Apr-18   0   0
John    house   …           0   0
John    house   17-Nov-21   0   0
John    house   15-Dec-21   7   1000
John    house   rest        8   500

我无法使用 pandas 获得结果。我能想到的唯一解决方案是通过数据帧逐行迭代,但我真的很想避免这样做。有什么优雅的方法吗?

【问题讨论】:

    标签: python pandas grouping binning


    【解决方案1】:

    这个问题其实可以归结为三个步骤:

    1。如何找到每个月的第三个星期三?

    这可能不是最优雅的解决方案,但您可以过滤掉每个月的第三个星期三,方法是屏蔽熊猫DatetimeIndex,其中包含时间范围内的每一天。

    # generate a DatetimeIndex for all days in the relevant time frame
    from datetime import datetime
    start = datetime(2017, 12, 1)
    end = datetime(2022, 1, 31)
    days = pd.date_range(start, end, freq='D')
    
    # filter out only the third wednesday of each month
    import itertools
    third_wednesdays = []
    for year, month in itertools.product(range(2017, 2023), range(1,13)):
        mask = (days.weekday == 2) & \
            (days.year == year) & \
            (days.month == month)
        if len(days[mask]) > 0:
            third_wednesdays.append(days[mask][2])
    bucket_lower_bounds = pd.DatetimeIndex(third_wednesdays)
    

    将结果列表转换为DatetimeIndex,以便您可以在步骤 2 中将其用作 bin 的下限。

    2。如何对 DataFrame 的值进行 bin 处理?

    然后,一旦您将存储桶列表设为DatetimeIndex,您就可以简单地使用panda's cut function 将每个日期分配给存储桶。将日期列转换为整数,然后再将它们传递给cut,然后将结果转换回日期:

    time_buckets = pd.to_datetime(
        pd.cut(
            x = pd.to_numeric(df['Date']), 
            bins = pd.to_numeric(bucket_lower_bounds), 
            labels = bucket_lower_bounds[:-1]
        )
    )
    

    time_buckets 系列将原始数据帧的每个索引值分配给存储桶的下限。我们现在可以简单地将其添加到原始数据框中:

    df['TimeBucket'] = time_buckets
    

    结果应该有点像这样(不是NaT 代表“休息”桶):

        Name    Item    Date    value1  value2  TimeBucket
    0   Marc    bike    2017-12-21  7   1000    2017-12-20
    1   Marc    bike    2018-01-05  9   2000    2017-12-20
    2   Marc    bike    2018-07-27  4   500     2018-07-18
    3   John    house   2017-12-14  4   500     NaT
    4   John    house   2018-02-02  6   500     2018-01-17
    5   John    house   2018-02-07  8   1000    2018-01-17
    6   John    house   2018-02-16  2   1000    2018-01-17
    7   John    house   2021-12-05  7   1000    2021-11-17
    8   John    house   2025-08-27  8   500     NaT
    9   John    car     2018-04-17  4   500     2018-03-21
    

    3。如何聚合一个 binned DataFrame?

    现在就像使用groupby 获取名称、项目和存储桶的每个组合的总和 一样简单:

    df.groupby(['Name','Item','TimeBucket']).sum()
    

    结果:

    Name    Item    TimeBucket  value1  value2
    John    car     2018-03-21  4       500
            house   2018-01-17  16      2500
                    2021-11-17  7       1000
    Marc    bike    2017-12-20  16      3000
                    2018-07-18  4       500
    

    很遗憾,NaT values are excluded from groupby。如果您还需要将这些相加,也许最简单的方法是确保您的存储桶列表在您的输入范围内的每个日期都至少有一个存储桶。

    编辑:第 2 步需要 pandas 版本 >= 0.18.1。

    【讨论】:

    • 非常感谢这个快速的解决方案。我只需要对bins = pd.to_numeric(bucket_lower_bounds) 进行调整,这给了我:TypeError:需要一个类似字节的对象,而不是“索引”。知道为什么吗?
    • 您是否完全按照描述生成bucket_lower_bounds?你用的是哪个版本的 pandas 和 python?
    • 另外,type(bucket_lower_bounds) 的结果是什么?我希望它是DatetimeIndexpd.date_range 的返回类型),但它似乎是Index
    • - 是的,生成的 bucket_lower_bounds 如上所述
    • - python 3.5.1 和 pandas 0.17.1 - type(bucket_lower_bounds) = pandas.tseries.index.DatetimeIndex - 并且日期列是 dateime64[ns]
    猜你喜欢
    • 1970-01-01
    • 2019-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多