Niveau de vie moyen par décile - reve-niv-vie-decile

Données - INSEE

Niveau de vie moyen annuel par unité de consommation, en euros 2024 constants, France métropolitaine, 1996-2024. « D1- » désigne les 10 % les plus modestes (sous le 1er décile), « D9+ » les 10 % les plus aisés (au-dessus du 9e décile). Les séries ont plusieurs ruptures (2010, 2012, 2020) : les courbes ne sont reliées qu’au sein d’une même série.

Le niveau de vie moyen par décile

Code
pal <- viridisLite::mako(10, begin = 0.1, end = 0.85, direction = -1)
ggplot(deciles |> mutate(decile = factor(decile, levels = ordre_deciles)),
       aes(x = year, y = value / 1000, color = decile, group = interaction(decile, segment))) +
  geom_line(linewidth = 1) +
  geom_text(data = ~ filter(.x, year == max(year)), aes(label = decile), hjust = -0.15, size = 3.3,
            show.legend = FALSE) +
  scale_color_manual(values = pal, guide = "none") +
  scale_x_continuous(breaks = seq(1996, 2024, 4), expand = expansion(mult = c(0.02, 0.1))) +
  scale_y_log10(breaks = c(10, 15, 20, 30, 50, 80)) +
  theme_nv() +
  labs(x = NULL, y = "Milliers d'euros 2024 par an (échelle logarithmique)",
       title = "Le niveau de vie progresse à toutes les hauteurs de la distribution",
       subtitle = "niveau de vie annuel moyen par tranche de déciles, euros 2024 constants")

Le niveau de vie des 10 % les plus aisés : IPC contre IPCH

Les séries publiées par l’Insee sont en euros 2024 constants au sens de l’IPC (indice des prix à la consommation). Comme dans ipp/indicefp.qmd, on les redéflate par l’IPCH (indice harmonisé, Eurostat) : on retrouve le nominal en multipliant par IPC(année)/IPC(2024), puis on divise par IPCH(année)/IPCH(2024). L’IPCH ayant augmenté plus vite que l’IPC depuis 1996, les niveaux de vie passés sont plus élevés en euros 2024 « IPCH » et la progression apparente est plus faible.

Code
top <- niv |>
  filter(decile == "D9+") |>
  select(year, segment, `IPC (série publiée)` = value, `IPCH (calculé)` = value_ipch) |>
  pivot_longer(starts_with("IPC"), names_to = "deflateur", values_to = "valeur")
cols_def <- c("IPC (série publiée)" = rose, "IPCH (calculé)" = vert)
ggplot(top, aes(x = year, y = valeur / 1000, color = deflateur, group = interaction(deflateur, segment))) +
  geom_line(linewidth = 1.2) +
  geom_point(data = ~ filter(.x, year %in% c(1996, 2024)), size = 2.6) +
  geom_text(data = ~ filter(.x, year == 2024), aes(label = sprintf("%.1f k€", valeur / 1000)),
            hjust = -0.15, size = 3.6, show.legend = FALSE) +
  geom_text(data = ~ filter(.x, year == 1996), aes(label = sprintf("%.1f k€", valeur / 1000)),
            hjust = 1.15, size = 3.6, show.legend = FALSE) +
  scale_color_manual(values = cols_def, name = NULL) +
  scale_x_continuous(breaks = seq(1996, 2024, 4), expand = expansion(mult = c(0.08, 0.1))) +
  theme_nv() +
  labs(x = NULL, y = "Milliers d'euros 2024 par an",
       title = "Niveau de vie moyen des 10 % les plus aisés, en euros 2024 constants",
       subtitle = "déflaté par l'IPC (série Insee) et par l'IPCH (calcul) ; courbes interrompues aux ruptures de série")

Code
base100 <- niv |>
  filter(decile == "D9+") |>
  group_by(segment) |>
  ungroup() |>
  select(year, annee_brut, segment, ipc = value, ipch = value_ipch) |>
  mutate(ref_ipc = ipc[year == 1996][1], ref_ipch = ipch[year == 1996][1]) |>
  transmute(year, segment, `IPC (série publiée)` = 100 * ipc / ref_ipc, `IPCH (calculé)` = 100 * ipch / ref_ipch) |>
  pivot_longer(starts_with("IPC"), names_to = "deflateur", values_to = "indice")
