Salaires dans le secteur privé - Distribution depuis 1996, en euros constants IPC et IPCH

Données - INSEE

Éditions de l’Insee Première

L’histogramme par tranche de 100 euros apparaît pour la première fois dans l’IP1798 (salaires de 2017, paru le 16 avril 2020). Avant, les fichiers de données ne donnent que des déciles, avec les P95 et P99 (depuis l’édition 2010), et des centiles (depuis l’édition 2011).

Code
editions |>
  transmute(`Insee Première` = paste0("IP", ip), `Salaires de` = annee, `Paru le` = parution,
            Histogramme = if_else(is.na(histogramme), "", "oui"),
            Centiles = if_else(is.na(centiles), "", "oui"),
            Champ = champ) |>
  print_table_conditional()
Insee Première Salaires de Paru le Histogramme Centiles Champ
IP1403 2010 2012-06-26 2011-2020 : privé et entreprises publiques, hors apprentis et stagiaires
IP1471 2011 2013-10-31 oui 2011-2020 : privé et entreprises publiques, hors apprentis et stagiaires
IP1528 2012 2014-12-16 oui 2011-2020 : privé et entreprises publiques, hors apprentis et stagiaires
IP1565 2013 2015-09-16 oui 2011-2020 : privé et entreprises publiques, hors apprentis et stagiaires
IP1617 2014 2016-09-21 oui 2011-2020 : privé et entreprises publiques, hors apprentis et stagiaires
IP1669 2015 2017-10-12 oui 2011-2020 : privé et entreprises publiques, hors apprentis et stagiaires
IP1750 2016 2019-04-23 oui 2011-2020 : privé et entreprises publiques, hors apprentis et stagiaires
IP1798 2017 2020-04-16 oui oui 2011-2020 : privé et entreprises publiques, hors apprentis et stagiaires
IP1828 2018 2020-12-04 oui oui 2011-2020 : privé et entreprises publiques, hors apprentis et stagiaires
IP1863 2019 2021-06-15 oui oui 2011-2020 : privé et entreprises publiques, hors apprentis et stagiaires
IP1898 2020 2022-04-26 oui oui 2011-2020 : privé et entreprises publiques, hors apprentis et stagiaires
IP1938 2021 2023-02-16 oui oui 2021-2023 : privé, hors apprentis et stagiaires
IP1971 2022 2023-11-08 oui oui 2021-2023 : privé, hors apprentis et stagiaires
IP2020 2023 2024-10-23 oui oui 2021-2023 : privé, hors apprentis et stagiaires
IP2079 2024 2025-10-23 oui oui 2024-2025 : privé, y compris apprentis et stagiaires
IP2132 2025 NA oui oui 2024-2025 : privé, y compris apprentis et stagiaires

Attention aux ruptures de champ. Les histogrammes et les centiles publiés dans chaque édition ne sont comparables qu’à l’intérieur d’un même champ :

  • 2010-2020 : privé et entreprises publiques, hors apprentis et stagiaires (contrats aidés inclus à partir de 2012, contrats de professionnalisation à partir de 2015) ;
  • 2021-2023 : privé seulement, hors apprentis et stagiaires, France hors Mayotte ;
  • 2024-2025 : privé, y compris apprentis et stagiaires, Mayotte incluse.

Sur cette page, les histogrammes et centiles publiés ne sont donc comparés qu’au sein d’un même champ. Pour comparer des années éloignées (1996, 2008, 2017… avec 2025), on utilise uniquement les séries longues de l’Insee. Elles sont rétropolées sur un champ homogène qui inclut les apprentis et les stagiaires depuis 1996.

Effet de l’entrée des apprentis et stagiaires en 2024 (IP2079)

Pour une même année (2024), les quantiles sont nettement plus bas avec les apprentis et les stagiaires, surtout en bas de la distribution. Comparer un centile de 2023 (ou de 2017) à un centile de 2025 mélange donc l’évolution des salaires avec ce changement de champ.

Code
champ_2024 |>
  filter(type == "Quantile") |>
  transmute(Quantile = quantile,
            `Sans apprentis ni stagiaires, hors Mayotte` = sans_niveau,
            `Avec apprentis et stagiaires, y c. Mayotte` = avec_niveau,
            `Écart (%)` = round(100 * (avec_niveau / sans_niveau - 1), 1)) |>
  print_table_conditional()
