熊猫:使用多个功能进行分组和聚合

时间:2017-07-16 22:32:23

标签: python pandas dataframe aggregate

情况

我的pandas数据框定义如下:

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

问题

我想在df上执行分组和聚合操作,它会为我提供以下结果数据框:

  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_valueScore列中的组最大值。
  • Max_score_elementElement列中与组最大Score值对应的值。
  • Max_score_caseCase列中与组最大Score值对应的值。
  • Min_evaluationEvaluation列中的组最小值。

到目前为止尝试

我已经为分组和聚合提出了以下代码:

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 个答案:

答案 0 :(得分: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

答案 1 :(得分: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

虽然它提供了所需的输出,但我不确定它的效率是否低于你的方法。

答案 2 :(得分: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

答案 3 :(得分: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