M55 - Data Science & Scientific Computing 5
  1. Exercices
  2. 9  🛒 Ventes et ☔ pluie
  • 1  M55 - Data Science and Scientific Computing 5
  • Introduction
    • 2  Syllabus

  • Cours
    • 3  Introduction
    • 4  Series
    • 5  Data Frames
    • 6  🚱 Une analyse complĂšte : le Titanic

  • Exercices
    • 7  PremiĂšres manipulations
    • 8  📊 Notes L3 Math
    • 9  🛒 Ventes et ☔ pluie
    • 10  đŸ˜ïž Population France
    • 11  🏭 Émissions de CO₂ des communes
    • 12  đŸ“ș Netflix User
    • 13  đŸ—łïž PrĂ©sidentielles USA 2016

  • Downloads
    • 14  Fichiers sources

Table des matiĂšres

  • 9.1 Exercice
    • 9.1.1 Partie A : Ventes de deux magasins
    • 9.1.2 Partie B : PluviomĂ©trie de trois villes
  • 10 Partie A : Ventes de deux magasins
    • 10.1 1. Charger le fichier et vĂ©rifier les types
    • 10.2 2. Ajouter la colonne montant
    • 10.3 3. Quel magasin rĂ©alise le plus gros chiffre d’affaires ?
    • 10.4 4. Quel produit porte chaque magasin ?
    • 10.5 5. Évolution trimestrielle
    • 10.6 6. Et en nombre d’articles ?
  • 11 Partie B : PluviomĂ©trie de trois villes
    • 11.1 1. Charger le fichier
    • 11.2 2. et 3. Deux classements
    • 11.3 4. Comparaison des deux classements
    • 11.4 5. L’intensitĂ© des pluies
    • 11.5 6. Tableau croisĂ© station × mois
    • 11.6 7. Le mois le plus arrosĂ© de chaque ville
  • 12 Conclusion
  1. Exercices
  2. 9  🛒 Ventes et ☔ pluie

9  🛒 Ventes et ☔ pluie : agrĂ©ger, c’est choisir

Auteur·rice

Gloria FACCANONI

Date de publication

30 octobre 2026

Les chapitres prĂ©cĂ©dents ont prĂ©sentĂ© la mĂ©canique de l’agrĂ©gation : groupby, pivot_table, crosstab. Cet exercice porte sur autre chose : la lecture du rĂ©sultat.

Vous allez constater, sur deux jeux de donnĂ©es diffĂ©rents, qu’une mĂȘme question posĂ©e Ă  des donnĂ©es identiques reçoit des rĂ©ponses opposĂ©es selon la grandeur que l’on choisit d’agrĂ©ger. AgrĂ©ger n’est pas une opĂ©ration neutre : c’est dĂ©jĂ  une dĂ©cision d’analyse.

9.1 Exercice

9.1.1 Partie A : Ventes de deux magasins

Le fichier ventes.csv donne, pour deux magasins et quatre trimestres, les quantités vendues de trois produits et leur prix unitaire.

  1. Charger le fichier et vérifier le type de chaque colonne avant toute chose.
  2. Ajouter une colonne montant, égale à la quantité multipliée par le prix unitaire.
  3. Quel magasin rĂ©alise le plus gros chiffre d’affaires ?
  4. Croiser magasin et produit : quel produit porte le chiffre d’affaires de chaque magasin ?
  5. Tracer l’évolution du chiffre d’affaires par trimestre pour chaque magasin.
  6. Quel magasin vend le plus d’articles ? Comparer avec la rĂ©ponse de la question 3 et commenter.

9.1.2 Partie B : Pluviométrie de trois villes

Le fichier pluviometrie.csv donne, pour Brest, Paris et Nice, la hauteur de pluie et le nombre de jours de pluie de chaque mois.

  1. Charger le fichier.
  2. Classer les trois villes par cumul annuel de pluie.
  3. Classer les trois villes par nombre de jours de pluie.
  4. Comparer les deux classements. Que peut-on en conclure ?
  5. Calculer la hauteur moyenne de pluie par jour de pluie dans chaque ville.
  6. Construire un tableau croisĂ© station × mois. Attention Ă  l’ordre des mois.
  7. Pour chaque ville, déterminer le mois le plus arrosé.