Quantile Sans apprentis ni stagiaires, hors Mayotte Avec apprentis et stagiaires, y c. Mayotte Écart (%)
1er décile (D1) 1556 1492 -4.1
2e décile 1721 1669 -3.0
3e décile 1875 1823 -2.8
4e décile 2045 1992 -2.6
Médiane 2247 2190 -2.5
6e décile 2503 2442 -2.4
7e décile 2850 2785 -2.3
8e décile 3378 3305 -2.2
9e décile (D9) 4422 4334 -2.0
95e centile 5704 5593 -1.9
99e centile 10478 10261 -2.1
Moyenne 2814 2733 -2.9

Indices de prix : IPC et IPCH

Code
prix |>
  group_by(deflateur) |>
  mutate(indice = 100 * prix / first(prix)) |>
  ggplot(aes(x = annee, y = indice, color = deflateur)) +
  geom_line(linewidth = 1.1) +
  scale_color_manual(values = couleurs_deflateurs, name = NULL) +
  scale_x_continuous(breaks = seq(1996, 2026, 4)) +
  scale_y_continuous(breaks = seq(100, 200, 5)) +
  theme_minimal(base_size = 12) +
  labs(x = NULL, y = NULL, title = "Indices de prix à la consommation, France",
       subtitle = "Base 100 en 1996") +
  theme(legend.position = "bottom", panel.grid.minor = element_blank())

Code
prix_wide |>
  filter(annee %in% c(1996, 2008, 2017, 2019, 2021, 2025)) |>
  mutate(across(c(IPC, IPCH), ~ round(100 * .x / .x[annee == 1996], 1), .names = "{.col} (base 100 en 1996)"),
         `Écart IPCH - IPC (points)` = `IPCH (base 100 en 1996)` - `IPC (base 100 en 1996)`) |>
  select(-IPC, -IPCH) |>
  print_table_conditional()
annee IPC (base 100 en 1996) IPCH (base 100 en 1996) Écart IPCH - IPC (points)
1996 100.0 100.0 0.0
2008 121.4 123.2 1.8
2017 131.9 135.4 3.5
2019 135.8 140.1 4.3
2021 138.7 143.7 5.0
2025 157.6 166.1 8.5

Déciles et centiles depuis 1996, en euros constants

Les séries longues (EQTP08) portent sur un champ homogène depuis 1996 : privé et entreprises publiques, y compris apprentis et stagiaires, données rétropolées par l’Insee. Les comparaisons de cette section entre années éloignées sont donc à champ constant. Ces séries vont jusqu’en 2023. Elles sont prolongées en 2024 et en 2025 avec les évolutions en euros constants à champ constant publiées dans l’IP2079 et l’IP2132. Ces évolutions sont reconverties en euros courants avec l’IPC, puis redéflatées par l’IPC ou par l’IPCH. P25 et P75 s’arrêtent en 2023.

Vérification : reconstitution de la figure 5 de l’IP2132

Les indices reconstitués avec l’IPC doivent retrouver ceux de l’Insee.

Code
series |>
  filter(deflateur == "IPC", indicateur %in% c("D1", "D5", "D9"), annee %in% c(2008, 2017, 2023, 2024, 2025)) |>
  transmute(annee, indicateur = recode(indicateur, D5 = "Mediane"), reconstitue = round(reel_1996, 1)) |>
  left_join(ip2132_fig5 |> rename(insee = value), by = c("annee", "indicateur")) |>
  mutate(ecart = round(reconstitue - insee, 1)) |>
  print_table_conditional()
annee indicateur reconstitue insee ecart
2008 D1 111.5 111.6 -0.1
2008 Mediane 106.1 106.1 0.0
2008 D9 106.3 106.3 0.0
2017 D1 115.1 115.1 0.0
2017 Mediane 110.5 110.5 0.0
2017 D9 113.1 113.1 0.0
2023 D1 116.7 116.7 0.0
2023 Mediane 111.8 111.8 0.0
2023 D9 112.5 112.5 0.0
2024 D1 117.8 117.8 0.0
2024 Mediane 112.7 112.7 0.0
2024 D9 113.3 113.3 0.0
2025 D1 118.9 119.0 -0.1
2025 Mediane 114.2 114.1 0.1
2025 D9 115.1 115.1 0.0

Indices base 100 en 1996

