2017-04-14 7 views
1

Variationen dieser Frage wurden gestellt (siehe this question), aber ich habe keine gute Lösung gefunden für einen gemeinsamen Anwendungsfall von groupby in Pandas scheinen.Wenden Sie mehrere Funktionen gleichzeitig auf Pandas groupby Objekt

sagen, dass ich die Datenrahmen lasts und I-Gruppe haben von 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]}) 

Und ich habe diese Funktionen ich groupby_obj anwenden möchten (was die Funktionen tun, ist nicht wichtig, und ich machte sie auf, nur wissen, dass sie mehrere Spalten aus dem Datenrahmen) erfordern:

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) 

ich konnte apply jede dieser Funktionen separat an den Datenrahmen und dann verschmelzen den daraus resultierenden Datenrahmen, aber das scheint ineffizient, ist unelegant, und ich stelle mir vor, es muss eine One-Line-Lösung sein.

Ich habe nicht wirklich eine gefunden, obwohl diese blog post (Suche nach "Erstellen Sie eine Funktion, um die Statistiken einer Gruppe" auf der Unterseite der Seite) schlug die Funktionen in eine Funktion als ein Wörterbuch soly:

def get_stats(group): 
    return {'custom_column_1': custom_func(group), 'custom_column_2':custom_func2(group)} 

Allerdings, wenn ich den Code groupby_obj.apply(get_stats), statt Spalten laufen bekomme ich eine Spalte Wörterbuch Ergebnisse:

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 

wenn in der Realität würde ich mag verwenden eine Codezeile, um etwas näher an diesen Datenrahmen zu bringen:

user custom_column_1 custom_column_2 
a 29993.0    10000 
d 22493.5    75000 
s 19992.0    30000 

Vorschläge zur Verbesserung dieses Workflows?

Antwort

3

Wenn Sie die get_stats Funktion leicht ändern:

def get_stats(group): 
    return pd.Series({'custom_column_1': custom_func(group), 
         'custom_column_2':custom_func2(group)}) 

jetzt können Sie einfach tun:

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 

Alternative (bisschen hässlich) Ansatz, der Ihre Funktionen (unverändert) verwendet:

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

IMHO Wrapping das Wörterbuch in einer Serie ist die beste Lösung für diese Frage der drei, die Sie vorgestellt haben. Vielen Dank. –

+0

@ zthomas.nc, Sie sind willkommen :) Bitte beachten Sie [akzeptieren] (http://meta.stackexchange.com/a/5235) eine Antwort, wenn Sie denken, dass es Ihre Frage beantwortet hat – MaxU

+0

Stimmen Sie mit @ zthomas.nc Und ja Vergiss nicht zu akzeptieren .... – piRSquared

3

Betrachten Sie den folgenden Ansatz:

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() 

Ergebnis:

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 

Erläuterung (Schritt für Schritt):

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

Auch gut ... Mausklick zwei – piRSquared

+0

@piRSquared, danke! – MaxU

Verwandte Themen