Skip to content

Latest commit

 

History

History
931 lines (745 loc) · 26.3 KB

File metadata and controls

931 lines (745 loc) · 26.3 KB

🔝 Retour au Sommaire

13.2.3 GroupBy et agrégations

Introduction

L'une des opérations les plus puissantes dans l'analyse de données est la capacité de regrouper des données et d'effectuer des calculs par groupe. Par exemple :

  • Calculer les ventes totales par ville
  • Trouver la moyenne des notes par classe
  • Compter le nombre de clients par catégorie

Pandas offre la méthode groupby() qui permet d'effectuer ces opérations facilement et efficacement.

import pandas as pd  
import numpy as np  

Le concept Split-Apply-Combine

La méthode groupby() suit un paradigme en trois étapes appelé Split-Apply-Combine :

  1. Split (Diviser) : Les données sont divisées en groupes selon un critère
  2. Apply (Appliquer) : Une fonction est appliquée indépendamment à chaque groupe
  3. Combine (Combiner) : Les résultats sont combinés en une structure de données
# Visualisation conceptuelle
#
# Données originales:
#    Ville      Ventes
# 0  Paris      100
# 1  Lyon       150
# 2  Paris      120
# 3  Lyon       180
#
# SPLIT par Ville:
# Groupe Paris: [100, 120]
# Groupe Lyon:  [150, 180]
#
# APPLY (somme):
# Paris: 220
# Lyon:  330
#
# COMBINE:
# Ville
# Lyon     330
# Paris    220

GroupBy de base

Grouper par une colonne

# Créer un DataFrame d'exemple
df = pd.DataFrame({
    'Ville': ['Paris', 'Lyon', 'Paris', 'Lyon', 'Marseille', 'Paris'],
    'Produit': ['A', 'A', 'B', 'B', 'A', 'A'],
    'Ventes': [100, 150, 120, 180, 90, 110],
    'Quantité': [5, 8, 6, 9, 4, 5]
})

print("DataFrame original:")  
print(df)  

# Grouper par ville et calculer la somme
ventes_par_ville = df.groupby('Ville')['Ventes'].sum()  
print("\nVentes totales par ville:")  
print(ventes_par_ville)  

Sortie :

Ville  
Lyon         330  
Marseille     90  
Paris        330  
Name: Ventes, dtype: int64  

Comprendre l'objet GroupBy

# Créer un objet GroupBy
groupe = df.groupby('Ville')  
print("Type de l'objet:", type(groupe))  
print("Nombre de groupes:", groupe.ngroups)  
print("Noms des groupes:", list(groupe.groups.keys()))  

# Voir le contenu de chaque groupe
print("\nContenu des groupes:")  
for nom, groupe_df in groupe:  
    print(f"\n--- Groupe: {nom} ---")
    print(groupe_df)

Accéder à un groupe spécifique

# Obtenir un groupe spécifique
groupe_paris = df.groupby('Ville').get_group('Paris')  
print("Groupe Paris:")  
print(groupe_paris)  

Fonctions d'agrégation courantes

sum() - Somme

df = pd.DataFrame({
    'Catégorie': ['A', 'B', 'A', 'B', 'A'],
    'Ventes': [100, 150, 120, 180, 90],
    'Quantité': [5, 8, 6, 9, 4]
})

# Somme des ventes par catégorie
print("Somme par catégorie:")  
print(df.groupby('Catégorie')['Ventes'].sum())  

# Somme sur toutes les colonnes numériques
print("\nSomme sur toutes les colonnes:")  
print(df.groupby('Catégorie').sum())  

mean() - Moyenne

# Moyenne des ventes par catégorie
print("Moyenne par catégorie:")  
print(df.groupby('Catégorie')['Ventes'].mean())  

count() - Comptage

# Compter le nombre d'entrées par catégorie
print("Nombre d'entrées par catégorie:")  
print(df.groupby('Catégorie')['Ventes'].count())  

