top of page

Pandas groupby en Python : comment grouper un DataFrame (agg, transform, exemples)

2 days ago
8 min read

Vous avez un tableau de ventes et votre manager vous demande « le chiffre d'affaires par région ». Dans Excel, vous feriez un tableau croisé dynamique. En SQL, un GROUP BY. En Python, avec la librairie pandas, c'est la méthode groupby() qui fait le travail, et elle va beaucoup plus loin qu'un simple total.


Dans ce tutoriel, on part d'un petit DataFrame de ventes et on voit, étape par étape, comment grouper des lignes, calculer plusieurs indicateurs par groupe, garder le détail ligne par ligne et regrouper par mois. Tous les exemples ont été testés avec pandas 3. pandas est la bibliothèque de référence pour analyser des données en Python : autant bien maîtriser sa fonction la plus utilisée.



Qu'est-ce que la fonction pandas groupby ?

Cette fonction sert à regrouper les lignes d'un DataFrame qui partagent la même valeur dans une ou plusieurs colonnes, puis à calculer un résultat pour chaque groupe. C'est l'équivalent d'une requête SQL agrégée ou d'un tableau croisé dynamique dans Excel.


Une opération groupby se déroule toujours en trois temps. Par exemple, avec des ventes par région, pandas regroupe les données comme ceci :


  1. Séparer : pandas répartit les lignes en groupes selon les valeurs de la colonne choisie.

  2. Appliquer : une fonction est appliquée à chaque groupe : somme, moyenne, comptage...

  3. Combiner : les résultats sont rassemblés dans un nouveau DataFrame.


Point important : l'appel df.groupby("region") seul ne calcule rien. Il renvoie un objet « en attente ». Le calcul se déclenche quand vous ajoutez une méthode comme sum() ou mean().



Exemple : le DataFrame de ventes à grouper

On travaille sur huit ventes, avec une date, une région, un vendeur, un produit et un montant :


import pandas as pd

# un DataFrame de 8 ventes, utilisé dans tous les exemples
ventes = pd.DataFrame({
    "date": pd.to_datetime(["2026-01-05", "2026-01-12", "2026-01-20", "2026-02-03",
                            "2026-02-10", "2026-02-18", "2026-03-02", "2026-03-15"]),
    "region": ["Nord", "Sud", "Nord", "Sud", "Nord", "Est", "Est", "Sud"],
    "vendeur": ["Léa", "Hugo", "Léa", "Inès", "Marc", "Paul", "Paul", "Hugo"],
    "produit": ["Formation", "Conseil", "Conseil", "Formation",
                "Formation", "Conseil", "Formation", "Formation"],
    "montant": [1200, 800, 950, 1500, 1100, 700, 1300, 900],
})


Comment agréger une colonne avec groupby et sum

Pour obtenir le chiffre d'affaires par région, on indique la colonne à grouper, la colonne à calculer, puis la fonction d'agrégation :


# somme d'une colonne par groupe : un total par région
ventes.groupby("region")["montant"].sum()

Résultat :


region
Est     2000
Nord    3250
Sud     3200
Name: montant, dtype: int64

Les noms de régions deviennent les étiquettes des lignes du résultat. Pour changer de calcul, il suffit de remplacer sum() par une autre fonction :


  • mean() : moyenne (le panier moyen)

  • count() : nombre de valeurs non vides

  • size() : nombre de lignes, valeurs vides comprises

  • min() / max() : plus petite et plus grande valeur

  • nunique() : nombre de valeurs distinctes, par exemple le nombre de vendeurs par région



Grouper un DataFrame sur plusieurs colonnes

Passez une liste de colonnes pour obtenir un regroupement à plusieurs niveaux, par exemple le chiffre d'affaires par région et par produit :


# deux colonnes : une ligne par couple (région, produit)
ventes.groupby(["region", "produit"])["montant"].sum()

Résultat :


region  produit
Est     Conseil       700
        Formation    1300
Nord    Conseil       950
        Formation    2300
Sud     Conseil       800
        Formation    2400

Vous préférez un tableau à double entrée, comme un tableau croisé dynamique, avec les produits en colonnes ? Ajoutez .unstack() à la fin. C'est l'objet de notre article sur les méthodes stack(), unstack(), pivot() et melt().



Appliquer plusieurs fonctions à chaque groupe avec agg()

Dans la vraie vie, on veut rarement un seul chiffre. La méthode agg() avec des agrégations nommées construit une synthèse complète en une instruction, en choisissant le nom de chaque colonne du résultat :