Sur les donnĂ©es. Les ventes sont fictives. Pour la pluviomĂ©trie, les totaux annuels sont proches des normales publiĂ©es par MĂ©tĂ©o-France (Brest ≈ 1200 mm, Nice ≈ 800 mm, Paris ≈ 640 mm), mais la rĂ©partition mensuelle a Ă©tĂ© reconstruite pour les besoins de l’exercice : ne citez pas ces chiffres comme des relevĂ©s officiels.

import pandas as pd
import numpy as np

import matplotlib.pyplot as plt
import seaborn as sns

sns.set_theme(context="notebook", style="whitegrid", palette="pastel")
plt.rcParams['figure.figsize'] = (12, 5)

from IPython.display import display, Markdown

10 Partie A : Ventes de deux magasins

10.1 1. Charger le fichier et vérifier les types

Premier rĂ©flexe, avant mĂȘme de lire les donnĂ©es : regarder Ă  quoi ressemble le fichier. Ici le sĂ©parateur est ; et le marqueur dĂ©cimal est la virgule.

display(Markdown("```\n" + open('ventes.csv', encoding='utf-8').read()[:220] + "\n```"))
magasin;trimestre;produit;quantite;prix_unitaire
Nord;T1;stylo;400;1,50
Nord;T1;cahier;120;3,20
Nord;T1;classeur;30;8,90
Nord;T2;stylo;420;1,50
Nord;T2;cahier;130;3,20
Nord;T2;classeur;25;8,90
Nord;T3;stylo;380;1,50
Nord
ventes = pd.read_csv('ventes.csv', sep=';', decimal=',')

display(ventes.head())
display(ventes.dtypes)
magasin trimestre produit quantite prix_unitaire
0 Nord T1 stylo 400 1.5
1 Nord T1 cahier 120 3.2
2 Nord T1 classeur 30 8.9
3 Nord T2 stylo 420 1.5
4 Nord T2 cahier 130 3.2
magasin           object
trimestre         object
produit           object
quantite           int64
prix_unitaire    float64
dtype: object

prix_unitaire est bien en float64. Si nous avions oubliĂ© decimal=',', la colonne aurait Ă©tĂ© lue comme du texte et toute la suite aurait Ă©tĂ© fausse : c’est la vĂ©rification Ă  ne jamais sauter.

10.2 2. Ajouter la colonne montant

ventes['montant'] = ventes['quantite'] * ventes['prix_unitaire']
ventes.head()
magasin trimestre produit quantite prix_unitaire montant
0 Nord T1 stylo 400 1.5 600.0
1 Nord T1 cahier 120 3.2 384.0
2 Nord T1 classeur 30 8.9 267.0
3 Nord T2 stylo 420 1.5 630.0
4 Nord T2 cahier 130 3.2 416.0

10.3 3. Quel magasin rĂ©alise le plus gros chiffre d’affaires ?

ca = ventes.groupby('magasin')['montant'].sum()
display(ca)
display(Markdown(f"Le magasin {ca.idxmax()} rĂ©alise le plus gros chiffre d'affaires : {ca.max():.2f} €"))
magasin
Nord    5264.0
Sud     8920.5
Name: montant, dtype: float64

Le magasin Sud rĂ©alise le plus gros chiffre d’affaires : 8920.50 €

10.4 4. Quel produit porte chaque magasin ?

tableau = ventes.pivot_table(index='magasin', columns='produit',
                            values='montant', aggfunc='sum', margins=True)
tableau.round(2)
produit cahier classeur stylo All
magasin
Nord 1632.0 1157.0 2475.0 5264.0
Sud 1360.0 6630.5 930.0 8920.5
All 2992.0 7787.5 3405.0 14184.5

Plus lisible en pourcentage de chaque ligne :

part = ventes.pivot_table(index='magasin', columns='produit', values='montant', aggfunc='sum')
(part.div(part.sum(axis=1), axis=0) * 100).round(1)
produit cahier classeur stylo
magasin
Nord 31.0 22.0 47.0
Sud 15.2 74.3 10.4

La lecture est immĂ©diate : le Sud rĂ©alise l’essentiel de son chiffre d’affaires sur les classeurs, le Nord est beaucoup plus Ă©quilibrĂ©, avec une part importante de stylos.