# Alternative : size() compte aussi les valeurs NaN
print("\nAvec size():")  
print(df.groupby('Catégorie').size())  

min() et max() - Minimum et Maximum

# Minimum et maximum par catégorie
print("Ventes minimales par catégorie:")  
print(df.groupby('Catégorie')['Ventes'].min())  

print("\nVentes maximales par catégorie:")  
print(df.groupby('Catégorie')['Ventes'].max())  

std() et var() - Écart-type et Variance

# Écart-type des ventes par catégorie
print("Écart-type par catégorie:")  
print(df.groupby('Catégorie')['Ventes'].std())  

# Variance
print("\nVariance par catégorie:")  
print(df.groupby('Catégorie')['Ventes'].var())  

median() - Médiane

# Médiane des ventes par catégorie
print("Médiane par catégorie:")  
print(df.groupby('Catégorie')['Ventes'].median())  

Agrégations multiples avec agg()

Une fonction sur plusieurs colonnes

df = pd.DataFrame({
    'Ville': ['Paris', 'Lyon', 'Paris', 'Lyon', 'Marseille'],
    'Produit': ['A', 'A', 'B', 'B', 'A'],
    'Ventes': [100, 150, 120, 180, 90],
    'Quantité': [5, 8, 6, 9, 4]
})

# Calculer la somme de plusieurs colonnes
resultat = df.groupby('Ville')[['Ventes', 'Quantité']].sum()  
print("Somme des ventes et quantités par ville:")  
print(resultat)  

Plusieurs fonctions sur une colonne

# Appliquer plusieurs fonctions d'agrégation
stats = df.groupby('Ville')['Ventes'].agg(['sum', 'mean', 'min', 'max', 'count'])  
print("Statistiques des ventes par ville:")  
print(stats)  

Fonctions différentes pour différentes colonnes

# Agrégations personnalisées par colonne
resultat = df.groupby('Ville').agg({
    'Ventes': ['sum', 'mean'],
    'Quantité': ['sum', 'max']
})
print("Agrégations personnalisées:")  
print(resultat)  

Nommer les colonnes agrégées

# Utiliser des noms personnalisés pour les colonnes
resultat = df.groupby('Ville').agg(
    Ventes_totales=('Ventes', 'sum'),
    Ventes_moyennes=('Ventes', 'mean'),
    Quantité_totale=('Quantité', 'sum'),
    Quantité_max=('Quantité', 'max')
)
print("Avec noms personnalisés:")  
print(resultat)  

Fonctions personnalisées

# Définir une fonction personnalisée
def etendue(serie):
    """Calcule l'étendue (max - min)"""
    return serie.max() - serie.min()

# Appliquer la fonction personnalisée
resultat = df.groupby('Ville')['Ventes'].agg(['mean', etendue])  
print("Avec fonction personnalisée:")  
print(resultat)  

# Fonction lambda
resultat = df.groupby('Ville')['Ventes'].agg(
    moyenne='mean',
    coefficient_variation=lambda x: x.std() / x.mean()
)
print("\nAvec lambda:")  
print(resultat)  

Grouper par plusieurs colonnes

GroupBy hiérarchique

df = pd.DataFrame({
    'Région': ['Nord', 'Nord', 'Sud', 'Sud', 'Nord', 'Sud'],
    'Ville': ['Paris', 'Lyon', 'Marseille', 'Toulouse', 'Paris', 'Marseille'],
    'Produit': ['A', 'A', 'A', 'B', 'B', 'B'],
    'Ventes': [100, 150, 90, 120, 110, 130]
})

print("DataFrame original:")  
print(df)  

# Grouper par Région et Ville
ventes_region_ville = df.groupby(['Région', 'Ville'])['Ventes'].sum()  
print("\nVentes par Région et Ville:")  
print(ventes_region_ville)  

# Avec plusieurs colonnes et agrégations
stats = df.groupby(['Région', 'Ville']).agg({
    'Ventes': ['sum', 'mean'],
    'Produit': 'count'
})
print("\nStatistiques détaillées:")  
print(stats)  

