3h30M12

📈 Module 12 — Visualisation avec R & Python

Acte IV — R & Python pour l'analyse de données
Niveau : Débutant · Durée estimée : 3h30



🎯 Objectifs du module

À la fin de ce module, tu sauras :

  • Choisir le bon type de graphique selon la question posée
  • Créer des graphiques en Python avec matplotlib et seaborn
  • Créer des graphiques en R avec ggplot2
  • Personnaliser titres, couleurs, étiquettes
  • Sauvegarder un graphique en image (PNG, PDF)
  • Produire un mini-rapport visuel sur les données RH

🌍 Mise en contexte — Bamba & Associés, Abidjan

M. Bamba a reçu le rapport statistique d'Ama. Il est satisfait des chiffres, mais il veut aller plus loin :

"Ama, les tableaux c'est bien, mais moi je dois présenter ça au Conseil d'Administration. J'ai besoin de graphiques clairs. Un graphique vaut mille chiffres."

C'est le rôle de la visualisation de données : transformer des chiffres en images qui racontent une histoire.



1. Quel graphique pour quelle question ?

Le choix du graphique dépend de ce que tu veux montrer :

Question Type de graphique Exemple
Distribution d'une variable numérique Histogramme Distribution des salaires
Comparer des groupes (valeurs) Barres Salaire moyen par département
Comparer des groupes (dispersion) Boîte à moustaches Salaires CDI vs CDD
Répartition en % Camembert / Donut % de CDI, CDD, Stage
Relation entre 2 variables numériques Nuage de points Ancienneté vs Salaire
Évolution dans le temps Courbe (ligne) CA mensuel
Plusieurs variables en une vue Pairplot / Heatmap Corrélations globales

Les erreurs classiques à éviter

Erreur Pourquoi c'est un problème
Camembert avec 8 tranches Illisible — utiliser un barplot à la place
Axe Y qui ne commence pas à 0 Exagère visuellement les différences
Couleurs sans signification Distrait du message principal
Titre absent Le lecteur ne sait pas quoi regarder
Trop de graphiques sur une slide Un graphique = un message


2. Préparer les données

On reprend le dataset RH Bamba & Associés du module 11 (15 employés).

En Python

pythonimport pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# Style global seaborn
sns.set_theme(style="whitegrid", palette="Blues_d")

data = {
    "nom": ["Kouassi Ama", "Kouamé Jean", "Traoré Seydou", "Diabaté Fatou",
            "Bamba Koné", "Ouédraogo Moussa", "Soro Aminata", "Koné Ibrahim",
            "Yao Adjoua", "Diallo Mamadou", "Coulibaly Assata", "N'Guessan Éric",
            "Touré Mariam", "Keita Souleymane", "Fofana Raïssa"],
    "departement": ["RH", "Finance", "Commercial", "RH", "Direction",
                    "IT", "Commercial", "Finance", "IT", "Commercial",
                    "RH", "Finance", "IT", "Commercial", "Direction"],
    "contrat": ["CDI", "CDI", "CDI", "CDD", "CDI", "CDI", "Stage", "CDI",
                "CDI", "CDD", "CDI", "CDI", "Stage", "CDI", "CDI"],
    "salaire": [450000, 620000, 380000, 310000, 1800000, 580000, 180000,
                540000, 610000, 340000, 420000, 590000, 200000, 360000, 950000],
    "anciennete": [5, 8, 3, 1, 12, 6, 0, 7, 9, 2, 4, 7, 0, 3, 10]
}

df = pd.DataFrame(data)

En R

rlibrary(ggplot2)
library(dplyr)

df <- data.frame(
  nom         = c("Kouassi Ama", "Kouamé Jean", "Traoré Seydou", "Diabaté Fatou",
                  "Bamba Koné", "Ouédraogo Moussa", "Soro Aminata", "Koné Ibrahim",
                  "Yao Adjoua", "Diallo Mamadou", "Coulibaly Assata", "N'Guessan Éric",
                  "Touré Mariam", "Keita Souleymane", "Fofana Raïssa"),
  departement = c("RH", "Finance", "Commercial", "RH", "Direction",
                  "IT", "Commercial", "Finance", "IT", "Commercial",
                  "RH", "Finance", "IT", "Commercial", "Direction"),
  contrat     = c("CDI", "CDI", "CDI", "CDD", "CDI", "CDI", "Stage", "CDI",
                  "CDI", "CDD", "CDI", "CDI", "Stage", "CDI", "CDI"),
  salaire     = c(450000, 620000, 380000, 310000, 1800000, 580000, 180000,
                  540000, 610000, 340000, 420000, 590000, 200000, 360000, 950000),
  anciennete  = c(5, 8, 3, 1, 12, 6, 0, 7, 9, 2, 4, 7, 0, 3, 10)
)


