Агрегат панд с динамическими именами столбцов

У меня есть скрипт, который генерирует фрейм данных Pandas с различным количеством столбцов значений. Например, этот df может быть

import pandas as pd
df = pd.DataFrame({
'group': ['A', 'A', 'A', 'B', 'B'],
'group_color' : ['green', 'green', 'green', 'blue', 'blue'],
'val1': [5, 2, 3, 4, 5], 
'val2' : [4, 2, 8, 5, 7]
})

  group group_color  val1  val2
0     A       green     5     4
1     A       green     2     2
2     A       green     3     8
3     B        blue     4     5
4     B        blue     5     7

Моя цель - получить сгруппированное среднее значение для каждого из столбцов значений. В этом конкретном случае (с двумя столбцами значений) я могу использовать

df.groupby('group').agg({"group_color": "first", "val1": "mean", "val2": "mean"})

      group_color      val1      val2
group                                
A           green  3.333333  4.666667
B            blue  4.500000  6.000000

но это не работает, когда рассматриваемый фрейм данных имеет больше столбцов значений (val3, val4 и т.д.). Есть ли способ динамически принять среднее значение "все остальные столбцы" или "все столбцы, содержащие в своих именах val"?

Ответы

Ответ 1

Более легко, как

df.groupby('group').agg(lambda x : x.head(1) if x.dtype=='object' else x.mean())
Out[63]: 
      group_color      val1      val2
group                                
A           green  3.333333  4.666667
B            blue  4.500000  6.000000

Ответ 2

Если ваш group_color всегда одинаков в пределах одной группы, вы можете сделать следующее:

df.pivot_table(index=['group','group_color'],aggfunc='mean')

Выход:

                       val1      val2
group group_color                    
A     green        3.333333  4.666667
B     blue         4.500000  6.000000

В другом случае вы можете создать словарь и передать его в agg:

agg_dict = {f: 'first' if f=='group_color' else 'mean' for f in df.columns[1:]}
df.groupby('group').agg(agg_dict)

Какой вывод:

      group_color      val1      val2
group                                
A           green  3.333333  4.666667
B            blue  4.500000  6.000000

Ответ 3

К сожалению, вам придется применять обе функции агрегирования по отдельности (или повторить "valn": "mean" столько раз, сколько столбцов valx). Groupby.agg может принимать словарь, но ключи должны быть отдельными столбцами.

Я бы сделал это, используя DataFrame.filter, чтобы выбрать подмножество кадра данных со столбцами, соответствующими формату valx, агрегировать со средним значением, а затем назначить новые столбцы с агрегированными результатами для других столбцов:

(df.filter(regex=r'^val').groupby(df.group).mean()
   .assign(color = df.group_color.groupby(df.group).first()))

         val1      val2    color
group                           
A      3.333333  4.666667  green
B      4.500000  6.000000   blue

Ответ 4

За комментарий OP

enter image description here

Мы можем группировать по 'group' и 'group_color' без риска наличия более одного 'group_color' на 'group'

Следовательно:

df.groupby(['group', 'group_color']).mean().reset_index(level=1)

      group_color      val1      val2
group                                
A           green  3.333333  4.666667
B            blue  4.500000  6.000000

Ответ 5

Вы можете использовать 2 словаря, которые вы можете комбинировать следующим образом:

df.groupby('group').agg({**{'group_color': 'first'}, **{c: 'mean' for c in df.columns if c.startswith('val')}})

В этом случае у вас есть один dict с фиксированными агрегатами, а другой с динамическим выбором столбцов.