10.5 5. Évolution trimestrielle

evolution = ventes.pivot_table(index='trimestre', columns='magasin',
                               values='montant', aggfunc='sum')
display(evolution.round(2))

evolution.plot(marker='o')
plt.ylabel("Chiffre d'affaires (€)")
plt.title("Chiffre d'affaires trimestriel par magasin")
plt.show()
magasin Nord Sud
trimestre
T1 1251.0 2147.0
T2 1268.5 2235.0
T3 1233.5 2119.5
T4 1511.0 2419.0

10.6 6. Et en nombre d’articles ?

Jusqu’ici nous avons agrĂ©gĂ© les euros. AgrĂ©geons maintenant les articles :

bilan = ventes.groupby('magasin').agg(
    articles          = ('quantite', 'sum'),
    chiffre_affaires  = ('montant',  'sum')
)
bilan['panier_moyen'] = (bilan['chiffre_affaires'] / bilan['articles']).round(2)
bilan
articles chiffre_affaires panier_moyen
magasin
Nord 2290 5264.0 2.30
Sud 1790 8920.5 4.98
display(Markdown(f"Plus d'articles vendus     : {bilan['articles'].idxmax()}"))
display(Markdown(f"Plus gros chiffre d'affaires : {bilan['chiffre_affaires'].idxmax()}"))

Plus d’articles vendus : Nord

Plus gros chiffre d’affaires : Sud

Les deux classements s’opposent. Le magasin Nord vend 2 290 articles contre 1 790 pour le Sud, mais le Sud rĂ©alise un chiffre d’affaires de 8 920,50 € contre 5 264 € pour le Nord.

Il n’y a aucune contradiction : le Nord Ă©coule des stylos Ă  1,50 €, le Sud des classeurs Ă  8,90 €. La derniĂšre colonne le rĂ©sume : le prix moyen d’un article vendu est prĂšs de deux fois plus Ă©levĂ© au Sud.

La question « quel est le meilleur magasin ? » n’a donc pas de rĂ©ponse tant qu’on n’a pas dit ce qu’on mesure. C’est Ă  l’analyste de le prĂ©ciser, pas Ă  groupby.

11 Partie B : Pluviométrie de trois villes

11.1 1. Charger le fichier

pluie = pd.read_csv('pluviometrie.csv', sep=';')

display(pluie.head())
display(pluie.shape)
station mois pluie_mm jours_pluie
0 Brest janvier 150 17
1 Brest février 110 14
2 Brest mars 100 14
3 Brest avril 85 13
4 Brest mai 75 12
(36, 4)

11.2 2. et 3. Deux classements

bilan_villes = pluie.groupby('station').agg(
    cumul_mm = ('pluie_mm',    'sum'),
    jours    = ('jours_pluie', 'sum')
)
bilan_villes
cumul_mm jours
station
Brest 1225 162
Nice 797 63
Paris 655 111
display(Markdown("**Classement par hauteur de pluie :** "
                 + " > ".join(bilan_villes['cumul_mm'].sort_values(ascending=False).index)))
display(Markdown("**Classement par jours de pluie :** "
                 + " > ".join(bilan_villes['jours'].sort_values(ascending=False).index)))

Classement par hauteur de pluie : Brest > Nice > Paris

Classement par jours de pluie : Brest > Paris > Nice

11.3 4. Comparaison des deux classements

Paris et Nice échangent leur place.

  • En hauteur de pluie, il tombe nettement plus d’eau Ă  Nice qu’à Paris : environ 800 mm contre 655 mm.
  • En nombre de jours, c’est l’inverse : il pleut Ă  Paris environ 111 jours par an, contre 63 Ă  Nice.

« Il pleut plus Ă  Paris qu’à Nice » est donc vrai et faux Ă  la fois, selon la grandeur choisie. Les deux affirmations dĂ©crivent la mĂȘme rĂ©alitĂ© : Ă  Nice il pleut rarement, mais fort ; Ă  Paris il pleut souvent, mais peu.

Brest, elle, est premiĂšre dans les deux classements : il n’y a pas toujours d’ambiguĂŻtĂ©, mais il faut toujours vĂ©rifier.

11.4 5. L’intensitĂ© des pluies

