2017-04-14 95 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