3. Histogramme — Distribution des salaires

L'histogramme montre comment les valeurs se répartissent : où se concentrent les salaires, y a-t-il des valeurs extrêmes ?

Python — matplotlib

pythonplt.figure(figsize=(9, 5))
plt.hist(df['salaire'], bins=6, color='#2196F3', edgecolor='white')
plt.title('Distribution des salaires — Bamba & Associés', fontsize=14, fontweight='bold')
plt.xlabel('Salaire (FCFA)')
plt.ylabel('Nombre d\'employés')
plt.axvline(df['salaire'].mean(),   color='red',    linestyle='--', label=f"Moyenne : {df['salaire'].mean():,.0f}")
plt.axvline(df['salaire'].median(), color='orange', linestyle='--', label=f"Médiane : {df['salaire'].median():,.0f}")
plt.legend()
plt.tight_layout()
plt.savefig('histogramme_salaires.png', dpi=150)
plt.show()

Python — seaborn (plus joli, même résultat)

pythonplt.figure(figsize=(9, 5))
sns.histplot(df['salaire'], bins=6, kde=True, color='#2196F3')
plt.title('Distribution des salaires — Bamba & Associés')
plt.xlabel('Salaire (FCFA)')
plt.ylabel('Effectif')
plt.show()
# kde=True ajoute une courbe de densité lissée par-dessus

R — ggplot2

rggplot(df, aes(x = salaire)) +
  geom_histogram(bins = 6, fill = "#2196F3", color = "white") +
  geom_vline(aes(xintercept = mean(salaire)), color = "red",    linetype = "dashed") +
  geom_vline(aes(xintercept = median(salaire)), color = "orange", linetype = "dashed") +
  labs(
    title    = "Distribution des salaires — Bamba & Associés",
    subtitle = "Ligne rouge = moyenne | Ligne orange = médiane",
    x        = "Salaire (FCFA)",
    y        = "Nombre d'employés"
  ) +
  theme_minimal()

📌 Lecture du graphique : la majorité des salaires se concentrent entre 300k et 650k FCFA. Le salaire de 1 800 000 (Bamba Koné) est clairement isolé à droite — c'est l'outlier identifié en M11.



4. Graphique en barres — Salaire moyen par département

Les barres permettent de comparer des catégories entre elles.

Python — seaborn

python# Calculer la moyenne par département
moy_dept = df.groupby('departement')['salaire'].mean().sort_values(ascending=False).reset_index()

plt.figure(figsize=(9, 5))
sns.barplot(data=moy_dept, x='departement', y='salaire', palette='Blues_d')
plt.title('Salaire moyen par département', fontsize=14, fontweight='bold')
plt.xlabel('Département')
plt.ylabel('Salaire moyen (FCFA)')
plt.xticks(rotation=15)

# Ajouter les valeurs sur les barres
for i, v in enumerate(moy_dept['salaire']):
    plt.text(i, v + 10000, f"{v:,.0f}", ha='center', fontsize=9)

plt.tight_layout()
plt.show()
# Direction : 1 375 000 | Finance : 583 333 | IT : 463 333
# RH : 393 333 | Commercial : 315 000

R — ggplot2

rdf %>%
  group_by(departement) %>%
  summarise(salaire_moyen = mean(salaire)) %>%
  arrange(desc(salaire_moyen)) %>%
  ggplot(aes(x = reorder(departement, -salaire_moyen), y = salaire_moyen)) +
    geom_col(fill = "#2196F3") +
    geom_text(aes(label = scales::comma(round(salaire_moyen))),
              vjust = -0.5, size = 3.5) +
    labs(
      title = "Salaire moyen par département",
      x     = "Département",
      y     = "Salaire moyen (FCFA)"
    ) +
    theme_minimal()