Code
series |>
  filter(quantile %in% names(couleurs_quantiles)) |>
  mutate(deflateur = paste0("Déflateur : ", deflateur)) |>
  ggplot(aes(x = annee, y = reel_1996, color = quantile)) +
  geom_hline(yintercept = 100, color = "grey60", linewidth = 0.3) +
  geom_line(linewidth = 1) +
  facet_wrap(~ deflateur) +
  scale_color_manual(values = couleurs_quantiles, name = NULL) +
  scale_x_continuous(breaks = c(1996, 2005, 2015, 2025)) +
  scale_y_continuous(breaks = seq(90, 150, 5)) +
  theme_minimal(base_size = 12) +
  labs(x = NULL, y = NULL,
       title = "Salaire net en EQTP, en euros constants",
       subtitle = "Base 100 en 1996, par quantile") +
  theme(legend.position = "bottom", panel.grid.minor = element_blank())

Écart entre les deux déflateurs, pour la médiane et le D9

Code
series |>
  filter(quantile %in% c("Médiane", "D9", "P99")) |>
  ggplot(aes(x = annee, y = reel_1996, color = deflateur)) +
  geom_hline(yintercept = 100, color = "grey60", linewidth = 0.3) +
  geom_line(linewidth = 1) +
  facet_wrap(~ quantile) +
  scale_color_manual(values = couleurs_deflateurs, name = "Déflateur") +
  scale_x_continuous(breaks = seq(1996, 2026, 10)) +
  scale_y_continuous(breaks = seq(90, 150, 5)) +
  theme_minimal(base_size = 12) +
  labs(x = NULL, y = NULL, title = "Salaire net en EQTP, en euros constants",
       subtitle = "Base 100 en 1996") +
  theme(legend.position = "bottom", panel.grid.minor = element_blank())

Évolutions cumulées, en euros constants

Code
periodes <- bind_rows(map(c(1996L, 2008L, 2017L, 2019L, 2021L), evol_periode))

periodes |>
  filter(quantile %in% names(couleurs_quantiles)) |>
  ggplot(aes(x = quantile, y = evolution, fill = deflateur)) +
  geom_hline(yintercept = 0, color = "grey40", linewidth = 0.4) +
  geom_col(position = position_dodge(width = 0.8), width = 0.75) +
  geom_text(aes(label = format(round(evolution, 1), decimal.mark = ","),
                vjust = if_else(evolution >= 0, -0.3, 1.2)),
            position = position_dodge(width = 0.8), size = 2.6) +
  facet_wrap(~ periode, ncol = 2, scales = "free_y") +
  scale_fill_manual(values = couleurs_deflateurs, name = "Déflateur") +
  scale_y_continuous(expand = expansion(mult = c(0.15, 0.15))) +
  theme_minimal(base_size = 12) +
  labs(x = NULL, y = NULL,
       title = "Évolution du salaire net en EQTP jusqu'en 2025, en euros constants",
       subtitle = "en %") +
  theme(legend.position = "bottom", panel.grid.minor = element_blank(),
        panel.grid.major.x = element_blank())

Code
periodes |>
  mutate(evolution = round(evolution, 1)) |>
  pivot_wider(names_from = quantile, values_from = evolution) |>
  arrange(periode, deflateur) |>
  print_table_conditional()
deflateur periode D1 Médiane D9 P95 P99 Moyenne
IPC 1996-2025 18.9 14.2 15.1 10.2 10.3 15.5
IPCH 1996-2025 12.9 8.4 9.2 4.6 4.7 9.6
IPC 2008-2025 6.6 7.6 8.3 4.8 2.7 7.1
IPCH 2008-2025 2.7 3.6 4.3 1.0 -1.1 3.2
IPC 2017-2025 3.3 3.3 1.8 0.7 0.5 2.5
IPCH 2017-2025 0.7 0.7 -0.8 -1.9 -2.0 -0.1
IPC 2019-2025 1.9 0.7 0.6 0.3 1.3 1.2
IPCH 2019-2025 -0.3 -1.5 -1.6 -1.9 -0.8 -1.0
IPC 2021-2025 1.8 0.0 -0.8 -1.4 -1.2 0.0
IPCH 2021-2025 0.1 -1.7 -2.4 -3.0 -2.9 -1.6

Évolution réelle par quantile depuis 1996

Évolution du salaire net en EQTP jusqu’en 2025, en euros constants, pour chaque quantile de la distribution (D1, médiane, D9, P95, P99), selon l’année de départ.

Code
rangs <- c("D1" = 10, "Médiane" = 50, "D9" = 90, "P95" = 95, "P99" = 99)