Réinitialiser l'index

# Le résultat a un MultiIndex, on peut le réinitialiser
ventes_df = df.groupby(['Région', 'Ville'])['Ventes'].sum().reset_index()  
print("Avec index réinitialisé:")  
print(ventes_df)  

Grouper et trier

# Grouper et trier par la valeur agrégée
ventes_triees = (df.groupby(['Région', 'Ville'])['Ventes']
                   .sum()
                   .sort_values(ascending=False)
                   .reset_index())
print("Trié par ventes décroissantes:")  
print(ventes_triees)  

Transform : Appliquer des opérations en gardant la forme

La méthode transform() applique une fonction et retourne un résultat de la même taille que l'entrée.

df = pd.DataFrame({
    'Ville': ['Paris', 'Lyon', 'Paris', 'Lyon', 'Paris'],
    'Ventes': [100, 150, 120, 180, 90]
})

print("DataFrame original:")  
print(df)  

# Calculer la moyenne par ville et l'ajouter comme colonne
df['Moyenne_ville'] = df.groupby('Ville')['Ventes'].transform('mean')  
print("\nAvec moyenne par ville:")  
print(df)  

# Normaliser par rapport à la moyenne du groupe
df['Ventes_normalisées'] = (df['Ventes'] - df['Moyenne_ville']) / df.groupby('Ville')['Ventes'].transform('std')  
print("\nAvec ventes normalisées:")  
print(df)  

# Fonction personnalisée avec transform
df['Rang_dans_ville'] = df.groupby('Ville')['Ventes'].transform(lambda x: x.rank(method='dense'))  
print("\nAvec rang dans chaque ville:")  
print(df)  

Filter : Filtrer des groupes entiers

La méthode filter() permet de conserver ou supprimer des groupes entiers selon une condition.

df = pd.DataFrame({
    'Ville': ['Paris', 'Lyon', 'Paris', 'Lyon', 'Marseille', 'Paris'],
    'Ventes': [100, 150, 120, 180, 50, 90]
})

print("DataFrame original:")  
print(df)  

# Garder seulement les villes avec des ventes totales > 200
villes_importantes = df.groupby('Ville').filter(lambda x: x['Ventes'].sum() > 200)  
print("\nVilles avec ventes totales > 200:")  
print(villes_importantes)  

# Garder les villes avec au moins 3 entrées
villes_frequentes = df.groupby('Ville').filter(lambda x: len(x) >= 3)  
print("\nVilles avec au moins 3 entrées:")  
print(villes_frequentes)  

Apply : Maximum de flexibilité

La méthode apply() est la plus flexible et permet d'appliquer n'importe quelle fonction à chaque groupe.

df = pd.DataFrame({
    'Catégorie': ['A', 'A', 'B', 'B', 'A', 'B'],
    'Valeur': [10, 15, 20, 25, 12, 22]
})

print("DataFrame original:")  
print(df)  

# Fonction simple
def calcul_range(groupe):
    return groupe['Valeur'].max() - groupe['Valeur'].min()

etendue = df.groupby('Catégorie').apply(calcul_range)  
print("\nÉtendue par catégorie:")  
print(etendue)  

# Retourner un DataFrame
def stats_groupe(groupe):
    return pd.Series({
        'min': groupe['Valeur'].min(),
        'max': groupe['Valeur'].max(),
        'etendue': groupe['Valeur'].max() - groupe['Valeur'].min()
    })

stats = df.groupby('Catégorie').apply(stats_groupe)  
print("\nStatistiques par catégorie:")  
print(stats)  

# Opération plus complexe : normalisation par groupe
def normaliser(groupe):
    groupe['Valeur_norm'] = (groupe['Valeur'] - groupe['Valeur'].mean()) / groupe['Valeur'].std()
    return groupe

df_norm = df.groupby('Catégorie').apply(normaliser)  
print("\nAvec valeurs normalisées par groupe:")  
print(df_norm)  