📌 Lecture : Direction écrase les autres départements à cause du salaire du DG. Si on exclut la Direction, Finance est en tête avec 583 000 FCFA de moyenne.



5. Boîte à moustaches — Dispersion par groupe

La boîte à moustaches (boxplot) montre la médiane, les quartiles et les outliers en même temps. Idéale pour comparer des groupes.

Anatomie d'un boxplot :

         ┌───────────┐
  ───────┤           ├───────  ← moustaches (min/max hors outliers)
         │     Q2    │         ← ligne centrale = médiane
         └───────────┘
         Q1         Q3
    ○                           ← point isolé = outlier

Python — seaborn

pythonplt.figure(figsize=(9, 5))
sns.boxplot(data=df, x='contrat', y='salaire',
            order=['CDI', 'CDD', 'Stage'],
            palette=['#1565C0', '#42A5F5', '#90CAF9'])
plt.title('Distribution des salaires par type de contrat', fontsize=14, fontweight='bold')
plt.xlabel('Type de contrat')
plt.ylabel('Salaire (FCFA)')
plt.tight_layout()
plt.show()
# CDI : médiane ≈ 570 000, outlier = 1 800 000
# CDD : médiane ≈ 340 000
# Stage : médiane ≈ 190 000

R — ggplot2

rggplot(df, aes(x = contrat, y = salaire, fill = contrat)) +
  geom_boxplot(outlier.color = "red", outlier.size = 3) +
  scale_fill_manual(values = c(CDI = "#1565C0", CDD = "#42A5F5", Stage = "#90CAF9")) +
  labs(
    title = "Distribution des salaires par type de contrat",
    x     = "Type de contrat",
    y     = "Salaire (FCFA)"
  ) +
  theme_minimal() +
  theme(legend.position = "none")

📌 Lecture : Les CDI ont la plus grande dispersion — allant de 310 000 à 1 800 000. Les Stage sont concentrés entre 180k et 200k. L'outlier rouge (CDI) est Bamba Koné.



6. Camembert — Répartition des contrats

Le camembert convient aux proportions quand il y a peu de catégories (≤ 5).

Python — matplotlib

pythoncontrats = df['contrat'].value_counts()
# CDI: 10, CDD: 3, Stage: 2

couleurs = ['#1565C0', '#42A5F5', '#90CAF9']
explode  = [0.05, 0, 0]   # légèrement détacher la première tranche

plt.figure(figsize=(7, 7))
plt.pie(
    contrats,
    labels=contrats.index,
    autopct='%1.0f%%',
    colors=couleurs,
    explode=explode,
    startangle=90
)
plt.title('Répartition des types de contrat\nBamba & Associés', fontsize=13, fontweight='bold')
plt.show()
# CDI : 67% | CDD : 20% | Stage : 13%

R — ggplot2 (donut)

rcontrats <- df %>%
  count(contrat) %>%
  mutate(pct = round(n / sum(n) * 100))

ggplot(contrats, aes(x = 2, y = n, fill = contrat)) +
  geom_col() +
  coord_polar(theta = "y") +
  xlim(0.5, 2.5) +
  geom_text(aes(label = paste0(pct, "%")),
            position = position_stack(vjust = 0.5), color = "white", size = 5) +
  scale_fill_manual(values = c(CDI = "#1565C0", CDD = "#42A5F5", Stage = "#90CAF9")) +
  labs(title = "Répartition des contrats — Bamba & Associés", fill = "Contrat") +
  theme_void()

📌 Lecture : 2 employés sur 3 sont en CDI. Les stagiaires représentent 13% de l'effectif.



7. Nuage de points — Ancienneté vs Salaire

Le scatter plot explore la relation entre deux variables numériques : est-ce que les employés anciens gagnent plus ?

Python — seaborn

pythonplt.figure(figsize=(9, 6))
sns.scatterplot(
    data=df,
    x='anciennete',
    y='salaire',
    hue='departement',
    size='salaire',
    sizes=(60, 400),
    palette='tab10'
)

# Ajouter une droite de tendance
sns.regplot(data=df, x='anciennete', y='salaire',
            scatter=False, color='gray', line_kws={'linestyle': '--'})