periodes |>
  filter(quantile %in% names(rangs), periode %in% c("1996-2025", "2008-2025", "2017-2025", "2021-2025")) |>
  mutate(quantile = factor(quantile, levels = names(rangs))) |>
  ggplot(aes(x = quantile, y = evolution, color = deflateur, group = deflateur)) +
  geom_hline(yintercept = 0, color = "grey40", linewidth = 0.4) +
  geom_line(linewidth = 1.1) +
  geom_point(size = 2) +
  geom_text(aes(label = format(round(evolution, 1), decimal.mark = ",")),
            vjust = -0.9, size = 2.8, show.legend = FALSE) +
  facet_wrap(~ periode, ncol = 2, scales = "free_y") +
  scale_color_manual(values = couleurs_deflateurs, name = "Déflateur") +
  scale_y_continuous(expand = expansion(mult = c(0.1, 0.2))) +
  theme_minimal(base_size = 12) +
  labs(x = NULL, y = NULL,
       title = "Évolution du salaire net en EQTP par quantile, en euros constants",
       subtitle = "en %, jusqu'en 2025") +
  theme(legend.position = "bottom", panel.grid.minor = element_blank())

Évolution réelle par quantile, 1996-2025, année par année

Chaque courbe donne, pour une année de départ, l’évolution réelle jusqu’en 2025 (déflateur : IPCH).

Code
series |>
  filter(deflateur == "IPCH", quantile %in% names(rangs)) |>
  group_by(quantile) |>
  mutate(evolution = 100 * (reel_1996[annee == 2025] / reel_1996 - 1)) |>
  ungroup() |>
  filter(annee < 2025) |>
  ggplot(aes(x = annee, y = evolution, color = quantile)) +
  geom_hline(yintercept = 0, color = "grey40", linewidth = 0.4) +
  geom_line(linewidth = 1) +
  scale_color_manual(values = couleurs_quantiles, name = NULL) +
  scale_x_continuous(breaks = seq(1996, 2024, 4)) +
  scale_y_continuous(breaks = seq(-10, 20, 2)) +
  theme_minimal(base_size = 12) +
  labs(x = "Année de départ", y = NULL,
       title = "Évolution du salaire net en EQTP entre l'année de départ et 2025",
       subtitle = "en %, en euros constants (déflateur : IPCH)") +
  theme(legend.position = "bottom", panel.grid.minor = element_blank())

Niveaux en euros courants

Code
series |>
  filter(deflateur == "IPC") |>
  select(annee, quantile, nominal) |>
  mutate(nominal = round(nominal)) |>
  pivot_wider(names_from = quantile, values_from = nominal) |>
  arrange(desc(annee)) |>
  print_table_conditional()
annee P95 P99 D1 Médiane D9 Moyenne P25 P75
2025 5744 10590 1521 2240 4445 2801 NA NA
2024 5595 10265 1492 2191 4335 2734 NA NA
2023 5458 10023 1449 2131 4220 2658 1697 2929
2022 5300 9784 1379 2043 4083 2560 1618 2816
2021 5125 9436 1315 1972 3943 2464 1552 2719
2020 5179 9506 1302 1974 3981 2471 1549 2738
2019 4936 9007 1286 1918 3809 2386 1515 2644
2018 4887 9072 1256 1850 3741 2335 1467 2556
2017 4774 8816 1232 1814 3655 2286 1438 2504
2016 4695 8662 1209 1780 3583 2242 1413 2453
2015 4663 8597 1205 1770 3560 2229 1409 2437
2014 4599 8468 1198 1756 3513 2203 1400 2410
2013 4537 8366 1192 1745 3460 2180 1393 2386
2012 4504 8325 1188 1731 3434 2167 1386 2363
2011 4442 8237 1165 1708 3375 2134 1364 2326
2010 4352 8046 1138 1668 3280 2083 1332 2269
2009 4262 7879 1122 1638 3217 2041 1311 2228
2008 4222 7943 1099 1604 3164 2015 1282 2182
2007 4100 7653 1063 1551 3078 1954 1240 2115
2006 3981 7379 1040 1511 2989 1894 1209 2060
2005 3922 7231 1025 1486 2946 1860 1187 2028
2004 3827 7063 987 1446 2862 1809 1148 1978
2003 3751 6879 957 1418 2806 1769 1120 1943
2002 3686 6742 937 1388 2753 1732 1097 1903
2001 3639 6710 912 1354 2706 1696 1067 1860
2000 3556 6655 882 1325 2635 1658 1040 1827
1999 3471 6412 869 1307 2580 1624 1024 1797
1998 3386 6194 855 1290 2522 1591 1010 1765
1997 3343 6167 833 1269 2484 1566 988 1736
1996 3307 6092 811 1245 2451 1538 966 1704