Le rapport des deux grandeurs donne directement la hauteur moyenne tombée un jour de pluie :

bilan_villes['mm_par_jour_de_pluie'] = (bilan_villes['cumul_mm'] / bilan_villes['jours']).round(1)
bilan_villes.sort_values('mm_par_jour_de_pluie', ascending=False)
cumul_mm jours mm_par_jour_de_pluie
station
Nice 797 63 12.7
Brest 1225 162 7.6
Paris 655 111 5.9

Une pluie niçoise apporte en moyenne plus du double d’une pluie parisienne. C’est cette troisiĂšme grandeur, construite Ă  partir des deux premiĂšres, qui rĂ©concilie les classements et explique le phĂ©nomĂšne.

11.5 6. Tableau croisĂ© station × mois

PremiĂšre tentative, et premier piĂšge :

pluie.pivot_table(index='station', columns='mois', values='pluie_mm')
mois août avril décembre février janvier juillet juin mai mars novembre octobre septembre
station
Brest 55.0 85.0 165.0 110.0 150.0 50.0 55.0 75.0 100.0 155.0 140.0 85.0
Nice 20.0 70.0 95.0 45.0 70.0 12.0 35.0 45.0 40.0 140.0 150.0 75.0
Paris 60.0 52.0 58.0 45.0 50.0 60.0 55.0 65.0 48.0 52.0 62.0 48.0

Les mois sont rangĂ©s par ordre alphabĂ©tique : aoĂ»t, avril, dĂ©cembre
 Pandas ne peut pas deviner qu’il s’agit d’un calendrier. Il faut le lui dire, en dĂ©clarant la colonne comme une variable catĂ©gorielle ordonnĂ©e :

calendrier = ["janvier","février","mars","avril","mai","juin",
              "juillet","août","septembre","octobre","novembre","décembre"]

pluie['mois'] = pd.Categorical(pluie['mois'], categories=calendrier, ordered=True)

croise = pluie.pivot_table(index='station', columns='mois', values='pluie_mm', observed=True)
croise
mois janvier février mars avril mai juin juillet août septembre octobre novembre décembre
station
Brest 150.0 110.0 100.0 85.0 75.0 55.0 50.0 55.0 85.0 140.0 155.0 165.0
Nice 70.0 45.0 40.0 70.0 45.0 35.0 12.0 20.0 75.0 150.0 140.0 95.0
Paris 50.0 45.0 48.0 52.0 65.0 55.0 60.0 60.0 48.0 62.0 52.0 58.0
sns.heatmap(croise, annot=True, fmt='.0f', cmap='Blues', cbar_kws={'label': 'mm'})
plt.title("Hauteur de pluie mensuelle (mm)")
plt.ylabel("")
plt.show()

La carte de chaleur rend les trois rĂ©gimes immĂ©diatement visibles : Brest arrosĂ©e toute l’annĂ©e avec un creux estival, Paris remarquablement rĂ©guliĂšre, et Nice avec un Ă©tĂ© quasi sec suivi d’un automne trĂšs violent.

11.6 7. Le mois le plus arrosé de chaque ville

croise.idxmax(axis=1).to_frame('mois le plus arrosé')
mois le plus arrosé
station
Brest décembre
Nice octobre
Paris mai

12 Conclusion

Ce que cet exercice voulait montrer

Dans les deux cas, le code Ă©tait trivial : un groupby et une somme. C’est l’interprĂ©tation qui demandait du travail.

  • Partie A : « quel est le meilleur magasin ? » dĂ©pend de ce qu’on somme, les euros ou les articles.
  • Partie B : « oĂč pleut-il le plus ? » dĂ©pend de ce qu’on somme, les millimĂštres ou les jours.

Retenez le rĂ©flexe : avant d’agrĂ©ger, demandez-vous quelle question vous posez rĂ©ellement, et aprĂšs avoir agrĂ©gĂ©, demandez-vous si une autre grandeur donnerait une autre rĂ©ponse. C’est prĂ©cisĂ©ment ce qu’on attend de vous Ă  l’examen : non pas produire un tableau, mais dire ce qu’il signifie.

8  📊 Notes L3 Math
10  đŸ˜ïž Population France

© Copyright 2026, Gloria Faccanoni

 

This page is built with ❀ and Quarto.