Author

Dr. Elisabetta Pietrostefani

Published

March 22, 2026

4.1 Qu’est-ce qu’une carte choroplèthe ?

Une carte choroplèthe est une représentation thématique dans laquelle chaque unité géographique (commune, département, région, pays…) est colorée selon la valeur d’une variable statistique.

C’est l’équivalent cartographique de l’histogramme : les deux permettent d’observer la distribution spatiale d’une variable.

La décision clé

Pourquoi une valeur donnée reçoit-elle une couleur spécifique ? Le choix de la méthode de classification et de la palette de couleurs change radicalement le message de la carte.


4.2 Données et packages

Nous utilisons {sf} pour les objets spatiaux, {ggplot2} pour toute la cartographie, et {rnaturalearth} pour les données géographiques françaises.

Code
library(sf)                 # objets spatiaux
library(ggplot2)            # visualisation — notre outil principal
library(dplyr)              # manipulation de données
library(classInt)           # méthodes de classification
library(viridis)            # palettes perceptuellement uniformes
library(RColorBrewer)       # palettes ColorBrewer
library(scales)             # formatage des axes et légendes
library(rnaturalearth)      # données géographiques
library(rnaturalearthdata)

# Régions françaises métropolitaines
france_regions <- ne_states(country = "France", returnclass = "sf") |>
  filter(!name %in% c("Guyane française", "Martinique",
                       "Guadeloupe", "La Réunion", "Mayotte")) |>
  select(name, region, iso_3166_2, geometry)