Histogrammes 2017-2025

Effectifs en EQTP par tranche de 100 euros de salaire mensuel net, d’après la figure de chaque édition. Les tranches ouvertes (« moins de… », « … ou plus ») sont exclues. Ces histogrammes ne sont pas rétropolés : on ne compare les années qu’à l’intérieur d’un même champ (2017-2020, 2021-2023, 2024-2025).

En euros courants

Code
histogrammes |>
  group_by(annee) |>
  mutate(part = 100 * effectifs_milliers / sum(effectifs_milliers)) |>
  filter(!is.na(borne_inf), !is.na(borne_sup), borne_inf < 6000) |>
  ggplot(aes(x = borne_inf + 50, y = part, fill = champ)) +
  geom_col(width = 100, color = "white", linewidth = 0.1) +
  facet_wrap(~ annee, ncol = 3) +
  scale_fill_manual(values = c(bleu_clair, "#4A72B8", bleu) |> setNames(sort(unique(histogrammes$champ))), name = NULL) +
  scale_x_continuous(breaks = seq(1000, 6000, 1000), labels = ~ format(.x, big.mark = " ")) +
  guides(fill = guide_legend(ncol = 1)) +
  theme_minimal(base_size = 11) +
  labs(x = "Salaire mensuel net (euros courants)", y = "% des EQTP",
       title = "Distribution des salaires mensuels nets en EQTP",
       subtitle = "Tranches de 100 euros, en % des effectifs en EQTP") +
  theme(legend.position = "bottom", panel.grid.minor = element_blank())

En euros constants de 2025 (IPCH), à champ constant

Chaque tranche est convertie en euros de 2025 avec l’IPCH. Pour que les années restent comparables, la hauteur est une densité : la part des EQTP pour 100 euros de 2025. Chaque panneau ne compare que des années de même champ.

Code
histogrammes |>
  group_by(annee) |>
  mutate(part = 100 * effectifs_milliers / sum(effectifs_milliers)) |>
  filter(!is.na(borne_inf), !is.na(borne_sup)) |>
  left_join(prix_wide |> transmute(annee, coef = IPCH[annee == 2025] / IPCH), by = "annee") |>
  mutate(milieu = (borne_inf + 50) * coef,
         densite = part / coef) |>
  filter(milieu < 6500) |>
  ungroup() |>
  ggplot(aes(x = milieu, y = densite, color = factor(annee))) +
  geom_line(linewidth = 0.9) +
  facet_wrap(~ champ, ncol = 1) +
  scale_color_manual(values = c("2017" = "#C6D7EE", "2018" = "#8FB0DE", "2019" = "#4A72B8", "2020" = "#1B3F8B",
                                "2021" = "#C6D7EE", "2022" = "#4A72B8", "2023" = "#1B3F8B",
                                "2024" = "#8FB0DE", "2025" = rouge), name = NULL) +
  scale_x_continuous(breaks = seq(1000, 7000, 500), labels = ~ format(.x, big.mark = " ")) +
  theme_minimal(base_size = 12) +
  labs(x = "Salaire mensuel net (euros de 2025, IPCH)", y = "% des EQTP pour 100 euros",
       title = "Distribution des salaires mensuels nets en EQTP, à champ constant",
       subtitle = "Euros constants de 2025 (déflateur : IPCH)") +
  theme(legend.position = "bottom", panel.grid.minor = element_blank(),
        axis.text.x = element_text(angle = 45, hjust = 1))

Log-log (Pareto), en euros constants de 2025 (IPCH), à champ constant