Opérations sur les index

Grouper par niveau d'index

# DataFrame avec MultiIndex
arrays = [
    ['A', 'A', 'B', 'B'],
    ['X', 'Y', 'X', 'Y']
]
index = pd.MultiIndex.from_arrays(arrays, names=['Groupe', 'Sous-groupe'])  
df = pd.DataFrame({'Valeur': [10, 20, 30, 40]}, index=index)  

print("DataFrame avec MultiIndex:")  
print(df)  

# Grouper par niveau d'index
par_groupe = df.groupby(level='Groupe')['Valeur'].sum()  
print("\nSomme par Groupe:")  
print(par_groupe)  

par_sous_groupe = df.groupby(level='Sous-groupe')['Valeur'].sum()  
print("\nSomme par Sous-groupe:")  
print(par_sous_groupe)  

Grouper par fonction sur l'index

df = pd.DataFrame({
    'Date': pd.date_range('2024-01-01', periods=10, freq='D'),
    'Ventes': [100, 120, 90, 110, 130, 95, 105, 115, 125, 100]
})
df.set_index('Date', inplace=True)

print("DataFrame avec dates:")  
print(df)  

# Grouper par mois
par_mois = df.groupby(df.index.month)['Ventes'].sum()  
print("\nVentes par mois:")  
print(par_mois)  

# Grouper par jour de la semaine
par_jour = df.groupby(df.index.dayofweek)['Ventes'].mean()  
print("\nVentes moyennes par jour de la semaine:")  
print(par_jour)  

Agrégations avec valeurs manquantes

df = pd.DataFrame({
    'Catégorie': ['A', 'A', 'B', 'B', 'A', 'B'],
    'Valeur': [10, np.nan, 20, 25, 15, np.nan]
})

print("DataFrame avec NaN:")  
print(df)  

# Les agrégations ignorent les NaN par défaut
print("\nMoyenne par catégorie (ignore NaN):")  
print(df.groupby('Catégorie')['Valeur'].mean())  

# Compter en excluant les NaN
print("\nCompte (sans NaN):")  
print(df.groupby('Catégorie')['Valeur'].count())  

# Compter tous les éléments (avec NaN)
print("\nCompte total (avec NaN):")  
print(df.groupby('Catégorie')['Valeur'].size())  

# Somme (NaN devient 0)
print("\nSomme par catégorie:")  
print(df.groupby('Catégorie')['Valeur'].sum())  

Exemples pratiques complets

Exemple 1 : Analyse de ventes par région

# Données de ventes
ventes = pd.DataFrame({
    'Date': pd.date_range('2024-01-01', periods=12, freq='MS'),
    'Région': ['Nord', 'Sud', 'Est', 'Ouest'] * 3,
    'Produit': ['A', 'A', 'B', 'B'] * 3,
    'Ventes': [1000, 1200, 800, 900, 1100, 1300, 850, 950, 1050, 1250, 900, 1000],
    'Quantité': [50, 60, 40, 45, 55, 65, 42, 48, 52, 62, 45, 50]
})

print("Données de ventes:")  
print(ventes.head())  

# 1. Ventes totales par région
print("\n--- Ventes totales par région ---")  
ventes_region = ventes.groupby('Région')['Ventes'].sum().sort_values(ascending=False)  
print(ventes_region)  

# 2. Statistiques par produit
print("\n--- Statistiques par produit ---")  
stats_produit = ventes.groupby('Produit').agg({  
    'Ventes': ['sum', 'mean', 'min', 'max'],
    'Quantité': ['sum', 'mean']
})
print(stats_produit)

# 3. Performance par région et produit
print("\n--- Performance par région et produit ---")  
perf = ventes.groupby(['Région', 'Produit']).agg(  
    Ventes_totales=('Ventes', 'sum'),
    Ventes_moyennes=('Ventes', 'mean'),
    Quantité_totale=('Quantité', 'sum')
).reset_index()
print(perf)