ggplot(base100, aes(x = year, y = indice, color = deflateur, group = interaction(deflateur, segment))) +
  geom_hline(yintercept = 100, color = "grey60", linetype = "dashed") +
  geom_line(linewidth = 1.2) +
  geom_text(data = ~ filter(.x, year == 2024), aes(label = sprintf("%.0f", indice)), hjust = -0.3,
            fontface = "bold", show.legend = FALSE) +
  scale_color_manual(values = cols_def, name = NULL) +
  scale_x_continuous(breaks = seq(1996, 2024, 4), expand = expansion(mult = c(0.02, 0.08))) +
  theme_nv() +
  labs(x = NULL, y = "Indice, base 100 en 1996",
       title = "Depuis 1996, le niveau de vie des plus aisés progresse moins avec l'IPCH qu'avec l'IPC",
       subtitle = "niveau de vie moyen au-dessus du 9e décile, euros constants, base 100 en 1996")

Le niveau de vie des 10 % les moins aisés : IPC contre IPCH

Même méthode pour les 10 % les plus modestes (en dessous du 1er décile).

Code
bas <- niv |>
  filter(decile == "D1-") |>
  select(year, segment, `IPC (série publiée)` = value, `IPCH (calculé)` = value_ipch) |>
  pivot_longer(starts_with("IPC"), names_to = "deflateur", values_to = "valeur")
ggplot(bas, aes(x = year, y = valeur / 1000, color = deflateur, group = interaction(deflateur, segment))) +
  geom_line(linewidth = 1.2) +
  geom_point(data = ~ filter(.x, year %in% c(1996, 2024)), size = 2.6) +
  geom_text(data = ~ filter(.x, year == 2024), aes(label = sprintf("%.1f k€", valeur / 1000)),
            hjust = -0.15, size = 3.6, show.legend = FALSE) +
  geom_text(data = ~ filter(.x, year == 1996), aes(label = sprintf("%.1f k€", valeur / 1000)),
            hjust = 1.15, size = 3.6, show.legend = FALSE) +
  scale_color_manual(values = cols_def, name = NULL) +
  scale_x_continuous(breaks = seq(1996, 2024, 4), expand = expansion(mult = c(0.08, 0.1))) +
  theme_nv() +
  labs(x = NULL, y = "Milliers d'euros 2024 par an",
       title = "Niveau de vie moyen des 10 % les moins aisés, en euros 2024 constants",
       subtitle = "déflaté par l'IPC (série Insee) et par l'IPCH (calcul) ; courbes interrompues aux ruptures de série")

Code
niv |>
  filter(decile == "D1-") |>
  mutate(ref_ipc = value[year == 1996][1], ref_ipch = value_ipch[year == 1996][1]) |>
  transmute(year, segment, `IPC (série publiée)` = 100 * value / ref_ipc, `IPCH (calculé)` = 100 * value_ipch / ref_ipch) |>
  pivot_longer(starts_with("IPC"), names_to = "deflateur", values_to = "indice") |>
  ggplot(aes(x = year, y = indice, color = deflateur, group = interaction(deflateur, segment))) +
  geom_hline(yintercept = 100, color = "grey60", linetype = "dashed") +
  geom_line(linewidth = 1.2) +
  geom_text(data = ~ filter(.x, year == 2024), aes(label = sprintf("%.0f", indice)), hjust = -0.3,
            fontface = "bold", show.legend = FALSE) +
  scale_color_manual(values = cols_def, name = NULL) +
  scale_x_continuous(breaks = seq(1996, 2024, 4), expand = expansion(mult = c(0.02, 0.08))) +
  theme_nv() +
  labs(x = NULL, y = "Indice, base 100 en 1996",
       title = "Les plus modestes : un niveau de vie en léger recul depuis 2002, davantage avec l'IPCH",
       subtitle = "niveau de vie moyen en dessous du 1er décile, euros constants, base 100 en 1996")

Haut contre bas, côte à côte

