Python 使用pandas在单个groupby调用中执行多个操作?
我想在按日期分组后生成一个摘要数据框。我希望有一列显示给定列的实际平均值,以及过滤大于0的实例后该列的平均值。我知道了如何做到这一点(见下文),但这需要执行两个单独的Python 使用pandas在单个groupby调用中执行多个操作?,python,pandas,pandas-groupby,Python,Pandas,Pandas Groupby,我想在按日期分组后生成一个摘要数据框。我希望有一列显示给定列的实际平均值,以及过滤大于0的实例后该列的平均值。我知道了如何做到这一点(见下文),但这需要执行两个单独的groupby调用,重命名列,然后将它们重新连接在一起。我觉得一个人应该可以在一个电话里完成这一切。我试图使用eval来执行此操作,但不断出现错误,并被告知使用apply,我无法在groupby对象上使用eval 代码可以满足我的需求,但似乎效率不高: # Sample data data = pd.DataFrame(
groupby
调用,重命名列,然后将它们重新连接在一起。我觉得一个人应该可以在一个电话里完成这一切。我试图使用eval
来执行此操作,但不断出现错误,并被告知使用apply
,我无法在groupby对象上使用eval
代码可以满足我的需求,但似乎效率不高:
# Sample data
data = pd.DataFrame(
{"year" : [2013, 2013, 2013, 2014, 2014, 2014],
"month" : [1, 2, 3, 1, 2, 3],
"day": [1, 1, 1, 1, 1, 1],
"delay": [0, -4, 50, -60, 9, 10]})
subset = (data
.groupby(['year', 'month', 'day'])['delay']
.mean()
.reset_index()
.rename(columns = {'delay' : 'avg_delay'})
)
subset_1 = (data[data.delay > 0]
.groupby(['year', 'month', 'day'])['delay']
.mean()
.reset_index()
.rename(columns = {'delay' : 'avg_delay_pos'})
)
combined = pd.merge(subset, subset_1, how='left', on=['year', 'month', 'day'])
combined
year month day avg_delay avg_delay_pos
0 2013 1 1 0 NaN
1 2013 2 1 -4 NaN
2 2013 3 1 50 50.0
3 2014 1 1 -60 NaN
4 2014 2 1 9 9.0
5 2014 3 1 10 10.0
解决方案是针对您的问题的,但您可以使用单个groupby调用来实现这一点。要获得“avg_delay_pos”,只需删除负值(和零值)
细分
其中
用于屏蔽非正值
df['delay_pos'] = df['delay'].where(df['delay'] > 0)
# df['delay'].where(df['delay'] > 0)
0 NaN
1 NaN
2 50.0
3 NaN
4 9.0
5 10.0
Name: delay, dtype: float64
接下来,提取要分组的延迟列
df.filter(like='delay')
delay delay_pos
0 0 NaN
1 -4 NaN
2 50 50.0
3 -60 NaN
4 9 9.0
5 10 10.0
然后在日期执行一个groupby
_.groupby(pd.to_datetime(df[['year', 'month', 'day']])).mean()
delay delay_pos
2013-01-01 0 NaN
2013-02-01 -4 NaN
2013-03-01 50 50.0
2014-01-01 -60 NaN
2014-02-01 9 9.0
2014-03-01 10 10.0
如果使用pd.to_datetime
将年/月/日列转换为单个datetime列,则在单个列上分组比在多个列上分组更有效
pd.to_datetime(df[['year', 'month', 'day']])
0 2013-01-01
1 2013-02-01
2 2013-03-01
3 2014-01-01
4 2014-02-01
5 2014-03-01
dtype: datetime64[ns]
最后的。添加前缀(“avg”)
在结果中添加前缀“\u avg”
如果您想要单独的年/月/日列,另一种方法是
df['delay_pos'] = df['delay'].where(df['delay'] > 0)
df.groupby(['year', 'month', 'day']).mean().add_prefix('avg_').reset_index()
year month day avg_delay avg_delay_pos
0 2013 1 1 0 NaN
1 2013 2 1 -4 NaN
2 2013 3 1 50 50.0
3 2014 1 1 -60 NaN
4 2014 2 1 9 9.0
5 2014 3 1 10 10.0
IIUC,您可以使用以下代码:
>>> data['avg_delay'] = data.pop('delay')
>>> data['avg_delay_pos'] = data.loc[data['avg_delay'].gt(0), 'avg_delay']
>>> data
day month year avg_delay avg_delay_pos
0 1 1 2013 0 NaN
1 1 2 2013 -4 NaN
2 1 3 2013 50 50.0
3 1 1 2014 -60 NaN
4 1 2 2014 9 9.0
5 1 3 2014 10 10.0
>>>
说明:
- 我首先删除了
列,并将其分配给新名称delay
,因此我实际上将avg\u delay
的名称重命名为delay
avg\u delay
- 然后我创建了一个名为
的新列,它首先使用avg_delay\u pos
获取大于零的值,由于索引不会重置,因此它会将大于零的索引设置为loc
的值,而其他索引则不包含任何赋值,也就是说,他们将像您预期的那样avg_delay
NaN
apply
。可能combined=data.groupby(['year','month','day'])['delay','pos'].mean().reset_index().rename(columns={'delay':'avg_delay','pos':'avg_delay_pos})就足够了。但如果没有一些数据,我无法确定……添加了示例数据我想我学到的是,这些类型的操作不能在一行中完成。@BenG这是怎么正确的,为什么正确?您得到与groupby解决方案相同答案的唯一原因是,日历日期在示例数据中是唯一的。我认为这是显而易见的。我想我学到的是这些类型的操作不能在一行中完成。@BenG-PS,你可以在一行中做任何你想做的事情。我的第二种方法可以简化为df.assign(delay_pos=df['delay']].where(df['delay']>0)).groupby(['year','month','day']).mean().add_prefix('avg_').reset_index()
,这是一行。为了清晰起见,我将其扩展为两个。这不是我该说的,但我觉得你已经放弃了你的赏金:(
>>> data['avg_delay'] = data.pop('delay')
>>> data['avg_delay_pos'] = data.loc[data['avg_delay'].gt(0), 'avg_delay']
>>> data
day month year avg_delay avg_delay_pos
0 1 1 2013 0 NaN
1 1 2 2013 -4 NaN
2 1 3 2013 50 50.0
3 1 1 2014 -60 NaN
4 1 2 2014 9 9.0
5 1 3 2014 10 10.0
>>>