# 4. Trouver la meilleure région pour chaque produit
print("\n--- Meilleure région par produit ---")  
meilleures = (ventes.groupby(['Produit', 'Région'])['Ventes']  
              .sum()
              .reset_index()
              .sort_values('Ventes', ascending=False)
              .groupby('Produit')
              .first())
print(meilleures)

# 5. Pourcentage des ventes par région
print("\n--- Pourcentage des ventes par région ---")  
total_ventes = ventes['Ventes'].sum()  
pct_region = (ventes.groupby('Région')['Ventes'].sum() / total_ventes * 100).round(2)  
print(pct_region)  

Exemple 2 : Analyse de notes d'étudiants

# Données d'étudiants
notes = pd.DataFrame({
    'Étudiant': ['Alice', 'Bob', 'Charlie', 'Alice', 'Bob', 'Charlie'] * 2,
    'Matière': ['Math', 'Math', 'Math', 'Physique', 'Physique', 'Physique'] * 2,
    'Trimestre': ['T1', 'T1', 'T1', 'T1', 'T1', 'T1', 'T2', 'T2', 'T2', 'T2', 'T2', 'T2'],
    'Note': [15, 12, 18, 13, 14, 16, 16, 13, 17, 14, 15, 18]
})

print("Données de notes:")  
print(notes)  

# 1. Moyenne par étudiant
print("\n--- Moyenne générale par étudiant ---")  
moy_etudiant = notes.groupby('Étudiant')['Note'].mean().round(2)  
print(moy_etudiant)  

# 2. Moyenne par matière
print("\n--- Moyenne par matière ---")  
moy_matiere = notes.groupby('Matière')['Note'].mean().round(2)  
print(moy_matiere)  

# 3. Évolution par étudiant et matière
print("\n--- Évolution par étudiant et matière ---")  
evolution = notes.groupby(['Étudiant', 'Matière', 'Trimestre'])['Note'].mean().unstack()  
print(evolution)  

# 4. Meilleure et moins bonne note par étudiant
print("\n--- Min et Max par étudiant ---")  
minmax = notes.groupby('Étudiant')['Note'].agg(['min', 'max', 'mean'])  
print(minmax)  

# 5. Classement des étudiants
print("\n--- Classement des étudiants ---")  
classement = (notes.groupby('Étudiant')['Note']  
              .mean()
              .sort_values(ascending=False)
              .reset_index()
              .rename(columns={'Note': 'Moyenne'}))
classement['Rang'] = range(1, len(classement) + 1)  
print(classement)  

# 6. Progression entre T1 et T2
print("\n--- Progression T1 -> T2 ---")  
notes_pivot = notes.pivot_table(values='Note',  
                                 index='Étudiant',
                                 columns='Trimestre',
                                 aggfunc='mean')
notes_pivot['Progression'] = notes_pivot['T2'] - notes_pivot['T1']  
print(notes_pivot)  

Exemple 3 : Analyse de données e-commerce

# Données de commandes
commandes = pd.DataFrame({
    'Date': pd.date_range('2024-01-01', periods=20, freq='D'),
    'Client': ['Client_A', 'Client_B', 'Client_A', 'Client_C'] * 5,
    'Catégorie': ['Électronique', 'Vêtements', 'Électronique', 'Livres'] * 5,
    'Montant': np.random.randint(50, 500, 20),
    'Produits': np.random.randint(1, 10, 20)
})

print("Données de commandes:")  
print(commandes.head(10))  

# 1. Chiffre d'affaires par client
print("\n--- CA par client ---")  
ca_client = (commandes.groupby('Client')['Montant']  
             .sum()
             .sort_values(ascending=False)
             .reset_index()
             .rename(columns={'Montant': 'CA_total'}))
print(ca_client)

# 2. Panier moyen par catégorie
print("\n--- Panier moyen par catégorie ---")  
panier_moy = commandes.groupby('Catégorie').agg(  
    Montant_moyen=('Montant', 'mean'),
    Montant_total=('Montant', 'sum'),
    Nb_commandes=('Montant', 'count')
).round(2)
print(panier_moy)

