web-dev-qa-db-fra.com

Pandas agrégat avec noms de colonnes dynamiques

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"?

22
MartijnVanAttekum

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
14
YOBEN_S

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
7
Quang Hoang

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
6
yatu

Selon le commentaire de l'OP

enter image description here

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
4
piRSquared

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.

1
zipa