Агрегат панд с динамическими именами столбцов
У меня есть скрипт, который генерирует фрейм данных 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
Мы можем группировать по '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
с фиксированными агрегатами, а другой с динамическим выбором столбцов.