# 3. Fréquence d'achat par client
print("\n--- Fréquence d'achat ---")  
freq = commandes.groupby('Client').agg(  
    Nb_commandes=('Date', 'count'),
    Première_commande=('Date', 'min'),
    Dernière_commande=('Date', 'max')
)
freq['Jours_entre_commandes'] = (freq['Dernière_commande'] - freq['Première_commande']).dt.days / freq['Nb_commandes']  
print(freq)  

# 4. Top 3 des catégories par client
print("\n--- Top catégories par client ---")  
top_cat = (commandes.groupby(['Client', 'Catégorie'])['Montant']  
           .sum()
           .reset_index()
           .sort_values(['Client', 'Montant'], ascending=[True, False])
           .groupby('Client')
           .head(2))
print(top_cat)

# 5. Analyse temporelle : CA par semaine
commandes['Semaine'] = commandes['Date'].dt.isocalendar().week  
print("\n--- CA par semaine ---")  
ca_semaine = commandes.groupby('Semaine')['Montant'].sum()  
print(ca_semaine)  

Exemple 4 : Analyse RH - Salaires par département

# Données d'employés
employes = pd.DataFrame({
    'Nom': ['Alice', 'Bob', 'Charlie', 'David', 'Eve', 'Frank', 'Grace', 'Henry'],
    'Département': ['IT', 'Ventes', 'IT', 'RH', 'Ventes', 'IT', 'RH', 'Ventes'],
    'Poste': ['Dev', 'Manager', 'Dev', 'Recrut', 'Vendeur', 'Manager', 'Recrut', 'Vendeur'],
    'Salaire': [45000, 55000, 48000, 40000, 38000, 65000, 42000, 40000],
    'Ancienneté': [2, 5, 3, 4, 1, 8, 6, 2]
})

print("Données employés:")  
print(employes)  

# 1. Salaire moyen par département
print("\n--- Salaire moyen par département ---")  
sal_dept = employes.groupby('Département')['Salaire'].mean().round(2)  
print(sal_dept)  

# 2. Statistiques complètes par département
print("\n--- Statistiques par département ---")  
stats_dept = employes.groupby('Département').agg({  
    'Salaire': ['mean', 'min', 'max', 'std'],
    'Ancienneté': 'mean',
    'Nom': 'count'
}).round(2)
stats_dept.columns = ['Sal_moyen', 'Sal_min', 'Sal_max', 'Sal_std', 'Anc_moy', 'Effectif']  
print(stats_dept)  

# 3. Masse salariale par département
print("\n--- Masse salariale ---")  
masse_sal = employes.groupby('Département').agg(  
    Masse_salariale=('Salaire', 'sum'),
    Effectif=('Nom', 'count')
)
masse_sal['Salaire_moyen'] = masse_sal['Masse_salariale'] / masse_sal['Effectif']  
print(masse_sal)  

# 4. Salaire moyen par poste et département
print("\n--- Salaire par poste et département ---")  
sal_poste = employes.groupby(['Département', 'Poste'])['Salaire'].mean().unstack()  
print(sal_poste)  

# 5. Identifier les salaires au-dessus de la moyenne du département
print("\n--- Employés au-dessus de la moyenne ---")  
employes['Salaire_moy_dept'] = employes.groupby('Département')['Salaire'].transform('mean')  
employes['Au_dessus_moy'] = employes['Salaire'] > employes['Salaire_moy_dept']  
print(employes[['Nom', 'Département', 'Salaire', 'Salaire_moy_dept', 'Au_dessus_moy']])  

# 6. Top 2 salaires par département
print("\n--- Top 2 salaires par département ---")  
top_salaires = (employes.sort_values('Salaire', ascending=False)  
                .groupby('Département')
                .head(2)[['Nom', 'Département', 'Salaire']])