glimpse(france_regions)
Rows: 96
Columns: 4
$ name       <chr> "Nord", "Ardennes", "Aisne", "Meuse", "Meurthe-et-Moselle",…
$ region     <chr> "Hauts-de-France", "Grand Est", "Hauts-de-France", "Grand E…
$ iso_3166_2 <chr> "FR-59", "FR-08", "FR-02", "FR-55", "FR-54", "FR-57", "FR-0…
$ geometry   <MULTIPOLYGON [°]> MULTIPOLYGON (((2.5218 51.0..., MULTIPOLYGON (…
Code
# Données socio-économiques simulées
# (dans un vrai projet : read_csv() + left_join())
set.seed(42)
n <- nrow(france_regions)

france_regions <- france_regions |>
  mutate(
    taux_chomage  = round(runif(n, min = 4.5, max = 18.0), 1),
    revenu_median = round(rnorm(n, mean = 1850, sd = 280), 0),
    densite_pop   = round(exp(rnorm(n, mean = 4.8, sd = 1.2)), 0),
    part_diplomes = round(rnorm(n, mean = 28, sd = 7), 1)
  )

# Vecteur numérique brut — nécessaire pour classIntervals
chomage_vec <- france_regions |> st_drop_geometry() |> pull(taux_chomage)

# Fonction utilitaire : remplace les labels (a,b] par "a – b" (plus lisible)
clean_cut_labels <- function(x) {
  levels(x) <- gsub("\\(|\\[|\\]|\\)", "", levels(x)) |>  # retirer les crochets
    gsub(",", " – ", x = _)                                 # remplacer la virgule
  x
}

france_regions |>
  st_drop_geometry() |>
  select(name, taux_chomage, revenu_median, densite_pop) |>
  head(8) |>
  knitr::kable(caption = "Extrait des données simulées par région")
Extrait des données simulées par région
name taux_chomage revenu_median densite_pop
Nord 16.8 1729 74
Ardennes 17.2 2034 462
Aisne 8.4 1940 68
Meuse 15.7 1631 72
Meurthe-et-Moselle 13.2 2291 280
Moselle 11.5 2030 34
Alpes-Maritimes 14.4 1875 116
Alpes-de-Haute-Provence 6.3 1927 19

4.3 Première carte : deux syntaxes, un même résultat

Avant de classifier, affichons simplement la géographie. Ce cours utilise ggplot2 comme outil principal tout au long des deux jours — mais il est utile de voir une fois comment mapsf produit le même résultat avec une syntaxe différente.

Code
library(mapsf)

mf_map(france_regions, type = "base",
       col    = "#e8f4f8",
       border = "white",
       lwd    = 0.8)
mf_title("Régions françaises — mapsf")
mf_credits("Source : Natural Earth")
Figure 4.1: Carte de base avec mapsf — syntaxe proche de la cartographie classique
Code
ggplot(france_regions) +
  geom_sf(fill = "#e8f4f8", color = "white", linewidth = 0.5) +
  labs(
    title   = "Régions françaises — ggplot2",
    caption = "Source : Natural Earth | Cours R-Carto"
  ) +
  theme_void(base_size = 13) +
  theme(plot.title = element_text(face = "bold", hjust = 0.5))
Figure 4.2: La même carte avec ggplot2 — syntaxe que nous utiliserons tout au long du cours
Pourquoi ggplot2 ?

mapsf est excellent pour produire des cartes rapidement avec peu de code. Nous choisissons ggplot2 parce qu’il partage exactement la même grammaire que tous vos autres graphiques — histogrammes, nuages de points, boxplots. Apprendre une seule syntaxe suffit pour tout visualiser.


4.4 Méthodes de classification

Le choix de la méthode de classification est la décision cartographique la plus importante. Voyons les quatre principales méthodes appliquées au taux de chômage.

4.4.1 Aperçu de la distribution

Toujours examiner la distribution avant de cartographier.

Code
ggplot(france_regions, aes(x = taux_chomage)) +
  geom_histogram(binwidth = 1.5, fill = "#2c7bb6", color = "white", alpha = 0.85) +
  geom_density(aes(y = after_stat(count) * 1.5),
               color = "#d7191c", linewidth = 1) +
  labs(
    title    = "Distribution du taux de chômage par région",
    subtitle = "Données simulées à des fins pédagogiques",
    x = "Taux de chômage (%)",
    y = "Nombre de régions"
  ) +
  theme_minimal(base_size = 13) +
  theme(plot.title = element_text(face = "bold"))
Figure 4.3: Distribution du taux de chômage simulé par région

4.4.2 Méthode 1 — Intervalle égal (equal interval)

Divise la plage des valeurs en intervalles de largeur identique, indépendamment de la distribution des données.

Code
breaks_equal <- classIntervals(chomage_vec, n = 5, style = "equal")

france_regions <- france_regions |>
  mutate(classe_equal = cut(taux_chomage,
                             breaks = breaks_equal$brks,
                             include.lowest = TRUE,
                             dig.lab = 4,
                             ordered_result = TRUE) |> clean_cut_labels())

ggplot(france_regions) +
  geom_sf(aes(fill = classe_equal), color = "white", linewidth = 0.4) +
  scale_fill_brewer(palette = "YlOrRd", name = "Taux de\nchômage (%)") +
  labs(
    title    = "Intervalle égal (n = 5)",
    subtitle = "Chaque classe couvre un intervalle de valeurs identique",
    caption  = "Données simulées | Cours R-Carto"
  ) +
  theme_void(base_size = 12) +
  theme(plot.title    = element_text(face = "bold", hjust = 0.5),
        plot.subtitle = element_text(color = "grey40", hjust = 0.5),
        legend.position = "right")

cat("Bornes — intervalle égal :", round(breaks_equal$brks, 1), "\n")
Bornes — intervalle égal : 4.5 7.2 9.9 12.5 15.2 17.9 
Figure 4.4: Classification par intervalles égaux
Attention avec l’intervalle égal

Si la distribution est asymétrique, cette méthode peut créer des classes presque vides et des classes très peuplées, rendant la carte peu informative.

4.4.3 Méthode 2 — Quantiles (quantile)

Chaque classe contient le même nombre d’observations, indépendamment des valeurs numériques.

Code
breaks_quantile <- classIntervals(chomage_vec, n = 5, style = "quantile")

france_regions <- france_regions |>
  mutate(classe_quantile = cut(taux_chomage,
                                breaks = breaks_quantile$brks,
                                include.lowest = TRUE,
                                dig.lab = 4,
                                ordered_result = TRUE) |> clean_cut_labels())

ggplot(france_regions) +
  geom_sf(aes(fill = classe_quantile), color = "white", linewidth = 0.4) +
  scale_fill_brewer(palette = "YlOrRd", name = "Taux de\nchômage (%)") +
  labs(
    title    = "Quantiles (n = 5)",
    subtitle = "Chaque classe contient le même nombre de régions",
    caption  = "Données simulées | Cours R-Carto"
  ) +
  theme_void(base_size = 12) +
  theme(plot.title    = element_text(face = "bold", hjust = 0.5),
        plot.subtitle = element_text(color = "grey40", hjust = 0.5),
        legend.position = "right")

cat("Bornes — quantiles :", round(breaks_quantile$brks, 1), "\n")
Bornes — quantiles : 4.5 7.3 10.4 13.4 15.7 17.9 
Figure 4.5: Classification par quantiles
Quand utiliser les quantiles ?

Idéal pour comparer les régions entre elles (les 20% les plus élevés vs les 20% les plus faibles). Attention : peut regrouper dans la même classe des valeurs numériquement très différentes.

4.4.4 Méthode 3 — Jenks / Ruptures naturelles (natural breaks)

Minimise la variance au sein de chaque classe et maximise la variance entre les classes.

Code
breaks_jenks <- classIntervals(chomage_vec, n = 5, style = "jenks")

france_regions <- france_regions |>
  mutate(classe_jenks = cut(taux_chomage,
                             breaks = breaks_jenks$brks,
                             include.lowest = TRUE,
                             dig.lab = 4,
                             ordered_result = TRUE) |> clean_cut_labels())

ggplot(france_regions) +
  geom_sf(aes(fill = classe_jenks), color = "white", linewidth = 0.4) +
  scale_fill_brewer(palette = "YlOrRd", name = "Taux de\nchômage (%)") +
  labs(
    title    = "Jenks / Ruptures naturelles (n = 5)",
    subtitle = "Les bornes suivent les sauts naturels de la distribution",
    caption  = "Données simulées | Cours R-Carto"
  ) +
  theme_void(base_size = 12) +
  theme(plot.title    = element_text(face = "bold", hjust = 0.5),
        plot.subtitle = element_text(color = "grey40", hjust = 0.5),
        legend.position = "right")

cat("Bornes — Jenks :", round(breaks_jenks$brks, 1), "\n")
Bornes — Jenks : 4.5 6.6 9.3 12.1 15.1 17.9 
Figure 4.6: Classification de Jenks — optimise la cohérence interne des classes

4.4.5 Méthode 4 — Écart-type (standard deviation)

Les classes sont définies par rapport à la moyenne, en multiples d’écart-type. Très utile pour mettre en évidence les valeurs atypiques.

Code
breaks_sd <- classIntervals(chomage_vec, n = 5, style = "sd")

france_regions <- france_regions |>
  mutate(classe_sd = cut(taux_chomage,
                          breaks = breaks_sd$brks,
                          include.lowest = TRUE,
                          dig.lab = 4,
                          ordered_result = TRUE) |> clean_cut_labels())

ggplot(france_regions) +
  geom_sf(aes(fill = classe_sd), color = "white", linewidth = 0.4) +
  scale_fill_brewer(palette = "RdBu", direction = -1,
                    name = "Taux de\nchômage (%)") +
  labs(
    title    = "Écart-type",
    subtitle = paste0("Moyenne : ", round(mean(chomage_vec), 1),
                      "% | Écart-type : ", round(sd(chomage_vec), 1), "%"),
    caption  = "Données simulées | Cours R-Carto"
  ) +
  theme_void(base_size = 12) +
  theme(plot.title    = element_text(face = "bold", hjust = 0.5),
        plot.subtitle = element_text(color = "grey40", hjust = 0.5),
        legend.position = "right")
Figure 4.7: Classification par écart-type — met en évidence les valeurs atypiques

4.4.6 Comparaison visuelle des quatre méthodes

Code
bornes <- data.frame(
  valeur  = c(breaks_equal$brks, breaks_quantile$brks,
              breaks_jenks$brks, breaks_sd$brks),
  methode = rep(
    c("Intervalle égal", "Quantiles", "Jenks", "Écart-type"),
    times = c(length(breaks_equal$brks),   length(breaks_quantile$brks),
              length(breaks_jenks$brks),    length(breaks_sd$brks))
  )
) |>
  mutate(methode = factor(methode,
                           levels = c("Intervalle égal", "Quantiles",
                                      "Jenks", "Écart-type")))

histo <- data.frame(x = chomage_vec)

ggplot() +
  geom_histogram(data = histo, aes(x = x),
                 binwidth = 1.2, fill = "#cce5f0", color = "white") +
  geom_vline(data = bornes, aes(xintercept = valeur),
             color = "#d7191c", linewidth = 0.8, linetype = "dashed") +
  facet_wrap(~methode, nrow = 2) +
  labs(
    title   = "Position des bornes selon la méthode de classification",
    x       = "Taux de chômage (%)",
    y       = "Nombre de régions",
    caption = "Les lignes rouges indiquent les bornes de chaque classe"
  ) +
  theme_minimal(base_size = 12) +
  theme(plot.title   = element_text(face = "bold", hjust = 0.5),
        strip.text   = element_text(face = "bold"),
        plot.caption = element_text(color = "grey50", size = 9))
Figure 4.8: Position des bornes de chaque méthode sur la distribution

4.5 Choix de la palette de couleurs

La palette doit correspondre à la nature de la variable et à l’objectif de la carte.

4.5.1 Palettes séquentielles — variables continues unidirectionnelles

Pour des variables qui vont du bas vers le haut (chômage, densité, revenu).

Code
ggplot(france_regions) +
  geom_sf(aes(fill = taux_chomage), color = "white", linewidth = 0.4) +
  scale_fill_viridis_c(
    option = "viridis",
    name   = "Taux de\nchômage (%)",
    labels = label_number(suffix = "%")
  ) +
  labs(
    title    = "Palette séquentielle : viridis",
    subtitle = "Continue, sans classification préalable",
    caption  = "Données simulées | Cours R-Carto"
  ) +
  theme_void(base_size = 12) +
  theme(plot.title      = element_text(face = "bold", hjust = 0.5),
        plot.subtitle   = element_text(color = "grey40", hjust = 0.5),
        legend.key.height = unit(1.5, "cm"))
Figure 4.9: Palette séquentielle (viridis) — lisible en noir et blanc et pour les daltoniens

4.5.2 Palettes divergentes — variables bipolaires

Pour des variables organisées autour d’un point central (écart à la moyenne, variation positive/négative).

Code
france_regions <- france_regions |>
  mutate(ecart_revenu = revenu_median - median(revenu_median))

ggplot(france_regions) +
  geom_sf(aes(fill = ecart_revenu), color = "white", linewidth = 0.4) +
  scale_fill_distiller(
    palette   = "RdBu",
    direction = 1,
    name      = "Écart au\nmédian (€)",
    labels    = label_number()
  ) +
  labs(
    title    = "Palette divergente : RdBu",
    subtitle = "Bleu = au-dessus du médian national | Rouge = en dessous",
    caption  = "Données simulées | Cours R-Carto"
  ) +
  theme_void(base_size = 12) +
  theme(plot.title      = element_text(face = "bold", hjust = 0.5),
        plot.subtitle   = element_text(color = "grey40", hjust = 0.5),
        legend.key.height = unit(1.5, "cm"))
Figure 4.10: Palette divergente (RdBu) pour l’écart au revenu médian

4.5.3 Palettes catégorielles — données nominales

Pour des variables sans ordre (vote majoritaire, type de zone, catégorie d’usage du sol).

Code
france_regions <- france_regions |>
  mutate(zone = case_when(
    taux_chomage < 8  ~ "Faible chômage",
    taux_chomage < 12 ~ "Chômage modéré",
    TRUE              ~ "Chômage élevé"
  ),
  zone = factor(zone, levels = c("Faible chômage",
                                  "Chômage modéré",
                                  "Chômage élevé")))

ggplot(france_regions) +
  geom_sf(aes(fill = zone), color = "white", linewidth = 0.4) +
  scale_fill_manual(
    values = c("Faible chômage"  = "#2c7bb6",
               "Chômage modéré" = "#ffffbf",
               "Chômage élevé"  = "#d7191c"),
    name = "Niveau de\nchômage"
  ) +
  labs(
    title    = "Palette catégorielle : 3 niveaux",
    subtitle = "Pas de dégradé — les catégories n'ont pas d'ordre implicite de couleur",
    caption  = "Données simulées | Cours R-Carto"
  ) +
  theme_void(base_size = 12) +
  theme(plot.title    = element_text(face = "bold", hjust = 0.5),
        plot.subtitle = element_text(color = "grey40", hjust = 0.5))
Figure 4.11: Palette catégorielle — 3 niveaux de chômage

4.6 Carte complète : tous les éléments réunis

Une carte publiable doit comporter : titre, sous-titre, légende, et source.

Code
ggplot(france_regions) +
  geom_sf(aes(fill = classe_jenks), color = "white", linewidth = 0.4) +
  scale_fill_brewer(
    palette = "YlOrRd",
    name    = "Taux de chômage (%)\n[Jenks, n = 5]"
  ) +
  labs(
    title    = "Taux de chômage par région",
    subtitle = "France métropolitaine — données simulées",
    caption  = "Source : données simulées à des fins pédagogiques\nCours R-Carto | Dr. E. Pietrostefani"
  ) +
  theme_void(base_size = 13) +
  theme(
    plot.title        = element_text(face = "bold", size = 16, hjust = 0.5),
    plot.subtitle     = element_text(color = "grey40", hjust = 0.5,
                                      margin = margin(b = 8)),
    plot.caption      = element_text(color = "grey55", size = 9,
                                      hjust = 0, margin = margin(t = 10)),
    legend.position   = "right",
    legend.title      = element_text(face = "bold", size = 10),
    legend.key.height = unit(0.9, "cm"),
    plot.margin       = margin(10, 20, 10, 20),
    plot.background   = element_rect(fill = "white", color = NA)
  )
Figure 4.12: Carte choroplèthe complète avec tous les éléments cartographiques

4.6.1 Choroplèthe + symboles proportionnels

ggplot2 permet de superposer facilement plusieurs couches — ici une choroplèthe et des cercles proportionnels sur les centroïdes.

Code
centroids <- france_regions |>
  st_centroid() |>
  mutate(lon = st_coordinates(geometry)[, 1],
         lat = st_coordinates(geometry)[, 2])

ggplot(france_regions) +
  geom_sf(aes(fill = revenu_median), color = "white", linewidth = 0.4) +
  scale_fill_distiller(
    palette   = "Blues",
    direction = 1,
    name      = "Revenu\nmédian (€)",
    labels    = label_comma(suffix = " €")
  ) +
  geom_point(
    data  = centroids,
    aes(x = lon, y = lat, size = densite_pop),
    color = "#d7191c", alpha = 0.65
  ) +
  scale_size_continuous(
    name   = "Densité\n(hab/km²)",
    range  = c(1, 10),
    breaks = c(50, 200, 500, 1000)
  ) +
  labs(
    title    = "Revenu médian et densité de population",
    subtitle = "Choroplèthe + symboles proportionnels",
    caption  = "Source : données simulées | Cours R-Carto"
  ) +
  theme_void(base_size = 12) +
  theme(
    plot.title      = element_text(face = "bold", size = 14, hjust = 0.5),
    plot.subtitle   = element_text(color = "grey40", hjust = 0.5),
    plot.caption    = element_text(color = "grey55", size = 9),
    legend.position = "right",
    plot.background = element_rect(fill = "white", color = NA),
    plot.margin     = margin(10, 20, 10, 20)
  )
Figure 4.13: Combinaison choroplèthe (revenu) + symboles proportionnels (densité)

4.7 Résumé : quelle méthode choisir ?

Méthode Quand l’utiliser Attention
Intervalle égal Distribution relativement uniforme Classes vides si distribution asymétrique
Quantiles Comparer les positions relatives Peut regrouper des valeurs très différentes
Jenks Usage général, distribution quelconque Calcul plus long sur grands jeux de données
Écart-type Mettre en évidence les anomalies Nécessite une distribution proche de la normale
Valeurs uniques Variables catégorielles nominales Ne pas utiliser pour des données continues

Nombre de classes : entre 5 et 7 pour la plupart des cartes. Au-delà de 12, la lecture devient difficile.

Règle pratique

Testez toujours plusieurs méthodes et choisissez celle qui :

  1. Reflète honnêtement la distribution réelle
  2. Met en valeur le message géographique souhaité
  3. Est compréhensible pour votre audience

Les choix cartographiques sont des choix éditoriaux — ils ne sont jamais neutres.


4.8 Exercice pratique

À vous de jouer !

Avec les données france_regions chargées dans ce document :

  1. Cartographiez part_diplomes avec 3 méthodes de classification en ggplot2
  2. Pour chaque carte, justifiez en une phrase pourquoi cette méthode est adaptée (ou non)
  3. Choisissez la palette la plus appropriée et justifiez votre choix
  4. Produisez une carte finale complète avec titre, sous-titre, légende et source

Bonus : Superposez densite_pop en symboles proportionnels sur votre carte finale.

Code
# Votre code ici — remplacez les ... par vos paramètres
diplomes_vec <- france_regions |> st_drop_geometry() |> pull(part_diplomes)
breaks_ex    <- classIntervals(diplomes_vec, n = 5, style = "...")

france_regions <- france_regions |>
  mutate(classe_ex = cut(part_diplomes,
                          breaks = breaks_ex$brks,
                          include.lowest = TRUE,
                          dig.lab = 4,
                          ordered_result = TRUE) |> clean_cut_labels())

ggplot(france_regions) +
  geom_sf(aes(fill = classe_ex), color = "white", linewidth = 0.4) +
  scale_fill_brewer(palette = "...", name = "...") +
  labs(title = "...", subtitle = "...", caption = "...") +
  theme_void()

← Section précédente : Introduction à la Cartographie → Section suivante : Manipulation d’Objets Vectoriels (à venir)


Dr. Elisabetta Pietrostefani — Directrice Adjointe, Geographic Data Science Lab — Université de Liverpool | Co-Directrice, Imago : Data Service for Imagery | Chercheuse Associée, London School of Economics