【问题标题】:groupby rolling date window sum with duplicate dates具有重复日期的 groupby 滚动日期窗口总和
【发布时间】:2022-01-20 16:36:04
【问题描述】:

This answer 提供了一种解决方案,可以根据日期窗口获取由另一列分组的列的滚动总和。在这里复制它:

df = pd.DataFrame(
    {
        'ID': {0: 10001, 1: 10001, 2: 10001, 3: 10001, 4: 10002, 5: 10002, 6: 10002},
        'Date': {
            0: datetime.datetime(2019, 7, 1),
            1: datetime.datetime(2019, 5, 1),
            2: datetime.datetime(2019, 6, 25),
            3: datetime.datetime(2019, 5, 27),
            4: datetime.datetime(2019, 6, 29),
            5: datetime.datetime(2019, 7, 18),
            6: datetime.datetime(2019, 7, 15)
        },
        'Amount': {0: 50, 1: 15, 2: 10, 3: 20, 4: 25, 5: 35, 6: 40},
    }
)
amounts = df.groupby(["ID"]).apply(lambda g: g.sort_values('Date').rolling('28d', on='Date').sum())
df['amount_4wk_rolling'] = df["Date"].map(amounts.set_index('Date')['Amount'])

输出:

+-------+------------+--------+--------------------+
|  ID   |    Date    | Amount | amount_4wk_rolling |
+-------+------------+--------+--------------------+
| 10001 | 01/07/2019 |     50 |                 60 |
| 10001 | 01/05/2019 |     15 |                 15 |
| 10001 | 25/06/2019 |     10 |                 10 |
| 10001 | 27/05/2019 |     20 |                 35 |
| 10002 | 29/06/2019 |     25 |                 25 |
| 10002 | 18/07/2019 |     35 |                100 |
| 10002 | 15/07/2019 |     40 |                 65 |
+-------+------------+--------+--------------------+

但是,如果其中两个日期相同,则会出现错误:

pandas.errors.InvalidIndexError: Reindexing only valid with uniquely valued Index objects

这是有道理的,正如我在最后一行看到的那样,Date 被用来设置一个现在不再唯一的索引。但是,由于我不太明白最后一行是做什么的,所以我很难尝试开发替代解决方案。

有人可以帮忙吗?