print(top_salaires)

Exemple 5 : Analyse de données de capteurs IoT

# Données de capteurs
np.random.seed(42)  
dates = pd.date_range('2024-01-01', periods=24, freq='h')  
capteurs = pd.DataFrame({  
    'DateTime': list(dates) * 2,
    'Capteur': ['Temp_1'] * 24 + ['Temp_2'] * 24,
    'Température': np.random.normal(20, 3, 48),
    'Humidité': np.random.normal(60, 10, 48)
})

print("Données capteurs:")  
print(capteurs.head(10))  

# 1. Moyenne quotidienne par capteur
capteurs['Date'] = capteurs['DateTime'].dt.date  
print("\n--- Moyenne quotidienne ---")  
moy_jour = capteurs.groupby(['Date', 'Capteur']).agg({  
    'Température': 'mean',
    'Humidité': 'mean'
}).round(2)
print(moy_jour.head(10))

# 2. Min, Max, Moyenne par capteur
print("\n--- Statistiques par capteur ---")  
stats = capteurs.groupby('Capteur').agg({  
    'Température': ['min', 'max', 'mean', 'std'],
    'Humidité': ['min', 'max', 'mean', 'std']
}).round(2)
print(stats)

# 3. Heures avec température > 22°C
print("\n--- Heures chaudes par capteur ---")  
capteurs['Chaud'] = capteurs['Température'] > 22  
heures_chaudes = capteurs.groupby('Capteur')['Chaud'].sum()  
print(heures_chaudes)  

# 4. Plage horaire : moyenne par heure de la journée
capteurs['Heure'] = capteurs['DateTime'].dt.hour  
print("\n--- Température moyenne par heure ---")  
temp_heure = capteurs.groupby('Heure')['Température'].mean().round(2)  
print(temp_heure)  

# 5. Écart entre les deux capteurs
print("\n--- Écart entre capteurs ---")  
temp_pivot = capteurs.pivot_table(values='Température',  
                                   index='DateTime',
                                   columns='Capteur')
temp_pivot['Écart'] = abs(temp_pivot['Temp_1'] - temp_pivot['Temp_2'])  
print("\nÉcart moyen:", temp_pivot['Écart'].mean().round(2))  
print("Écart maximum:", temp_pivot['Écart'].max().round(2))  

Techniques avancées

Grouper avec des bins (intervalles)

# Créer des groupes d'âge
df = pd.DataFrame({
    'Nom': ['Alice', 'Bob', 'Charlie', 'David', 'Eve', 'Frank'],
    'Âge': [23, 35, 28, 45, 31, 52],
    'Salaire': [35000, 45000, 40000, 55000, 42000, 60000]
})

print("DataFrame original:")  
print(df)  

# Créer des tranches d'âge
df['Tranche_âge'] = pd.cut(df['Âge'], bins=[20, 30, 40, 60], labels=['20-30', '30-40', '40-60'])  
print("\nAvec tranches d'âge:")  
print(df)  

# Analyser par tranche
print("\nSalaire moyen par tranche d'âge:")  
print(df.groupby('Tranche_âge')['Salaire'].mean())  

Grouper avec des conditions personnalisées

# Grouper selon une condition
df = pd.DataFrame({
    'Produit': ['A', 'B', 'C', 'D', 'E'],
    'Prix': [10, 25, 15, 45, 20]
})

# Créer des groupes personnalisés
def categoriser_prix(prix):
    if prix < 20:
        return 'Économique'
    elif prix < 40:
        return 'Standard'
    else:
        return 'Premium'

df['Catégorie'] = df['Prix'].apply(categoriser_prix)  
print("Produits catégorisés:")  
print(df)  

print("\nNombre de produits par catégorie:")  
print(df.groupby('Catégorie').size())  

Rolling et groupby

# Moyenne mobile par groupe
df = pd.DataFrame({
    'Date': pd.date_range('2024-01-01', periods=10),
    'Ville': ['Paris', 'Lyon'] * 5,
    'Ventes': [100, 120, 110, 130, 105, 125, 115, 135, 120, 140]
})

