2017-07-16 107 views
4

现状

我有一个熊猫数据帧定义如下:熊猫:分组和聚合具有多种功能

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) 

它看起来像这样在控制台输出:

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 

问题

我想对执行分组和汇总操作会给我下面的结果数据框:

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 

要澄清的详细信息:我想组由Group列,然后应用聚合,得到以下结果列:

  • Max_score_value:来自Score列的组最大值。
  • Max_score_element:从对应于所述基团的最大ScoreElement列的值。
  • Max_score_case:从对应于所述基团的最大ScoreCase列的值。
  • Min_evaluation:从Evaluation列中的基团的最小值。因此

尝试远

我来为分组和聚集下面的代码:

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

这给作为输出:

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

由于你可以看到基本数据在那里,但它还不是我需要的格式。这是我努力的最后一步。有没有人有这样的想法来生成我想要的格式的结果数据框?

回答

4

result数据帧开始,可以在两个步骤中的变换如下,以所需的格式:

# 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 

另一种从原始df使用join开始,这可能不那么有效但更简练类似@ 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 

与示例数据集天真时间:

%%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

哦,明确设定指标和加入后这是一个不错的改进。 – tarashypka

+0

我很高兴从这里所有海报的优秀答案。我认为现在我会用Psidom的答案中的'join'来解决这个问题,因为我喜欢低冗长度。没关系,这是慢一点,因为我当前程序中的性能瓶颈在其他地方。 – Xukrao

2

这里是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 

可能的解决方案虽然它提供了所需的输出,我不知道,如果它不是有效的小于你接近。

1

您可以使用apply而不是agg来一次构建所有列。

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

我就拿

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