Code
histogrammes |>
  group_by(annee) |>
  mutate(total = sum(effectifs_milliers)) |>
  filter(!is.na(borne_inf)) |>
  arrange(desc(borne_inf), .by_group = TRUE) |>
  mutate(part_au_dessus = cumsum(effectifs_milliers) / total) |>
  ungroup() |>
  left_join(prix_wide |> transmute(annee, coef = IPCH[annee == 2025] / IPCH), by = "annee") |>
  filter(borne_inf >= 2000) |>
  ggplot(aes(x = borne_inf * coef, y = part_au_dessus, color = factor(annee))) +
  geom_line(linewidth = 0.8) +
  facet_wrap(~ champ, ncol = 1) +
  scale_x_log10(breaks = c(2000, 3000, 4000, 5000, 7000, 10000), labels = ~ format(.x, big.mark = " ")) +
  scale_y_log10(breaks = c(0.005, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5),
                labels = scales::percent_format(accuracy = 0.1, decimal.mark = ",")) +
  scale_color_manual(values = c("2017" = "#C6D7EE", "2018" = "#8FB0DE", "2019" = "#4A72B8", "2020" = "#1B3F8B",
                                "2021" = "#C6D7EE", "2022" = "#4A72B8", "2023" = "#1B3F8B",
                                "2024" = "#8FB0DE", "2025" = rouge), name = NULL) +
  theme_minimal(base_size = 12) +
  labs(x = "Salaire mensuel net s (euros de 2025, IPCH, échelle log)",
       y = "Part des EQTP ≥ s (log)",
       title = "Queue de distribution des salaires mensuels nets en EQTP, à champ constant",
       subtitle = "Échelle log-log (Pareto), euros constants de 2025 (déflateur : IPCH)") +
  theme(legend.position = "bottom", panel.grid.minor = element_blank())

Coefficients de Pareto

La pente de la queue (α, régression à partir de 3 000 euros courants) est calculée année par année. Ajouter des salariés payés sous le seuil (apprentis, stagiaires) multiplie toutes les parts au-dessus du seuil par un même facteur : la droite se déplace mais sa pente ne change pas. α reste donc comparable malgré les ruptures de champ, contrairement aux niveaux des parts.

Code
histogrammes |>
  group_by(annee, champ) |>
  mutate(total = sum(effectifs_milliers)) |>
  filter(!is.na(borne_inf)) |>
  arrange(desc(borne_inf), .by_group = TRUE) |>
  mutate(part_au_dessus = cumsum(effectifs_milliers) / total) |>
  filter(borne_inf >= 3000) |>
  summarise(alpha = -coef(lm(log(part_au_dessus) ~ log(borne_inf)))[2], .groups = "drop") |>
  mutate(alpha = round(alpha, 3)) |>
  print_table_conditional()
annee champ alpha
2017 2011-2020 : privé et entreprises publiques, hors apprentis et stagiaires 2.671
2018 2011-2020 : privé et entreprises publiques, hors apprentis et stagiaires 2.601
2019 2011-2020 : privé et entreprises publiques, hors apprentis et stagiaires 2.672
2020 2011-2020 : privé et entreprises publiques, hors apprentis et stagiaires 2.635
2021 2021-2023 : privé, hors apprentis et stagiaires 2.635
2022 2021-2023 : privé, hors apprentis et stagiaires 2.641
2023 2021-2023 : privé, hors apprentis et stagiaires 2.662
2024 2024-2025 : privé, y compris apprentis et stagiaires 2.668
2025 2024-2025 : privé, y compris apprentis et stagiaires 2.657

Histogrammes reconstitués à partir des centiles, 2011-2025

L’Insee n’a pas publié d’histogramme par tranche de 100 euros avant 2017. Ni les Insee Première, ni les Tableaux de l’économie française, ni les séries longues n’en contiennent : on n’y trouve que des déciles. En revanche, les centiles P5 à P99 sont publiés depuis 2011. Entre deux centiles, on interpole linéairement la fonction de répartition, ce qui revient à une densité uniforme dans chaque intervalle. On en déduit la part des EQTP dans chaque tranche de 100 euros, entre P5 et P99.

Code
histo_centiles <- function(df) {
  df <- df |> arrange(centile)
  repartition <- approxfun(df$salaire_net, df$centile / 100, ties = mean)
  bornes <- seq(ceiling(min(df$salaire_net) / 100) * 100, floor(max(df$salaire_net) / 100) * 100 - 100, 100)
  tibble(borne_inf = bornes,
         part = 100 * (repartition(bornes + 100) - repartition(bornes)))
}

reconstitues <- centiles |>
  group_by(annee, champ) |>
  group_modify(~ histo_centiles(.x)) |>
  ungroup()

Validation sur 2017 et 2025, où l’on dispose des deux