Code
niv |>
  filter(decile %in% c("D1-", "D9+")) |>
  mutate(groupe = if_else(decile == "D9+", "10 % les plus aisés", "10 % les moins aisés")) |>
  group_by(decile) |>
  mutate(ref_ipc = value[year == 1996][1], ref_ipch = value_ipch[year == 1996][1]) |>
  ungroup() |>
  transmute(year, segment, groupe, `IPC (série publiée)` = 100 * value / ref_ipc,
            `IPCH (calculé)` = 100 * value_ipch / ref_ipch) |>
  pivot_longer(starts_with("IPC"), names_to = "deflateur", values_to = "indice") |>
  ggplot(aes(x = year, y = indice, color = deflateur, group = interaction(deflateur, segment))) +
  geom_hline(yintercept = 100, color = "grey60", linetype = "dashed") +
  geom_line(linewidth = 1.1) +
  geom_text(data = ~ filter(.x, year == 2024), aes(label = sprintf("%.0f", indice)), hjust = -0.3,
            fontface = "bold", show.legend = FALSE) +
  facet_wrap(~ groupe) +
  scale_color_manual(values = cols_def, name = NULL) +
  scale_x_continuous(breaks = seq(1996, 2024, 8), expand = expansion(mult = c(0.02, 0.15))) +
  theme_nv() + theme(strip.text = element_text(face = "bold", hjust = 0)) +
  labs(x = NULL, y = "Indice, base 100 en 1996",
       title = "Le haut a progressé nettement plus que le bas, avec l'un ou l'autre indice de prix",
       subtitle = "niveau de vie moyen, euros 2024 constants, base 100 en 1996")

Niveau de vie mensuel (euros par mois)

Les mêmes séries exprimées par mois (niveau de vie annuel ÷ 12), pour être directement comparables aux seuils de pauvreté mensuels (reve-pauv-seuil-mensuel).

Code
mens <- niv |>
  filter(decile %in% c("D1-", "D9+")) |>
  mutate(groupe = if_else(decile == "D9+", "10 % les plus aisés", "10 % les moins aisés")) |>
  select(year, segment, groupe, `IPC (série publiée)` = value, `IPCH (calculé)` = value_ipch) |>
  pivot_longer(starts_with("IPC"), names_to = "deflateur", values_to = "annuel") |>
  mutate(mensuel = annuel / 12)
ggplot(mens, aes(x = year, y = mensuel, color = deflateur, group = interaction(deflateur, segment))) +
  geom_line(linewidth = 1.2) +
  geom_text(data = ~ filter(.x, year == 2024), aes(label = paste0(format(round(mensuel), big.mark = " "), " €")),
            hjust = -0.2, size = 3.6, show.legend = FALSE) +
  facet_wrap(~ groupe, scales = "free_y") +
  scale_color_manual(values = cols_def, name = NULL) +
  scale_x_continuous(breaks = seq(1996, 2024, 8), expand = expansion(mult = c(0.02, 0.2))) +
  scale_y_continuous(labels = ~ format(.x, big.mark = " ")) +
  theme_nv() + theme(strip.text = element_text(face = "bold", hjust = 0)) +
  labs(x = NULL, y = "Euros 2024 par mois",
       title = "Niveau de vie mensuel moyen : 830 € pour les plus modestes, 6 400 € pour les plus aisés",
       subtitle = "euros 2024 constants par mois et par unité de consommation, IPC (série Insee) et IPCH (calcul)")

Code
ggplot(deciles |> mutate(decile = factor(decile, levels = ordre_deciles)),
       aes(x = year, y = value / 12, color = decile, group = interaction(decile, segment))) +
  geom_line(linewidth = 1) +
  geom_text(data = ~ filter(.x, year == max(year)),
            aes(label = paste0(decile, " : ", format(round(value / 12), big.mark = " "), " €")),
            hjust = -0.1, size = 3.2, show.legend = FALSE) +
  scale_color_manual(values = pal, guide = "none") +
  scale_x_continuous(breaks = seq(1996, 2024, 4), expand = expansion(mult = c(0.02, 0.2))) +
  scale_y_log10(breaks = c(800, 1000, 1500, 2000, 3000, 5000)) +
  theme_nv() +
  labs(x = NULL, y = "Euros 2024 par mois (échelle logarithmique)",
       title = "Niveau de vie mensuel moyen par tranche de déciles",
       subtitle = "euros 2024 constants par mois, série publiée (IPC)")