plt.title('Ancienneté vs Salaire par département', fontsize=14, fontweight='bold')
plt.xlabel('Ancienneté (années)')
plt.ylabel('Salaire (FCFA)')
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
plt.tight_layout()
plt.show()
# Tendance : plus on est anciens, plus le salaire tend à augmenter
# Bamba Koné (12 ans, 1 800 000) est très au-dessus de la droite

R — ggplot2

rggplot(df, aes(x = anciennete, y = salaire, color = departement, size = salaire)) +
  geom_point(alpha = 0.8) +
  geom_smooth(method = "lm", se = FALSE, color = "gray",
              linetype = "dashed", linewidth = 0.8, aes(group = 1)) +
  scale_size(range = c(3, 10), guide = "none") +
  labs(
    title  = "Ancienneté vs Salaire par département",
    x      = "Ancienneté (années)",
    y      = "Salaire (FCFA)",
    color  = "Département"
  ) +
  theme_minimal()

📌 Lecture : la droite de tendance confirme une corrélation positive — l'ancienneté est associée à des salaires plus élevés. Bamba Koné (point rouge en haut à droite) tire la droite vers lui.



8. Sauvegarder et personnaliser

Sauvegarder un graphique

python# Python — toujours AVANT plt.show()
plt.savefig('mon_graphique.png', dpi=150, bbox_inches='tight')
plt.savefig('mon_graphique.pdf')   # format vectoriel pour impression
r# R — sauvegarder le dernier graphique ggplot2
ggsave("mon_graphique.png", width = 10, height = 6, dpi = 150)
ggsave("mon_graphique.pdf", width = 10, height = 6)

Personnaliser les couleurs

python# Python — palettes seaborn disponibles
# 'Blues', 'Blues_d', 'viridis', 'magma', 'tab10', 'Set2'
sns.set_palette('Set2')

# Couleur personnalisée (hex)
sns.barplot(data=df, x='dept', y='salaire', color='#FF6B35')
r# R — couleurs manuelles
scale_fill_manual(values = c("#2196F3", "#FF5722", "#4CAF50"))
# ou palettes prêtes à l'emploi
scale_fill_brewer(palette = "Set2")

Thèmes ggplot2

r# Les thèmes les plus utilisés
theme_minimal()   # fond blanc, grille légère
theme_classic()   # fond blanc, axes seulement
theme_bw()        # fond blanc, cadre noir
theme_dark()      # fond sombre


🔗 Pont Excel → Python/R

Chaque graphique Excel a son équivalent direct :

Graphique Excel Python (seaborn/matplotlib) R (ggplot2)
Histogramme sns.histplot() geom_histogram()
Barres groupées sns.barplot() geom_col()
Barres empilées df.plot(kind='bar', stacked=True) geom_col(position='stack')
Courbe (ligne) sns.lineplot() geom_line()
Nuage de points sns.scatterplot() geom_point()
Camembert plt.pie() geom_col() + coord_polar()
Boîte à moustaches sns.boxplot() geom_boxplot()

La grammaire de ggplot2 (à retenir) :

rggplot(data, aes(x = col_x, y = col_y, color = col_groupe)) +
  geom_XXXX() +       # type de graphique
  labs(title = "...") +  # étiquettes
  theme_minimal()        # style

💡 En ggplot2, on ajoute des couches avec +. Chaque geom_ est une couche visuelle. C'est plus verbeux qu'Excel mais infiniment plus flexible.



✏️ Mini-exercices

Exercice 1 — Python : barres horizontales par ancienneté

Crée un graphique en barres horizontales montrant l'ancienneté de chaque employé,
trié du plus ancier au plus récent. Titre : "Ancienneté des employés".

👉 Voir la réponse......
pythondf_tri = df.sort_values('anciennete', ascending=True)

plt.figure(figsize=(9, 7))
plt.barh(df_tri['nom'], df_tri['anciennete'], color='#2196F3')
plt.title('Ancienneté des employés', fontsize=13, fontweight='bold')
plt.xlabel('Ancienneté (années)')
plt.tight_layout()
plt.show()
# Bamba Koné apparaît en haut (12 ans), Soro Aminata et Touré Mariam en bas (0 an)

