2017-04-14 103 views
1

已经提出了这个问题的变化(见this question),但我还没有找到一个好的解决方案,似乎是Pandas中的一个常见使用案例groupby一次应用多个功能到Pandas groupby object

说我有user数据框lasts和I组:

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每项功能分开到数据帧,然后合并所产生的dataframes,但似乎效率不高,是不雅的,我想应该有一个单线解决方案。

我还没有真正找到一个,虽然这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 

有关改进此工作流程的建议?

回答

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 
+1

恕我直言,在一个系列中包装字典是你提出的三个问题的最佳解决方案。谢谢。 –

+0

@ zthomas.nc,欢迎您:)如果您认为它已回答您的问题,请考虑[接受](http://meta.stackexchange.com/a/5235)答案 – MaxU

+0

同意@ zthomas.nc而且是,别忘了接受.... – piRSquared

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 
+0

也不错... ...鼠标点击两个 – piRSquared

+0

@piRSquared,谢谢! – MaxU