已经问过这个问题的变化(参见this question),但我没有找到一个好的解决方案似乎是Pandas中groupby
的常见用例。
说我有数据框lasts
,我按user
分组:
lasts = pd.DataFrame({'user':['a','s','d','d'],
'elapsed_time':[40000,50000,60000,90000],
'running_time':[30000,20000,30000,15000],
'num_cores':[7,8,9,4]})
我想要将这些函数应用于groupby_obj
(函数的作用并不重要,我编写了它们,只知道它们需要数据帧中的多个列):
def custom_func(group):
return group.running_time.median() - group.num_cores.mean()
def custom_func2(group):
return max(group.elapsed_time) -min(group.running_time)
我可以apply
将这些函数中的每一个分别与数据帧合并,然后合并生成的数据帧,但这似乎效率低下,不够优雅,我想必须有一个单行解决方案。
我还没有找到一个,虽然这个blog post(在页面底部搜索“创建一个获取组统计信息的函数”)建议将函数包装成一个函数作为字典因此:
def get_stats(group):
return {'custom_column_1': custom_func(group), 'custom_column_2':custom_func2(group)}
但是,当我运行代码groupby_obj.apply(get_stats)
时,而不是列,我得到 列字典结果:
user
a {'custom_column_1': 29993.0, 'custom_column_2'...
d {'custom_column_1': 22493.5, 'custom_column_2'...
s {'custom_column_1': 19992.0, 'custom_column_2'...
dtype: object
实际上,我想使用一行代码来更接近这个数据帧:
user custom_column_1 custom_column_2
a 29993.0 10000
d 22493.5 75000
s 19992.0 30000
有关改进此工作流程的建议?
答案 0 :(得分:3)
考虑以下方法:
funcs = {
'running_time': {'rt_med':'median', 'rt_min':'min'},
'num_cores': {'nc_avg':'mean'},
'elapsed_time': {'et_max':'max'}
}
x = lasts.groupby('user').agg(funcs)
x.columns = x.columns.droplevel(0)
formulas = """
custom_column_1 = rt_med - nc_avg
custom_column_2 = et_max - rt_min
"""
res = x.eval(formulas, inplace=False).drop(x.columns, 1).reset_index()
结果:
In [145]: res
Out[145]:
user custom_column_1 custom_column_2
0 a 29993.0 10000
1 d 22493.5 75000
2 s 19992.0 30000
说明(一步一步):
In [146]: x = lasts.groupby('user').agg(funcs)
In [147]: x
Out[147]:
running_time num_cores elapsed_time
rt_med rt_min nc_avg et_max
user
a 30000 30000 7.0 40000
d 22500 15000 6.5 90000
s 20000 20000 8.0 50000
In [148]: x.columns = x.columns.droplevel(0)
In [149]: x
Out[149]:
rt_med rt_min nc_avg et_max
user
a 30000 30000 7.0 40000
d 22500 15000 6.5 90000
s 20000 20000 8.0 50000
In [150]: x.eval(formulas, inplace=False)
Out[150]:
rt_med rt_min nc_avg et_max custom_column_1 custom_column_2
user
a 30000 30000 7.0 40000 29993.0 10000
d 22500 15000 6.5 90000 22493.5 75000
s 20000 20000 8.0 50000 19992.0 30000
In [151]: x.eval(formulas, inplace=False).drop(x.columns, 1)
Out[151]:
custom_column_1 custom_column_2
user
a 29993.0 10000
d 22493.5 75000
s 19992.0 30000
In [152]: x.eval(formulas, inplace=False).drop(x.columns, 1).reset_index()
Out[152]:
user custom_column_1 custom_column_2
0 a 29993.0 10000
1 d 22493.5 75000
2 s 19992.0 30000
答案 1 :(得分:3)
如果您稍微修改get_stats
功能:
def get_stats(group):
return pd.Series({'custom_column_1': custom_func(group),
'custom_column_2':custom_func2(group)})
现在你可以这样做:
In [202]: lasts.groupby('user').apply(get_stats).reset_index()
Out[202]:
user custom_column_1 custom_column_2
0 a 29993.0 10000.0
1 d 22493.5 75000.0
2 s 19992.0 30000.0
使用您的函数的替代(有点丑陋)方法(未更改):
In [188]: pd.DataFrame(lasts.groupby('user')
.apply(get_stats).to_dict()) \
.T \
.rename_axis('user') \
.reset_index()
Out[188]:
user custom_column_1 custom_column_2
0 a 29993.0 10000.0
1 d 22493.5 75000.0
2 s 19992.0 30000.0