Code
histogrammes |>
  group_by(annee) |>
  mutate(part = 100 * effectifs_milliers / sum(effectifs_milliers)) |>
  ungroup() |>
  filter(annee %in% c(2017, 2025), !is.na(borne_inf), !is.na(borne_sup)) |>
  select(annee, borne_inf, `Publié (Insee)` = part) |>
  inner_join(reconstitues |> select(-champ) |> rename(`Reconstitué (centiles)` = part), by = c("annee", "borne_inf")) |>
  pivot_longer(-c(annee, borne_inf), names_to = "source", values_to = "part") |>
  filter(borne_inf < 6000) |>
  ggplot(aes(x = borne_inf + 50, y = part, color = source)) +
  geom_line(linewidth = 0.9) +
  facet_wrap(~ annee, ncol = 1) +
  scale_color_manual(values = c("Publié (Insee)" = bleu, "Reconstitué (centiles)" = rouge), name = NULL) +
  scale_x_continuous(breaks = seq(1000, 6000, 500), labels = ~ format(.x, big.mark = " ")) +
  theme_minimal(base_size = 12) +
  labs(x = "Salaire mensuel net (euros courants)", y = "% des EQTP",
       title = "Histogramme publié et histogramme reconstitué à partir des centiles",
       subtitle = "Tranches de 100 euros, en % des effectifs en EQTP") +
  theme(legend.position = "bottom", panel.grid.minor = element_blank())

En euros constants de 2025 (IPCH), à champ constant

Code
reconstitues |>
  filter(annee %in% c(2011, 2014, 2017, 2020, 2021, 2023, 2024, 2025)) |>
  left_join(prix_wide |> transmute(annee, coef = IPCH[annee == 2025] / IPCH), by = "annee") |>
  mutate(milieu = (borne_inf + 50) * coef,
         densite = part / coef) |>
  filter(milieu < 6500) |>
  ggplot(aes(x = milieu, y = densite, color = factor(annee))) +
  geom_line(linewidth = 0.9) +
  facet_wrap(~ champ, ncol = 1) +
  scale_color_manual(values = c("2011" = "#C6D7EE", "2014" = "#8FB0DE", "2017" = "#4A72B8", "2020" = "#1B3F8B",
                                "2021" = "#8FB0DE", "2023" = "#1B3F8B",
                                "2024" = "#8FB0DE", "2025" = rouge), name = NULL) +
  scale_x_continuous(breaks = seq(1000, 7000, 500), labels = ~ format(.x, big.mark = " ")) +
  theme_minimal(base_size = 12) +
  labs(x = "Salaire mensuel net (euros de 2025, IPCH)", y = "% des EQTP pour 100 euros",
       title = "Distribution reconstituée à partir des centiles, à champ constant",
       subtitle = "Salaires mensuels nets en EQTP, euros de 2025 (IPCH), entre P5 et P99") +
  theme(legend.position = "bottom", panel.grid.minor = element_blank(),
        axis.text.x = element_text(angle = 45, hjust = 1))

En euros courants, 2011-2016

Code
reconstitues |>
  filter(annee <= 2016, borne_inf < 6000) |>
  ggplot(aes(x = borne_inf + 50, y = part)) +
  geom_col(width = 100, fill = bleu_clair, color = "white", linewidth = 0.1) +
  facet_wrap(~ annee, ncol = 3) +
  scale_x_continuous(breaks = seq(1000, 6000, 1000), labels = ~ format(.x, big.mark = " ")) +
  theme_minimal(base_size = 11) +
  labs(x = "Salaire mensuel net (euros courants)", y = "% des EQTP",
       title = "Distribution reconstituée à partir des centiles",
       subtitle = "Salaires mensuels nets en EQTP, tranches de 100 euros, entre P5 et P99") +
  theme(panel.grid.minor = element_blank())

Centiles publiés, en euros constants, à champ constant

Centiles des salaires mensuels nets en EQTP tels que publiés dans chaque édition. Chaque segment est en base 100 la première année de son champ (2011, 2021, 2024), et les segments ne sont pas reliés entre eux. Pour une évolution sur longue période à champ constant, voir les séries longues plus haut.

