J'ai un script qui génère une trame de données pandas avec un nombre variable de colonnes de valeur. Par exemple, ce df pourrait être
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
Mon objectif est d'obtenir la moyenne groupée pour chacune des colonnes de valeurs. Dans ce cas précis (avec 2 colonnes de valeur), je peux utiliser
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
mais cela ne fonctionne pas lorsque le bloc de données en question a plus de colonnes de valeurs (val3, val4 etc.). Existe-t-il un moyen de prendre dynamiquement la moyenne de "toutes les autres colonnes" ou de "toutes les colonnes contenant val dans leurs noms"?
Plus facile comme
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
Si ton group_color
est toujours le même au sein d'un même groupe, vous pouvez faire:
df.pivot_table(index=['group','group_color'],aggfunc='mean')
Production:
val1 val2
group group_color
A green 3.333333 4.666667
B blue 4.500000 6.000000
Dans l'autre cas, vous pouvez construire le dictionnaire et le passer à agg
:
agg_dict = {f: 'first' if f=='group_color' else 'mean' for f in df.columns[1:]}
df.groupby('group').agg(agg_dict)
Quelle sortie:
group_color val1 val2
group
A green 3.333333 4.666667
B blue 4.500000 6.000000
Malheureusement, vous devrez appliquer les deux fonctions d'agrégation séparément (cela ou répéter "valn": "mean"
autant de fois que valx
colonnes). Groupby.agg
peut prendre un dictionnaire mais les clés doivent être des colonnes individuelles.
La façon dont je le ferais utilise DataFrame.filter
pour sélectionner le sous-ensemble de la trame de données avec les colonnes suivant le format valx
, agréger avec la moyenne, puis affecter de nouvelles colonnes avec les résultats agrégés sur les autres colonnes:
(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
Selon le commentaire de l'OP
On peut regrouper par les deux 'group'
et 'group_color'
sans risquer d'avoir plus d'un unique 'group_color'
par 'group'
Par conséquent:
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
Vous pouvez aller avec 2 dictionnaires que vous pouvez combiner comme ceci:
df.groupby('group').agg({**{'group_color': 'first'}, **{c: 'mean' for c in df.columns if c.startswith('val')}})
Dans ce cas, vous avez un dict
avec des agrégations fixes et un autre avec une sélection de colonne dynamique.