Pandas链式调用排序后分组累计求和结果异常原因咨询
链式调用与原地排序的结果差异解析
原始数据
| player_id | device_id | event_date | games_played |
|---|---|---|---|
| 1 | 2 | 2016-03-01 | 5 |
| 1 | 2 | 2016-05-02 | 1 |
| 1 | 3 | 2015-06-25 | 2 |
| 3 | 1 | 2016-03-02 | 10 |
| 3 | 4 | 2016-02-03 | 15 |
问题场景
链式调用代码及异常结果
activity.sort_values('event_date').assign( games_played_so_far = activity.groupby('player_id') .games_played.cumsum())[['player_id', 'event_date', 'games_played_so_far']]
输出结果(games_played_so_far不符合预期):
| player_id | event_date | games_played_so_far |
|---|---|---|
| 1 | 2015-06-25 | 8 |
| 3 | 2016-02-03 | 25 |
| 1 | 2016-03-01 | 5 |
| 3 | 2016-03-02 | 10 |
| 1 | 2016-05-02 | 6 |
原地排序代码及正确结果
activity.sort_values('event_date', inplace = True) activity.assign(games_played_so_far = activity.groupby('player_id') .games_played.cumsum())[['player_id', 'event_date', 'games_played_so_far']]
输出结果:
| player_id | event_date | games_played_so_far |
|---|---|---|
| 1 | 2015-06-25 | 2 |
| 1 | 2016-03-01 | 7 |
| 1 | 2016-05-02 | 8 |
| 3 | 2016-02-03 | 15 |
| 3 | 2016-03-02 | 25 |
原因分析
核心问题:链式调用中groupby引用的是原始未排序数据
activity.sort_values('event_date')会生成一个全新的排序后DataFrame,但assign参数里的activity.groupby(...)依然指向原始未排序的activity对象,不是前面排序后的结果。
- 原始数据中player_id=1的行顺序是:2016-03-01(5)→2016-05-02(1)→2015-06-25(2),所以
cumsum计算结果是5→6→8。 - 排序后的新DataFrame把player_id=1的行顺序调整为2015-06-25→2016-03-01→2016-05-02,但
assign时会按原始索引把旧的cumsum值对应到新行上,导致数值完全错位。
而原地排序inplace=True直接修改了原始activity的行顺序,此时groupby基于排序后的数据计算,cumsum自然是按时间递增累加,结果正确。
为什么不是所有DataFrame都会出现异常?
只有当原始DataFrame的行顺序不满足分组内按event_date递增时才会出问题:
- 如果原始数据已经是按
player_id+event_date排序好的,那么链式调用中原始数据的cumsum结果和排序后的行顺序一致,不会出现错位。 - 只有当分组内的时间顺序混乱时,才会暴露这个引用错误。
正确的链式调用写法
要让groupby基于排序后的DataFrame计算,在链式调用里用lambda x指代前面的结果即可:
activity.sort_values('event_date').assign( games_played_so_far = lambda x: x.groupby('player_id') .games_played.cumsum())[['player_id', 'event_date', 'games_played_so_far']]
这里的x就是sort_values生成的排序后DataFrame,cumsum基于分组内的时间顺序计算,结果符合预期。
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