Code
centiles |>
  filter(centile %in% c(10, 25, 50, 75, 90, 95, 99)) |>
  crossing(deflateur = c("IPC", "IPCH")) |>
  left_join(prix, by = c("deflateur", "annee")) |>
  group_by(centile, deflateur, champ) |>
  arrange(annee) |>
  mutate(reel = 100 * (salaire_net / first(salaire_net)) / (prix / first(prix))) |>
  ungroup() |>
  mutate(centile = factor(paste0("P", centile), levels = paste0("P", c(10, 25, 50, 75, 90, 95, 99))),
         deflateur = paste0("Déflateur : ", deflateur)) |>
  ggplot(aes(x = annee, y = reel, color = centile, group = interaction(centile, champ))) +
  geom_vline(xintercept = c(2020.5, 2023.5), linetype = "dashed", color = "grey60") +
  geom_hline(yintercept = 100, color = "grey60", linewidth = 0.3) +
  geom_line(linewidth = 0.9) +
  geom_point(size = 0.8) +
  facet_wrap(~ deflateur) +
  scale_color_manual(values = colorRampPalette(c("#C6D7EE", "#1B3F8B", rouge))(7), name = NULL) +
  scale_x_continuous(breaks = seq(2011, 2025, 2)) +
  scale_y_continuous(breaks = seq(90, 120, 2)) +
  theme_minimal(base_size = 12) +
  labs(x = NULL, y = NULL, title = "Centiles du salaire mensuel net en EQTP, en euros constants",
       subtitle = "Base 100 la première année de chaque champ (2011, 2021, 2024)") +
  theme(legend.position = "bottom", panel.grid.minor = element_blank())

Données

Séries longues (EQTP08), ensemble, euros courants annuels

Code
SLS2025_EQTP08 |>
  filter(sexe == "Ensemble") |>
  pivot_wider(names_from = indicateur, values_from = value) |>
  arrange(desc(annee)) |>
  print_table_conditional()
annee sexe MOYENNE D1 Q1 D5 Q3 D9 C95 C99 ECART1 ECART2 ECART3
2023 Ensemble 31902 17392 20368 25569 35147 50637 65496 120280 2.91 1.98 1.47
2022 Ensemble 30718 16549 19422 24515 33797 48995 63596 117410 2.96 2.00 1.48
2021 Ensemble 29569 15783 18630 23668 32632 47320 61504 113238 3.00 2.00 1.50
2020 Ensemble 29649 15619 18590 23684 32853 47776 62145 114068 3.06 2.02 1.52
2019 Ensemble 28631 15431 18176 23010 31723 45708 59237 108087 2.96 1.99 1.49
2018 Ensemble 28024 15066 17601 22202 30678 44888 58639 108868 2.98 2.02 1.47
2017 Ensemble 27426 14783 17252 21769 30048 43864 57285 105792 2.97 2.01 1.47
2016 Ensemble 26901 14513 16961 21364 29432 43001 56340 103938 2.96 2.01 1.47
2015 Ensemble 26743 14463 16905 21234 29244 42719 55953 103166 2.95 2.01 1.47
2014 Ensemble 26435 14375 16797 21072 28915 42156 55189 101621 2.93 2.00 1.47
2013 Ensemble 26166 14302 16711 20943 28636 41514 54441 100390 2.90 1.98 1.46
2012 Ensemble 26000 14257 16634 20776 28361 41211 54046 99896 2.89 1.98 1.46
2011 Ensemble 25611 13979 16367 20495 27917 40504 53306 98842 2.90 1.98 1.47
2010 Ensemble 24992 13652 15989 20018 27231 39366 52230 96546 2.88 1.97 1.47
2009 Ensemble 24497 13469 15731 19655 26742 38607 51144 94544 2.87 1.96 1.46
2008 Ensemble 24176 13189 15387 19253 26186 37966 50669 95317 2.88 1.97 1.46
2007 Ensemble 23454 12756 14886 18617 25380 36935 49202 91834 2.90 1.98 1.46
2006 Ensemble 22733 12479 14510 18135 24716 35864 47775 88548 2.87 1.98 1.45
2005 Ensemble 22323 12297 14246 17836 24335 35346 47059 86767 2.87 1.98 1.45
2004 Ensemble 21705 11845 13777 17346 23730 34349 45923 84760 2.90 1.98 1.46
2003 Ensemble 21224 11481 13434 17017 23314 33671 45009 82548 2.93 1.98 1.48
2002 Ensemble 20786 11249 13160 16654 22833 33036 44228 80904 2.94 1.98 1.48
2001 Ensemble 20356 10948 12803 16243 22318 32467 43673 80523 2.97 2.00 1.48
2000 Ensemble 19893 10585 12474 15904 21920 31625 42670 79859 2.99 1.99 1.50
1999 Ensemble 19484 10429 12283 15681 21569 30959 41654 76950 2.97 1.97 1.50
1998 Ensemble 19088 10264 12121 15483 21180 30259 40636 74330 2.95 1.95 1.51
1997 Ensemble 18795 9995 11855 15223 20829 29813 40121 74007 2.98 1.96 1.52
1996 Ensemble 18459 9737 11596 14941 20451 29416 39680 73098 3.02 1.97 1.53