首页 > 解决方案 > 分配 groupby 多个索引的结果,应用自定义函数,返回父数据框

问题描述

我有一个数据框,我想在其中对 2 列进行分组,从每组中第 3 列的奇数行中减去偶数,并将结果分配给原始数据框中的列。不过,我从其他答案中尝试的任何方法似乎都对我有用。

示例数据框:

    ID Day OtherInfo log_timestamp
    A  17   foo       t1
    A  17   bar       t2
    A  18   footoo    t3
    A  18   bar_bar   t4

其中 log_timestamp 是一个日期时间对象。

我想要的结果应该类似于:

    ID Day OtherInfo log_timestamp duration
    A  17   foo       t1           (t2-t1)
    A  17   bar       t2 
    A  18   footoo    t3            (t4-t3)
    A  18   bar_bar   t4

我尝试过定义自己的函数、使用 lambda 函数以及使用“apply”、“agg”、“map”和“transform”的组合,但我不太明白。

>>>my_df['duration'] = my_df.groupby(['ID', 'day'])['log_timestamp'].agg({'duration': lambda series: (series - series.shift())[1::2].reset_index(drop=True)})
TypeError: incompatible index of inserted column with frame index

>>>my_df['duration'] = my_df['day'].map(my_df.groupby(['ID', 'day'])['log_timestamp'].apply({'duration': lambda series: (series - series.shift())[1::2].reset_index(drop=True)}))
TypeError: unhashable type: 'dict'

>>>my_df['duration'] = my_df.groupby(['ID', 'day'])['log_timestamp'].transform(lambda series: (series - series.shift())[1::2].reset_index(drop=True))
ValueError: Length of passed values is 1, index implies 2

标签: pythonpandaspandas-groupby

解决方案


我使用了 followitg 测试 DataFrame(带有“真实”时间戳):

  ID  Day OtherInfo       log_timestamp
0  A   17       foo 2019-09-01 10:20:00
1  A   17       bar 2019-09-01 11:30:00
2  A   18    footoo 2019-09-01 15:10:00
3  A   18   bar_bar 2019-09-01 15:55:00

从定义一个从 2 个源值(时间戳)计算 2 个结果值的函数开始:

def fn(grp):
    return [grp.iloc[1] - grp.iloc[0], np.nan]

第一个返回值是差值,第二个是NaN

使用它的方式如下:

df['duration'] = df.groupby(np.arange(len(df.index)) // 2)\
    .log_timestamp.transform(fn)

结果是:

  ID  Day OtherInfo       log_timestamp duration
0  A   17       foo 2019-09-01 10:20:00 01:10:00
1  A   17       bar 2019-09-01 11:30:00      NaT
2  A   18    footoo 2019-09-01 15:10:00 00:45:00
3  A   18   bar_bar 2019-09-01 15:55:00      NaT

groupby(np.arange(len(df.index)) // 2)是一个 pandasonic “成语”,将DataFrame(或Series)按 2 行(或元素)分组。

然后transform(fn)生成一个值序列,类似于原始序列的“副本”(一对时间戳)。

所以第一个返回值 - 两个时间戳之间的差异 - 是第一个元素的新值和NaN - 第二个元素。

因为目标列是Timestamp类型,所以NaN被转换为NaT


推荐阅读