print("DataFrame original:")  
print(df)  

# Moyenne mobile sur 3 jours par ville
df = df.sort_values(['Ville', 'Date'])  
df['Moyenne_mobile'] = df.groupby('Ville')['Ventes'].transform(lambda x: x.rolling(3, min_periods=1).mean())  
print("\nAvec moyenne mobile:")  
print(df)  

Bonnes pratiques

1. Choisir la bonne méthode d'agrégation

# ✅ Bon : utiliser agg() pour plusieurs agrégations
stats = df.groupby('Catégorie').agg({
    'Ventes': ['sum', 'mean'],
    'Quantité': 'sum'
})

# ❌ Moins efficace : chaîner plusieurs opérations
# somme = df.groupby('Catégorie')['Ventes'].sum()
# moyenne = df.groupby('Catégorie')['Ventes'].mean()

2. Utiliser reset_index() pour plus de clarté

# ✅ Bon : résultat sous forme de DataFrame
resultat = df.groupby('Ville')['Ventes'].sum().reset_index()
# Plus facile à manipuler et à visualiser

# ❌ Moins pratique : Series avec index
# resultat = df.groupby('Ville')['Ventes'].sum()

3. Nommer les colonnes agrégées

# ✅ Bon : noms explicites
resultat = df.groupby('Ville').agg(
    Total_ventes=('Ventes', 'sum'),
    Ventes_moyennes=('Ventes', 'mean'),
    Nb_transactions=('Ventes', 'count')
)

# ❌ Moins clair
# resultat = df.groupby('Ville')['Ventes'].agg(['sum', 'mean', 'count'])

4. Vérifier les groupes avant l'agrégation

# ✅ Bon : vérifier les groupes
print("Nombre de groupes:", df.groupby('Catégorie').ngroups)  
print("Groupes:", list(df.groupby('Catégorie').groups.keys()))  
# Puis agréger
resultat = df.groupby('Catégorie')['Ventes'].sum()

5. Gérer les valeurs manquantes

# ✅ Bon : gérer explicitement les NaN
# Option 1 : Nettoyer avant groupby
df_clean = df.dropna(subset=['Catégorie', 'Ventes'])  
resultat = df_clean.groupby('Catégorie')['Ventes'].sum()  

# Option 2 : Inclure les NaN comme groupe
resultat = df.groupby('Catégorie', dropna=False)['Ventes'].sum()

Résumé

GroupBy et les agrégations sont des outils essentiels pour l'analyse de données avec Pandas. Voici les points clés à retenir :

Concept de base :

  • Split-Apply-Combine : diviser, appliquer, combiner
  • groupby() crée des groupes selon un ou plusieurs critères

Fonctions d'agrégation principales :

  • sum(), mean(), count(), min(), max()
  • std(), var(), median()
  • agg() pour agrégations multiples et personnalisées

Méthodes avancées :

  • transform() : opérations en gardant la forme originale
  • filter() : filtrer des groupes entiers
  • apply() : maximum de flexibilité

Groupements :

  • Simple : une colonne
  • Multiple : plusieurs colonnes (MultiIndex)
  • Par index, par fonction, par bins

Bonnes pratiques :

  1. Utiliser agg() pour plusieurs agrégations
  2. Nommer les colonnes agrégées pour plus de clarté
  3. Utiliser reset_index() pour des DataFrames plus maniables
  4. Vérifier les groupes avant d'agréger
  5. Gérer explicitement les valeurs manquantes

GroupBy est particulièrement utile pour :

  • Analyses comparatives entre groupes
  • Calculs de statistiques par catégorie
  • Transformations par groupe
  • Filtrage de groupes selon des critères

La maîtrise de GroupBy vous permettra d'effectuer des analyses de données complexes de manière efficace et élégante.

⏭️ Visualisation avec Matplotlib et Plotly