Exercice 2 — R : boxplot salaires par département

En R avec ggplot2, crée un boxplot comparant la distribution des salaires pour chaque département.
Colorie chaque département d'une couleur différente.

👉 Voir la réponse......
rggplot(df, aes(x = departement, y = salaire, fill = departement)) +
  geom_boxplot(outlier.color = "red", outlier.size = 3) +
  labs(
    title = "Distribution des salaires par département",
    x     = "Département",
    y     = "Salaire (FCFA)"
  ) +
  theme_minimal() +
  theme(legend.position = "none")
# Direction : dispersion énorme (950k à 1 800k)
# Finance : groupé entre 540k et 620k
# Commercial : le plus bas, entre 180k et 380k

Exercice 3 — Python : graphiques en grille (subplots)

Crée une figure avec 2 graphiques côte à côte :

  • Gauche : histogramme des salaires
  • Droite : camembert des types de contrat
👉 Voir la réponse......
pythonfig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13, 5))

# Histogramme
ax1.hist(df['salaire'], bins=6, color='#2196F3', edgecolor='white')
ax1.set_title('Distribution des salaires')
ax1.set_xlabel('Salaire (FCFA)')
ax1.set_ylabel('Effectif')

# Camembert
contrats = df['contrat'].value_counts()
ax2.pie(contrats, labels=contrats.index, autopct='%1.0f%%',
        colors=['#1565C0', '#42A5F5', '#90CAF9'])
ax2.set_title('Répartition des contrats')

fig.suptitle('Tableau de bord RH — Bamba & Associés', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.savefig('dashboard_rh.png', dpi=150)
plt.show()


📋 Cheat Sheet — Visualisation

Python — matplotlib / seaborn

Graphique Code
Histogramme sns.histplot(df['col'])
Barres sns.barplot(data=df, x='cat', y='val')
Boxplot sns.boxplot(data=df, x='cat', y='val')
Scatter sns.scatterplot(data=df, x='x', y='y', hue='cat')
Ligne sns.lineplot(data=df, x='x', y='y')
Camembert plt.pie(valeurs, labels=labels, autopct='%1.0f%%')
Titre plt.title('Titre')
Axes plt.xlabel('X') / plt.ylabel('Y')
Sauvegarder plt.savefig('fichier.png', dpi=150)
Afficher plt.show()
Grille 1×2 fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12,5))

R — ggplot2

Graphique Code
Histogramme geom_histogram(bins=10)
Barres geom_col()
Boxplot geom_boxplot()
Scatter geom_point()
Ligne geom_line()
Droite tendance geom_smooth(method='lm', se=FALSE)
Titre labs(title='...', x='...', y='...')
Style theme_minimal() / theme_classic()
Sauvegarder ggsave('fichier.png', dpi=150)
Facettes (grille) facet_wrap(~ variable)


🧠 Quiz de validation

Question 1 — Tu veux montrer la répartition des salaires (est-ce que les valeurs se concentrent entre 400k et 600k ?). Quel graphique choisir ?

a) Camembert
b) Histogramme
c) Nuage de points
d) Courbe

👉 Voir la réponse......

Réponse : b) Histogramme
L'histogramme est fait pour visualiser la distribution d'une variable numérique continue. Il montre où se concentrent les valeurs, si la distribution est symétrique ou asymétrique, et où se trouvent les outliers.


Question 2 — En ggplot2, comment ajoute-t-on un titre à un graphique ?

a) title("Mon titre")
b) ggplot(title = "Mon titre")
c) + labs(title = "Mon titre")
d) + theme(title = "Mon titre")

👉 Voir la réponse......

Réponse : c) + labs(title = "Mon titre")
En ggplot2, labs() gère toutes les étiquettes : title, subtitle, x, y, color, fill, caption. On l'ajoute comme une couche supplémentaire avec +.


Question 3 — Tu dois comparer la dispersion des salaires entre les CDI, CDD et Stagiaires pour détecter des inégalités. Quel graphique est le plus adapté ?

a) Barres (salaire moyen par contrat)
b) Camembert
c) Boîte à moustaches (boxplot)
d) Histogramme

👉 Voir la réponse......

