【问题标题】:Pandas rolling and transform when multiple rows have the same date当多行具有相同的日期时,熊猫滚动和变换
【发布时间】:2020-03-24 14:40:47
【问题描述】:

我有以下数据框:

x = pd.DataFrame({'user': ['a','a','a','a','b','b'], 'dt': ['2016-01-01','2016-01-02','2016-01-02','2016-01-03', '2016-01-05','2016-01-06'], 'val': [1,33,45,3,2,1]})

  user          dt  val
0    a  2016-01-01    1
1    a  2016-01-02   33
2    a  2016-01-02   45
3    a  2016-01-03    3
4    b  2016-01-05    2
5    b  2016-01-06    1

我想要一个额外的列来显示过去 2 天中每个用户分组的聚合值(在原始数据集的每一行上)。所以我想要的输出如下所示:

  user          dt  val  sum
0    a  2016-01-01    1  1
1    a  2016-01-02   33  79
2    a  2016-01-02   45  79
3    a  2016-01-03    3  81
4    b  2016-01-05    2  2
5    b  2016-01-06    1  3

我尝试了以下方法,但没有成功。

x['sum'] = x.groupby(['user']).rolling('2d', on='dt')['val'].transform('sum')

即使不使用转换,它也会给我一个错误提示:

Exception: cannot handle a non-unique multi-index!

最好的方法是什么?

【问题讨论】:

  • 按照你的逻辑,用户 a 的所有行都应该被求和,因为天差只有 1
  • 这只是一个示例。您可以考虑每个组有两个以上的日期。
  • 在您的新示例中,对于 01-03 的“用户 a”,最后两天应该是 33 + 45 + 3 = 81,不是吗?怎么是48?
  • @dgg32 你说得对,对不起。解决了这个问题。

标签: python pandas pandas-groupby


【解决方案1】:

我有一个快速而肮脏的解决方案,至少它适用于您的新旧示例。

###calculate date sum and make it into a dataframe
tmp = x.groupby(['user', "dt"])["val"].sum().to_frame("date_sum")

tmp.reset_index(inplace=True)

#### do the rolling. However, rolling will leave the first date as NaN
a = tmp.groupby("user")["dt", "date_sum"].rolling(2, on='dt')["date_sum"].sum().reset_index()

#### fill the first date NaN with data from tmp
a.loc[(a["user"] == tmp["user"]) & (a["dt"] == tmp["dt"]) & pd.isna(a["date_sum"]), "date_sum"] = tmp["date_sum"]

final = pd.merge(x, a, how="left", on=["user", "dt"])
final

输出:

    user    dt  val     date_sum
0   a   2016-01-01  1   1.0
1   a   2016-01-02  33  79.0
2   a   2016-01-02  45  79.0
3   a   2016-01-03  3   81.0
4   b   2016-01-05  2   2.0
5   b   2016-01-06  1   3.0

【讨论】:

  • 这与 OP 的输出不匹配。
  • @dgg32 我想保留原始数据框的格式并添加一个额外的列,就像我们通常使用转换所做的那样。但变换不适用于滚动。
  • 谢谢,我应该合并原来的,以便“val”列可以转移。
【解决方案2】:

您的解决方案非常接近,您必须将dt 转换为datetime,另外您必须在分配为列时访问numpy array,因为在这种情况下GroupBy.rolling 将生成MultiIndex .

这样索引不能对齐:

x['dt'] = pd.to_datetime(x['dt'])
x['sum'] = x.groupby('user').rolling('2d', on='dt')['val'].sum().to_numpy()
  user         dt  val   sum
0    a 2016-01-01    1  1.00
1    a 2016-01-02   33 34.00
2    a 2016-01-02   45 79.00
3    a 2016-01-03    3 81.00
4    b 2016-01-05    2  2.00
5    b 2016-01-06    1  3.00

注意:第 1 行和第 2 行的区别是因为这是一个“滚动和”,所以第 1 行不会是79

【讨论】:

  • 在您的解决方案中,第二行的总和为 34,与他想要的 79 不同。
  • 阅读我在底部的“通知”。非常有信心这是 OP 想要的输出。
  • 实际上,我希望每一行都有一个每日总和,以便它显示该日期的总和,而不管它在数据框中的哪个位置。你知道有什么办法吗?
  • 不确定你的意思,你能编辑你想要的输出,让它准确地显示你想要的吗?这很令人困惑。 @polaris9508
  • @Erfan 我的意思是我已经在第二行有 79 的问题中的输出。这就是我要找的。​​span>
猜你喜欢
  • 1970-01-01
  • 2021-09-21
  • 2023-03-24
  • 2014-11-13
  • 2017-01-31
  • 2020-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多