Code
# Seuils de pauvreté mensuels (même convention de ruptures que reve-pauv-seuil-mensuel.qmd)
seuils <- read_parquet("reve-pauv-seuil-mensuel.parquet") |>
  filter(unite == "Euros 2024 constants", taux %in% c(50, 60)) |>
  group_by(taux) |>
  mutate(segment = cumsum(str_detect(coalesce(notes, ""), "[1235]"))) |>
  ungroup() |>
  filter(year >= 1996) |>
  transmute(year, segment, serie = paste0("Seuil de pauvreté à ", taux, " %"), mensuel = value)
bas_m <- niv |> filter(decile == "D1-") |>
  transmute(year, segment, serie = "Niveau de vie moyen des 10 % les moins aisés", mensuel = value / 12)
ggplot() +
  geom_line(data = seuils, aes(x = year, y = mensuel, color = serie, group = interaction(serie, segment)), linewidth = 1.1) +
  geom_line(data = bas_m, aes(x = year, y = mensuel, color = serie, group = segment), linewidth = 1.4) +
  geom_text(data = bind_rows(seuils, bas_m) |> filter(year == 2024) |> group_by(serie) |> slice_tail(n = 1) |> ungroup(),
            aes(x = year, y = mensuel, color = serie, label = paste0(format(round(mensuel), big.mark = " "), " €")),
            hjust = -0.2, size = 3.6, show.legend = FALSE) +
  scale_color_manual(values = c("Niveau de vie moyen des 10 % les moins aisés" = rose,
                                "Seuil de pauvreté à 50 %" = jaune, "Seuil de pauvreté à 60 %" = bleu), name = NULL) +
  scale_x_continuous(breaks = seq(1996, 2024, 4), expand = expansion(mult = c(0.02, 0.1))) +
  scale_y_continuous(labels = ~ format(.x, big.mark = " ")) +
  theme_nv() + guides(color = guide_legend(nrow = 2)) +
  labs(x = NULL, y = "Euros 2024 par mois",
       title = "Les 10 % les moins aisés vivent en moyenne sous le seuil de pauvreté à 60 %",
       subtitle = "niveau de vie mensuel moyen du 1er décile et seuils de pauvreté mensuels (euros 2024 constants, IPC)")

Séries temporelles en base 100

Code
niv |>
  filter(decile %in% c("D1-", "D4-D5", "D8-D9", "D9+", "Ensemble")) |>
  group_by(decile) |>
  mutate(indice = 100 * value / value[year == 1996][1]) |>
  ungroup() |>
  mutate(decile = factor(decile, levels = c("D9+", "D8-D9", "Ensemble", "D4-D5", "D1-"))) |>
  ggplot(aes(x = year, y = indice, color = decile, group = interaction(decile, segment))) +
  geom_hline(yintercept = 100, color = "grey60", linetype = "dashed") +
  geom_line(linewidth = 1.1) +
  geom_text(data = ~ filter(.x, year == 2024), aes(label = sprintf("%s : %.0f", decile, indice)),
            hjust = -0.1, size = 3.4, show.legend = FALSE) +
  scale_color_manual(values = c("D9+" = bleu, "D8-D9" = "#8FB0DE", "Ensemble" = "grey35",
                                "D4-D5" = jaune, "D1-" = rose), guide = "none") +
  scale_x_continuous(breaks = seq(1996, 2024, 4), expand = expansion(mult = c(0.02, 0.18))) +
  theme_nv() +
  labs(x = NULL, y = "Indice, base 100 en 1996",
       title = "Le haut de la distribution décolle, le bas stagne depuis 2002",
       subtitle = "niveau de vie moyen en euros 2024 constants (IPC), base 100 en 1996, tranches de déciles choisies")

Qui a gagné depuis 1996 ?

Code
croissance <- function(df, d, f) {
  a <- df |> filter(year == d, annee_brut == as.character(d)) |> select(decile, v0 = value)
  b <- df |> filter(year == f, annee_brut == as.character(f)) |> select(decile, v1 = value)
  inner_join(a, b, by = "decile") |> mutate(periode = sprintf("%d-%d", d, f), g = (v1 / v0)^(1 / (f - d)) - 1)
}
gains <- bind_rows(croissance(deciles, 1996, 2008), croissance(deciles, 2008, 2019), croissance(deciles, 2019, 2024)) |>
  mutate(decile = factor(decile, levels = ordre_deciles))
