2017-07-16 1 views
4

Situation

J'ai un suit dataframe défini comme de pandas géants:Pandas: le regroupement et l'agrégation avec plusieurs fonctions

import pandas as pd 

headers = ['Group', 'Element', 'Case', 'Score', 'Evaluation'] 
data = [ 
    ['A', 1, 'x', 1.40, 0.59], 
    ['A', 1, 'y', 9.19, 0.52], 
    ['A', 2, 'x', 8.82, 0.80], 
    ['A', 2, 'y', 7.18, 0.41], 
    ['B', 1, 'x', 1.38, 0.22], 
    ['B', 1, 'y', 7.14, 0.10], 
    ['B', 2, 'x', 9.12, 0.28], 
    ['B', 2, 'y', 4.11, 0.97], 
] 
df = pd.DataFrame(data, columns=headers) 

qui ressemble à ceci dans la sortie de la console:

Group Element Case Score Evaluation 
0  A  1 x 1.40  0.59 
1  A  1 y 9.19  0.52 
2  A  2 x 8.82  0.80 
3  A  2 y 7.18  0.41 
4  B  1 x 1.38  0.22 
5  B  1 y 7.14  0.10 
6  B  2 x 9.12  0.28 
7  B  2 y 4.11  0.97 

Problème

Je souhaite effectuer une opération de regroupement et d'agrégation sur df qui me donnera le résultat suivant dataframe:

Group Max_score_value Max_score_element Max_score_case Min_evaluation 
0  A    9.19     1    y   0.41 
1  B    9.12     2    x   0.10 

Pour préciser plus en détail: J'aimerais groupe par la colonne Group, puis appliquez l'agrégation pour obtenir les colonnes de résultat suivant:

  • Max_score_value: valeur maximale du groupe de la colonne Score.
  • Max_score_element: la valeur de la colonne Element qui correspond à la valeur maximale du groupe Score.
  • Max_score_case: la valeur de la colonne Case qui correspond à la valeur maximale du groupe Score.
  • Min_evaluation: la valeur minimale du groupe de la colonne Evaluation.

Essayé jusqu'ici

Je suis venu avec le code suivant pour le regroupement et l'agrégation:

result = (
    df.set_index(['Element', 'Case']) 
    .groupby('Group') 
    .agg({'Score': ['max', 'idxmax'], 'Evaluation': 'min'}) 
    .reset_index() 
) 
print(result) 

qui donne en sortie:

Group Score   Evaluation 
      max idxmax  min 
0  A 9.19 (1, y)  0.41 
1  B 9.12 (2, x)  0.10 

Comme vous pouvez voir les données de base sont là, mais ce n'est pas tout à fait dans le format dont j'ai besoin. C'est la dernière étape avec laquelle je me bats. Est-ce que quelqu'un ici a de bonnes idées pour générer un dataframe de résultat dans le format que je cherche?

Répondre

4

A partir de la trame de données result, vous pouvez transformer en deux étapes comme suit au format dont vous avez besoin:

# collapse multi index column to single level column 
result.columns = [y + '_' + x if y != '' else x for x, y in result.columns] 
​ 
# split the idxmax column into two columns 
result = result.assign(
    max_score_element = result.idxmax_Score.str[0], 
    max_score_case = result.idxmax_Score.str[1] 
).drop('idxmax_Score', 1) 

result 

#Group max_Score min_Evaluation max_score_case max_score_element 
#0 A  9.19    0.41    y     1 
#1 B  9.12    0.10    x     2 

Une alternative à partir de l'original df en utilisant join, qui peut ne pas être aussi efficace mais moins bavard semblable à l'idée de @ tarashypka:

(df.groupby('Group') 
    .agg({'Score': 'idxmax', 'Evaluation': 'min'}) 
    .set_index('Score') 
    .join(df.drop('Evaluation',1)) 
    .reset_index(drop=True)) 

#Evaluation Group Element Case Score 
#0  0.41  A  1  y 9.19 
#1  0.10  B  2  x 9.12 

calendrier Naive avec l'ensemble de données exemple:

%%timeit 
(df.groupby('Group') 
.agg({'Score': 'idxmax', 'Evaluation': 'min'}) 
.set_index('Score') 
.join(df.drop('Evaluation',1)) 
.reset_index(drop=True)) 
# 100 loops, best of 3: 3.47 ms per loop 

%%timeit 
result = (
    df.set_index(['Element', 'Case']) 
    .groupby('Group') 
    .agg({'Score': ['max', 'idxmax'], 'Evaluation': 'min'}) 
    .reset_index() 
) 
​ 
result.columns = [y + '_' + x if y != '' else x for x, y in result.columns] 
​ 
result = result.assign(
    max_score_element = result.idxmax_Score.str[0], 
    max_score_case = result.idxmax_Score.str[1] 
).drop('idxmax_Score', 1) 
# 100 loops, best of 3: 7.61 ms per loop 
+2

Oh, l'indice définissant explicitement et se joindre à ce qui suit est une belle amélioration. – tarashypka

+0

Je suis heureux avec les excellentes réponses de toutes les affiches ici. Je pense que pour l'instant j'irai avec la solution avec 'join' de la réponse de Psidom ici, parce que j'aime la basse verbosité. C'est bon, c'est un peu plus lent, car les goulots d'étranglement dans les performances de mon programme actuel sont ailleurs. – Xukrao

2

est ici solution possible avec pd.merge

>> r = df.groupby('Group') \ 
>>  .agg({'Score': 'idxmax', 'Evaluation': 'min'}) \ 
>>  .rename(columns={'Score': 'idx'}) 
>> for c in ['Score', 'Element', 'Case']: 
>> r = pd.merge(r, df[[c]], how='left', left_on='idx', right_index=True) 
>> r.drop('Score_idx', axis=1).rename(columns={'Score': 'Max_score_value', 
>>            'Element': 'Max_score_element', 
>>            'Case': 'Max_score_case'}) 
     Evaluation Max_score_value Max_score_element Max_score_case 
Group                
A   0.41    9.19     1    y 
B   0.10    9.12     2    x 

Bien qu'il fournit la sortie désirée, je ne suis pas sûr si ce n'est pas moins efficace que le vôtre approcher .

1

Vous pouvez utiliser apply au lieu de agg pour construire toutes les colonnes en une fois.

result = (
    df.groupby('Group').apply(lambda x: [np.max(x.Score), 
           df.loc[x.Score.idxmax(),'Element'], 
           df.loc[x.Score.idxmax(),'Case'], 
           np.min(x.Evaluation)]) 
     .apply(pd.Series) 
     .rename(columns={0:'Max_score_value', 
         1:'Max_score_element', 
         2:'Max_score_case', 
         3:'Min_evaluation'}) 
     .reset_index() 
) 



result 
Out[9]: 
    Group Max_score_value Max_score_element Max_score_case Min_evaluation 
0  A    9.19     1    y   0.41 
1  B    9.12     2    x   0.10 
1

Mon Take

g = df.set_index('Group').groupby(level='Group', group_keys=False) 

result = g.apply(
    pd.DataFrame.nlargest, n=1, columns='Score' 
) 

def f(x): 
    x = 'value' if x == 'Score' else x 
    return 'Max_score_' + x.lower() 

result.drop('Evaluation', 1).rename(columns=f).assign(
    Min_evaluation=g.Evaluation.min().values).reset_index() 

    Group Max_score_element Max_score_case Max_score_value Min_evaluation 
0  A     1    y    9.19   0.41 
1  B     2    x    9.12   0.10