如何使用转换条件转换 pandas groupby?
How do I transform a pandas groupby with a condition on the transformation?
我有一个包含会话和出价数据的数据框,其中有三列(感兴趣):user_id、事件和日期。
- user_id只是一个标识用户的ID
- 事件是出价或会话
- 日期是一个日期时间对象
现在我要做的是在我的数据框中添加一列,这是第一次出价的日期。我已经尝试了几种方法来让它工作,但问题是用户在出价之前生成会话当然很常见。
我尝试了多种方法来使过滤器正常工作,但它似乎并没有像我认为的那样工作。从文档中它说 "Return a copy of a DataFrame excluding elements from groups that do not satisfy the boolean criterion specified by func." 听起来像我想要的,忽略组中的会话事件而不是出价事件。
df['first bid date'] = df.groupby('user_id').filter(lambda x: x['event'] == 'bid')['date'].transform('min')
当这不起作用时,我尝试让转换采用自定义函数,如下所示:
def custom_transform(group):
return group[group['event'] == 'bid']['date'].min()
df['first bid date'] = df.groupby('user_id').['date'].transform(custom_transform)
但是这是行不通的,因为转换不能同时访问日期和事件,似乎无论我用什么 groupby。
最后我尝试按 user_id 和这样的事件分组
df['first bid date'] = df.groupby(['user_id', 'event'])['date'].transform('min')
哪种方法可行,但由于现在有第一个会话和第一个出价,所以我不得不将所有第一个会话更改为第一个出价。
有什么意见可以让这个 oneliner 正常工作吗?似乎 groupby、filter 和 transform 的组合应该可以解决问题,但我就是无法破解它。
谢谢!
想法是将transform
之前不匹配的值替换为缺失值,这里是Series.where
:
df['first bid date'] = (df.assign(date = df['date'].where(df['event'] == 'bid'))
.groupby('user_id')['date']
.transform('min'))
这是一些示例代码,其中包含一个数据框来匹配问题。
from io import StringIO
csv = StringIO("""index,uid,event,date
0,1,"bid",'2010-01-01'
1,1,"bid",'2013-01-01'
2,1,"session",'2009-01-01'
3,2,"session",'2010-01-01'
4,2,"bid",'2015-01-01'
5,2,"bid",'2017-01-01'""")
df = pd.read_csv(csv, index_col='index').reset_index(drop=True)
此替代方法使用 merge
函数。
df.merge(df[df['event']=='bid'].groupby('uid')['date'].min(),
on='uid', suffixes=('','_first_bid'))
打印:
uid event date date_first_bid
0 1 bid 2010-01-01 2010-01-01
1 1 bid 2013-01-01 2010-01-01
2 1 session 2009-01-01 2010-01-01
3 2 session 2010-01-01 2015-01-01
4 2 bid 2015-01-01 2015-01-01
5 2 bid 2017-01-01 2015-01-01
我有一个包含会话和出价数据的数据框,其中有三列(感兴趣):user_id、事件和日期。
- user_id只是一个标识用户的ID
- 事件是出价或会话
- 日期是一个日期时间对象
现在我要做的是在我的数据框中添加一列,这是第一次出价的日期。我已经尝试了几种方法来让它工作,但问题是用户在出价之前生成会话当然很常见。
我尝试了多种方法来使过滤器正常工作,但它似乎并没有像我认为的那样工作。从文档中它说 "Return a copy of a DataFrame excluding elements from groups that do not satisfy the boolean criterion specified by func." 听起来像我想要的,忽略组中的会话事件而不是出价事件。
df['first bid date'] = df.groupby('user_id').filter(lambda x: x['event'] == 'bid')['date'].transform('min')
当这不起作用时,我尝试让转换采用自定义函数,如下所示:
def custom_transform(group):
return group[group['event'] == 'bid']['date'].min()
df['first bid date'] = df.groupby('user_id').['date'].transform(custom_transform)
但是这是行不通的,因为转换不能同时访问日期和事件,似乎无论我用什么 groupby。
最后我尝试按 user_id 和这样的事件分组
df['first bid date'] = df.groupby(['user_id', 'event'])['date'].transform('min')
哪种方法可行,但由于现在有第一个会话和第一个出价,所以我不得不将所有第一个会话更改为第一个出价。
有什么意见可以让这个 oneliner 正常工作吗?似乎 groupby、filter 和 transform 的组合应该可以解决问题,但我就是无法破解它。
谢谢!
想法是将transform
之前不匹配的值替换为缺失值,这里是Series.where
:
df['first bid date'] = (df.assign(date = df['date'].where(df['event'] == 'bid'))
.groupby('user_id')['date']
.transform('min'))
这是一些示例代码,其中包含一个数据框来匹配问题。
from io import StringIO
csv = StringIO("""index,uid,event,date
0,1,"bid",'2010-01-01'
1,1,"bid",'2013-01-01'
2,1,"session",'2009-01-01'
3,2,"session",'2010-01-01'
4,2,"bid",'2015-01-01'
5,2,"bid",'2017-01-01'""")
df = pd.read_csv(csv, index_col='index').reset_index(drop=True)
此替代方法使用 merge
函数。
df.merge(df[df['event']=='bid'].groupby('uid')['date'].min(),
on='uid', suffixes=('','_first_bid'))
打印:
uid event date date_first_bid
0 1 bid 2010-01-01 2010-01-01
1 1 bid 2013-01-01 2010-01-01
2 1 session 2009-01-01 2010-01-01
3 2 session 2010-01-01 2015-01-01
4 2 bid 2015-01-01 2015-01-01
5 2 bid 2017-01-01 2015-01-01