Réponse : c) Boîte à moustaches
Le boxplot montre en même temps : médiane, Q1, Q3, outliers et étendue — pour chaque groupe. Un graphique en barres ne montre que la moyenne, cachant toute la dispersion. Le boxplot révèle si un groupe est homogène ou très hétérogène.



✅ Résumé du module

Ce que tu as appris Détail
Choisir le bon graphique Histogramme, barres, boxplot, scatter, camembert
matplotlib Graphiques de base, personnalisation, subplots
seaborn Graphiques statistiques élégants en une ligne
ggplot2 Grammaire des graphiques : aes + geom + labs + theme
Sauvegarder plt.savefig() (Python) · ggsave() (R)
Pont Excel→Python/R Chaque graphique Excel traduit en code

⚡ Bonus — Packages qui génèrent tous les graphiques automatiquement

Comme pour les statistiques descriptives (M11), il existe des packages qui produisent des visualisations complètes en une ou deux lignes, sans coder graphique par graphique.

Python

Package Ce qu'il génère Installation
plotly.express Graphiques interactifs (zoom, survol) — une ligne par graphique pip install plotly
ydata-profiling Rapport HTML complet : stats + histogrammes + corrélations + outliers pip install ydata-profiling
sweetviz Rapport visuel HTML avec tous les graphiques de distribution pip install sweetviz
dtale Interface interactive dans le navigateur : graphiques à la demande pip install dtale
pandas .plot() Graphiques rapides directement depuis un DataFrame, sans import inclus avec pandas
python# plotly.express — graphique interactif en une ligne
import plotly.express as px

px.histogram(df, x='salaire', color='departement', title='Distribution des salaires')
px.box(df, x='contrat', y='salaire', title='Salaires par contrat')
px.scatter(df, x='anciennete', y='salaire', color='departement', size='salaire')

# pandas .plot() — le plus rapide, sans import supplémentaire
df['salaire'].plot(kind='hist', bins=6, title='Distribution des salaires')
df.groupby('departement')['salaire'].mean().plot(kind='bar', title='Salaire moyen par département')

# dtale — lance une interface interactive dans le navigateur
import dtale
dtale.show(df)   # → ouvre un tableau de bord complet dans ton navigateur

R

Package Ce qu'il génère Installation
DataExplorer Rapport HTML complet : distributions, corrélations, valeurs manquantes install.packages("DataExplorer")
GGally Pairplot (toutes les relations entre variables en une grille) install.packages("GGally")
skimr Résumé texte + mini-histogrammes dans la console install.packages("skimr")
r# DataExplorer — rapport HTML automatique
library(DataExplorer)
create_report(df, output_file = "rapport_rh.html")
# → génère un HTML avec : aperçu, distributions, corrélations, valeurs manquantes

# GGally — pairplot : toutes les variables vs toutes les variables
library(GGally)
ggpairs(df[, c("salaire", "anciennete")])

# skimr — résumé avec mini-histogrammes ASCII
library(skimr)
skim(df)

Quand utiliser quoi ?

Besoin Outil recommandé
Explorer rapidement un nouveau dataset ydata-profiling (Python) · DataExplorer (R)
Graphique interactif pour une présentation plotly.express
Explorer librement dans le navigateur dtale
Graphique rapide sans réfléchir pandas .plot()
Toutes les relations entre variables GGally::ggpairs() (R)
Graphique précis et personnalisé seaborn / ggplot2

🎉 Acte IV terminé !

Tu as maintenant les bases de R & Python pour l'analyse de données :

  • M10 — Environnements, installation, premiers pas
  • M11 — Statistiques descriptives (pandas, dplyr, EDA rapide)
  • M12 — Visualisation (matplotlib, seaborn, ggplot2, packages auto)

🚀 La suite — Mini-projet B & Projet Final

Mini-projet B — Dataset télécom (opérateur mobile ivoirien fictif)
Tu appliqueras Excel + SQL + Python/R sur des données simples de téléphonie : abonnés, consommations, forfaits.

Projet Final Niveau Débutant — Rapport complet RH ivoirien
Le projet de clôture qui valide l'ensemble du bootcamp niveau débutant.


Bootcamp Data Analyst — From Zero to Hero | Niveau Débutant · Acte IV