ggplot(gains, aes(x = decile, y = g, fill = periode)) +
  geom_col(position = position_dodge(width = 0.8), width = 0.75) +
  geom_hline(yintercept = 0, color = "grey25") +
  scale_y_continuous(labels = scales::percent_format(accuracy = 0.1)) +
  scale_fill_manual(values = c("1996-2008" = bleu, "2008-2019" = rose, "2019-2024" = vert), name = NULL) +
  theme_nv() +
  labs(x = NULL, y = "Croissance annuelle moyenne",
       title = "Hausse jusqu'en 2008, stagnation ensuite, reprise depuis 2019 sauf pour le 1er décile",
       subtitle = "croissance annuelle moyenne du niveau de vie en euros constants, par décile et par période")

Code
croissance(deciles, 1996, 2024) |>
  mutate(decile = factor(decile, levels = ordre_deciles)) |>
  ggplot(aes(x = decile, y = v1 / v0 - 1)) +
  geom_col(fill = bleu, width = 0.7) +
  geom_text(aes(label = sprintf("%+.0f %%", 100 * (v1 / v0 - 1))), vjust = -0.4, size = 3.6) +
  scale_y_continuous(labels = scales::percent_format(accuracy = 1), expand = expansion(mult = c(0, 0.12))) +
  theme_nv() +
  labs(x = NULL, y = NULL, title = "Évolution cumulée du niveau de vie moyen entre 1996 et 2024",
       subtitle = "en euros constants, par tranche de déciles")

Le rapport entre le haut et le bas

Code
rap <- niv |> filter(type == "Rapport (B)/(A)")
rup <- rap |> filter(!is.na(notes))
ggplot(rap, aes(x = year, y = value)) +
  geom_line(aes(group = segment), color = rose, linewidth = 1.2) +
  geom_point(color = rose, size = 1.8) +
  geom_point(data = rup, shape = 21, size = 3.2, fill = "white", color = "grey25", stroke = 1) +
  ggrepel::geom_text_repel(data = rup, aes(label = sprintf("%s (note %s)", year, notes)), size = 3,
                           color = "grey30", nudge_y = -0.5, segment.color = "grey70") +
  scale_x_continuous(breaks = seq(1996, 2024, 4), expand = expansion(mult = c(0.02, 0.05))) +
  scale_y_continuous(limits = c(5, NA)) +
  theme_nv() +
  labs(x = NULL, y = "Rapport (B)/(A)",
       title = "Les 10 % les plus aisés ont un niveau de vie\nmoyen près de 8 fois supérieur aux 10 % les plus modestes",
       subtitle = "rapport entre le niveau de vie moyen au-dessus du 9e décile (B) et sous le 1er décile (A)\ncercles blancs : points de rupture de série")

Les données

Code
niv |>
  filter(annee_brut == as.character(year), year %in% c(1996, 2008, 2019, 2024)) |>
  select(tranche, year, value) |>
  mutate(value = round(value, 1)) |>
  pivot_wider(names_from = year, values_from = value) |>
  rename(`Tranche de niveau de vie` = tranche) |>
  print_table_conditional()
Tranche de niveau de vie 1996 2008 2019 2024
Inférieur au 1er décile 8530.0 10380.0 10020.0 9960.0
Entre le 1er et le 2e décile 12510.0 15260.0 15300.0 15890.0
Entre le 2e et le 3e décile 15100.0 18250.0 18530.0 19400.0
Entre le 3e et le 4e décile 17290.0 20770.0 21410.0 22430.0
Entre le 4e et le 5e décile 19520.0 23170.0 23980.0 25310.0
Entre le 5e et le 6e décile 21900.0 25770.0 26730.0 28290.0
Entre le 6e et le 7e décile 24720.0 28800.0 29870.0 31670.0
Entre le 7e et le 8e décile 28350.0 32890.0 33990.0 36040.0
Entre le 8e et le 9e décile 34160.0 39860.0 40610.0 43010.0
Supérieur au 9e décile 52870.0 69200.0 69200.0 76970.0
Ensemble 23490.0 28430.0 28960.0 30900.0
Rapport (B)/(A) 6.2 6.7 6.9 7.7
Code
`reve-niv-vie-decile` |>
  group_by(year, annee_brut) |>
  summarise(Nobs = n(), .groups = "drop") |>
  arrange(desc(year), annee_brut) |>
  print_table_conditional()