【问题讨论】:

  • 最后一行使用amounts 数据框作为伪字典在“Amount”列中查找值并填充“amount_4wk_rolling”列
  • 我认为我的answer here 应该可以工作。您只需要聚合总和而不是平均值。 (并确保在进行聚合之前sort_values
  • 那么你想得到什么答案?如果您想在滚动中对同一天的值进行分组然后有一个值或者仍然进行滚动求和,其中同一天的不同行会有不同的总和,这是不明确的
  • @ALollz - 谢谢。所以我试过df['amount_4wk_rolling'] = df.reset_index().groupby(["ID"]).apply(lambda g: g.sort_values('Date').rolling("28d", on="Date").agg({'Amount': 'sum', 'index': 'max'}).reset_index(drop=True).set_index('index')),但我得到了错误; ValueError: cannot handle a non-unique multi-index!。仔细检查后,代码似乎返回了一个多索引数据帧而不是一个系列,所以我已经很好地和真正地翘起。我只是想在这个阶段重新创建没有重复日期的确切答案。
  • 我也试过df['amount_4wk_rolling'] = df.sort_values('Date').reset_index().groupby(["ID"]).rolling("28d", on="Date").agg({'Amount': 'sum', 'index': 'max'}).reset_index(drop=True).set_index('index'),但后来我得到ValueError: cannot reindex from a duplicate axis

标签: python pandas


【解决方案1】:

根据问题的 cmets,看来 OP 已经找到了解决方案。但是,这是尝试提供另一种解决此问题的方法,即解决错误的根本原因 - 重复日期值。

为了解决这个问题,我们可以在应用中添加按日期聚合。在下面的 sn-p 中,Amount 值是使用 sum 聚合的,但在某些情况下可能应该使用另一个聚合,例如minmax。这是相关部分:

    .apply(
        lambda g: (
            g
            .groupby('Date', as_index=False)
            .agg({'Amount': 'sum'})
            .rolling('28d', on='Date')
            .sum()
        )
    )

下面是完整的sn-p:

import pandas as pd
import datetime

df = pd.DataFrame(
    {
        'ID': {0: 10001, 1: 10001, 2: 10001, 3: 10001, 4: 10002, 5: 10002, 6: 10002},
        'Date': {
            0: datetime.datetime(2019, 7, 1),
            1: datetime.datetime(2019, 5, 1),
            2: datetime.datetime(2019, 6, 25),
            3: datetime.datetime(2019, 5, 27),
            4: datetime.datetime(2019, 6, 29),
            5: datetime.datetime(2019, 7, 18),
            6: datetime.datetime(2019, 7, 18)
        },
        'Amount': {0: 50, 1: 15, 2: 10, 3: 20, 4: 25, 5: 35, 6: 40},
    }
)

amounts = (
    df
    .groupby(["ID"])
    .apply(
        lambda g: (
            g
            .groupby('Date', as_index=False)
            .agg({'Amount': 'sum'})
            .rolling('28d', on='Date')
            .sum()
        )
    )
)

df['amount_4wk_rolling'] = df["Date"].map(amounts.set_index('Date')['Amount'])

# this yields
#       ID       Date  Amount  amount_4wk_rolling
# 0  10001 2019-07-01      50                60.0
# 1  10001 2019-05-01      15                15.0
# 2  10001 2019-06-25      10                10.0
# 3  10001 2019-05-27      20                35.0
# 4  10002 2019-06-29      25                25.0
# 5  10002 2019-07-18      35               100.0
# 6  10002 2019-07-18      40               100.0

【讨论】:

  • 谢谢!如果我有两个 Amount 列; Amount1Amount2 - 我如何在不使用 2x map 的情况下将函数应用于这两者?
  • 感谢您的奖金! :) 嗯,我不确定这是可能的。如果您正在处理大型数据帧,则可以使用dask 加快速度,但仍会涉及两个映射...
  • 好的,谢谢!我选择pd.merge amounts 回到df
  • 这是有道理的,特别是如果可以通过有意义的非重叠日期范围(例如,仅特定月份内的日期)对 dfs 进行子集化,因为这样数据框很小。
【解决方案2】:

问题是amounts的一级索引:

>>> df
      ID       Date  Amount
0  10001 2019-07-01      50
1  10001 2019-05-01      15
2  10001 2019-06-25      10
3  10001 2019-05-27      20
4  10002 2019-06-29      25
5  10002 2019-07-18      35  # <- dup date
6  10002 2019-07-18      40  # <- dup date

>>> amounts
         Amount       Date       ID
ID                                 
10001 1    15.0 2019-05-01  10001.0
      3    35.0 2019-05-27  20002.0
      2    10.0 2019-06-25  10001.0
      0    60.0 2019-07-01  20002.0
10002 4    25.0 2019-06-29  10002.0
      5    60.0 2019-07-18  20004.0
      6   100.0 2019-07-18  30006.0

如果将 amounts 映射到 Date 列以合并 df 上的数据,则会出现错误,因为 Pandas 不知道它应该在 2019 年 7 月 18 日使用哪些值。如果你仔细看amounts的第二级索引是你原始数据框的索引。

所以如果去掉groupby设置的第一级索引,就可以使用直接赋值:

df['amount_4wk_rolling'] = amounts.droplevel(0)['Amount']
print(df)

# Output:
      ID       Date  Amount  amount_4wk_rolling
0  10001 2019-07-01      50                60.0
1  10001 2019-05-01      15                15.0
2  10001 2019-06-25      10                10.0
3  10001 2019-05-27      20                35.0
4  10002 2019-06-29      25                25.0
5  10002 2019-07-18      35                60.0
6  10002 2019-07-18      40               100.0

【讨论】:

    猜你喜欢
    • 2017-07-02
    • 2021-08-13
    • 2021-01-16
    • 2023-03-15
    • 2020-10-21
    • 2022-10-25
    • 2021-05-27
    • 2020-05-27
    • 1970-01-01
    相关资源
    最近更新 更多