# une fonction d'agrégation différente pour chaque colonne
# colonnes du résultat : ca_total, panier_moyen, nb_ventes, vendeurs
synthese = ventes.groupby("region").agg(
    ca_total=("montant", "sum"),
    panier_moyen=("montant", "mean"),
    nb_ventes=("montant", "count"),
    vendeurs=("vendeur", "nunique"),
)

Résultat :


        ca_total  panier_moyen  nb_ventes  vendeurs
region
Est         2000   1000.000000          2         1
Nord        3250   1083.333333          3         2
Sud         3200   1066.666667          3         2

La syntaxe se lit ainsi : nom_de_la_colonne_résultat=("colonne_source", "fonction"). C'est la forme la plus lisible, et la plus facile à relire six mois plus tard.



Récupérer un DataFrame « plat » : as_index et reset_index()

Par défaut, la colonne de regroupement devient l'étiquette des lignes du résultat. C'est pratique pour lire, moins pour enchaîner avec un graphique, une fusion ou un export Excel. Deux solutions équivalentes :


# option 1 : garder "region" comme colonne
ventes.groupby("region", as_index=False)["montant"].sum()

# option 2 : la remettre en colonne après coup
ventes.groupby("region")["montant"].sum().reset_index()

Résultat :


  region  montant
0    Est     2000
1   Nord     3250
2    Sud     3200

Ce format est celui qu'il vous faut si vous voulez ensuite rattacher ces totaux à une autre table avec merge() : voir merge, join, concat : comment fusionner des DataFrames pandas.



Parcourir chaque groupe avec une boucle for

Pour comprendre ce que fait le regroupement en coulisses, rien ne vaut une boucle : à chaque tour, vous récupérez le nom du groupe et le sous-DataFrame correspondant.


# pour chaque groupe, la boucle donne son nom et ses lignes
for region, lignes in ventes.groupby("region"):
    print(region, len(lignes), "ventes")

# récupérer les lignes d'un seul groupe
ventes.groupby("region").get_group("Nord")

Résultat de la boucle :


Est 2 ventes
Nord 3 ventes
Sud 3 ventes

La boucle est utile pour explorer ou exporter un fichier par groupe. Pour calculer, préférez toujours les méthodes vectorisées vues plus haut, bien plus rapides.



transform() : garder le détail ligne par ligne

agg() réduit chaque groupe à une seule ligne. Mais parfois, on veut garder toutes les ventes et ajouter une information calculée sur le groupe, par exemple la part de chaque vente dans le total de sa région. C'est le rôle de transform(), qui renvoie un résultat de la même taille que le DataFrame de départ :


# transform calcule sur chaque groupe et renvoie une valeur par ligne
total_region = ventes.groupby("region")["montant"].transform("sum")
ventes["part_region"] = ventes["montant"] / total_region

Résultat (arrondi) :


  region  montant  part_region
0   Nord     1200         0.37
1    Sud      800         0.25
2   Nord      950         0.29
3    Sud     1500         0.47
...

transform() sert aussi à remplir des valeurs manquantes avec la moyenne ou la médiane de leur groupe. On le détaille dans notre article sur les valeurs manquantes dans pandas.



filter() : ne garder que certains groupes

Vous voulez uniquement les ventes des régions qui dépassent 2 500 € de chiffre d'affaires ? filter() applique une condition à chaque groupe et conserve toutes les lignes des groupes qui la respectent :


# garder uniquement les régions dont le total dépasse 2 500
ventes.groupby("region").filter(lambda g: g["montant"].sum() > 2500)

Ici, la région Est (2 000 €) disparaît, les lignes Nord et Sud sont conservées. La petite fonction anonyme lambda g: ... reçoit chaque groupe sous forme de DataFrame. Si cette syntaxe vous est peu familière, lisez notre article sur les fonctions lambda.


À ne pas confondre avec le filtrage classique, qui travaille ligne par ligne : voir les différentes méthodes de filtrage avec pandas.



Regrouper les données par mois ou par trimestre

Pour un suivi mensuel du chiffre d'affaires, convertissez la date en période, puis regroupez :


# convertir la date en mois, puis faire la somme
ventes.groupby(ventes["date"].dt.to_period("M"))["montant"].sum()

Résultat :


date
2026-01    2950
2026-02    3300
2026-03    2200

Remplacez "M" par "Q" pour un regroupement par trimestre, ou par "Y" pour l'année. Attention : la colonne doit être au format date. Si elle a été importée comme texte, convertissez-la d'abord avec pd.to_datetime() (voir comment changer le type d'une colonne dans pandas).



Récapitulatif : quelle fonction pour quel besoin ?

Méthode

Ce qu'elle renvoie

Exemple d'usage

sum(), mean(), count()...

Une ligne par groupe

CA par région

agg()

Une ligne par groupe, plusieurs indicateurs

Tableau de synthèse commercial

transform()

Autant de lignes qu'au départ

Part de chaque vente dans sa région

filter()

Les lignes des groupes retenus

Régions à plus de 2 500 €

size()

Nombre de lignes par groupe

Nombre de commandes par client



5 erreurs fréquentes à éviter

1. Confondre count() et size()

count() ignore les valeurs vides (NaN), size() compte toutes les lignes. Si une vente n'a pas de montant renseigné, les deux ne donnent pas le même nombre de ventes. Choisissez en connaissance de cause.


2. Perdre les lignes dont la clé de regroupement est vide

Par défaut, les lignes dont la région est vide sont exclues sans avertissement du résultat. Vos totaux ne correspondent alors plus au total général. Pour les faire apparaître dans un groupe à part, ajoutez dropna=False :


# les lignes sans région forment leur propre groupe
ventes.groupby("region", dropna=False)["montant"].sum()

3. Utiliser apply() quand une méthode native existe

apply() accepte n'importe quelle fonction, mais il est nettement plus lent que sum(), mean() ou agg(), qui sont optimisées. Sur quelques milliers de lignes, ça ne se voit pas ; sur plusieurs millions, la différence se compte en minutes. Réservez apply() aux calculs qu'aucune méthode native ne sait faire.


4. Oublier que la colonne de regroupement a changé de place

Après le regroupement, la région ne figure plus parmi les colonnes. Un ventes_par_region["region"] provoque alors une erreur KeyError. Pensez à as_index=False ou reset_index().


5. Vouloir afficher l'objet lui-même

print(ventes.groupby("region")) affiche un message du type DataFrameGroupBy object at 0x... et rien d'autre. Ce n'est pas un bug : il manque simplement la fonction de calcul.



SQL, Excel et pandas : les équivalences

Si vous venez d'Excel ou de SQL, ce tableau vous aidera à faire le lien :


Besoin

Excel

SQL

pandas (après le regroupement)

Total par catégorie

Tableau croisé dynamique, SOMME.SI

SUM avec regroupement

.sum()

Plusieurs indicateurs

TCD avec plusieurs valeurs

Plusieurs agrégats dans le SELECT

.agg()

Filtrer sur un total

Filtre sur le TCD

HAVING

.filter()

Total à côté de chaque ligne

SOMME.SI sur chaque ligne

Fonction de fenêtre SUM() OVER

.transform()



Mémo : groupby en 10 lignes

Voici le mémo à garder sous la main, avec les usages les plus courants :


# groupby regroupe les lignes, puis calcule un résultat par groupe
g = df.groupby("col")                  # créer les groupes
g["val"].sum()                         # un total par groupe
g.agg(total=("val", "sum"))            # plusieurs indicateurs, colonnes nommées
g["val"].transform("sum")              # une valeur par ligne d'origine
g.filter(lambda x: len(x) > 2)         # garder certains groupes seulement
g.size()                               # nombre de lignes par groupe
g[["a", "b"]].max()                    # plusieurs colonnes à la fois
df.groupby(level=0).sum()              # regrouper selon l'index
df.groupby("col", dropna=False)        # garder les clés vides (NaN)


Pour aller plus loin


Le regroupement de données fait partie des notions que nous pratiquons sur des données réelles dès le premier jour de notre formation Python pour l'analyse de données. La formation est éligible au CPF : voir les modalités de financement.




Questions fréquentes sur pandas groupby

groupby() range les lignes d'un DataFrame en groupes selon la valeur d'une ou plusieurs colonnes, puis applique une fonction à chaque groupe (somme, moyenne, comptage...) et rassemble les résultats dans un nouveau tableau. C'est l'équivalent d'un tableau croisé dynamique Excel ou d'une requête SQL agrégée.

Passez une liste de noms de colonnes : df.groupby(["region", "produit"])["montant"].sum(). Le résultat a deux niveaux d'étiquettes, que vous pouvez aplatir avec reset_index() ou transformer en tableau à double entrée avec unstack().

agg() renvoie une ligne par groupe (un résumé), alors que transform() renvoie un résultat de la même longueur que le DataFrame d'origine, ce qui permet d'ajouter une colonne calculée sur le groupe à chaque ligne.

Les lignes dont la clé de regroupement est vide (NaN) sont exclues par défaut. Ajoutez dropna=False dans l'appel pour les conserver dans un groupe à part.

Le plus simple est d'utiliser les agrégations nommées dans agg() : df.groupby("region").agg(ca_total=("montant", "sum")). La colonne du résultat s'appelle directement ca_total.

 
 
bottom of page