Last data update: 10 Nov 2026, 09:04
Last compile: 11 Nov 2026, 05:37
Champ : France, salariés du privé, y compris alternants, stagiaires et bénéficiaires de contrats aidés, hors salariés de l’agriculture et des particuliers employeurs.
Source : Insee, base Tous salariés 2025 ; séries longues sur les salaires.
fig1 |>
mutate(categorie = fct_inorder(categorie),
dimension = fct_inorder(dimension)) |>
ggplot(aes(x = fct_rev(categorie), y = net_2025, fill = categorie == "Ensemble")) +
geom_col(width = 0.7) +
geom_text(aes(label = format(net_2025, big.mark = " ")), hjust = -0.15, size = 3.5) +
coord_flip(clip = "off") +
facet_grid(dimension ~ ., scales = "free_y", space = "free_y") +
scale_fill_manual(values = c("FALSE" = bleu, "TRUE" = rouge), guide = "none") +
scale_y_continuous(expand = expansion(mult = c(0, 0.12))) +
theme_minimal(base_size = 12) +
labs(x = NULL, y = "Euros par mois",
title = "Salaire mensuel net moyen en EQTP, en 2025") +
theme(panel.grid.minor = element_blank(),
panel.grid.major.y = element_blank(),
strip.text.y = element_text(angle = 0, hjust = 0))
fig1 |>
mutate(categorie = fct_inorder(categorie),
dimension = fct_inorder(dimension)) |>
select(dimension, categorie,
`Salaire net moyen en EQTP` = net_evol_constants,
`RMPP nette` = rmpp_net_evol_constants) |>
pivot_longer(-c(dimension, categorie), names_to = "indicateur", values_to = "evolution") |>
mutate(indicateur = factor(indicateur, levels = c("RMPP nette", "Salaire net moyen en EQTP"))) |>
ggplot(aes(x = fct_rev(categorie), y = evolution, fill = indicateur)) +
geom_col(position = position_dodge(width = 0.75), width = 0.7) +
coord_flip() +
facet_grid(dimension ~ ., scales = "free_y", space = "free_y") +
scale_fill_manual(values = c("Salaire net moyen en EQTP" = bleu, "RMPP nette" = bleu_clair), name = NULL,
breaks = c("Salaire net moyen en EQTP", "RMPP nette")) +
theme_minimal(base_size = 12) +
labs(x = NULL, y = NULL,
title = "Évolution 2025/2024 des salaires nets, en euros constants",
subtitle = "en % ; RMPP = rémunération moyenne des personnes en place") +
theme(legend.position = "bottom",
panel.grid.minor = element_blank(),
panel.grid.major.y = element_blank(),
strip.text.y = element_text(angle = 0, hjust = 0))
fig1 |>
select(-dimension) |>
print_table_conditional()| categorie | brut_2025 | brut_evol_courants | brut_evol_constants | net_2025 | net_evol_courants | net_evol_constants | part_effectifs | rmpp_net_evol_constants |
|---|---|---|---|---|---|---|---|---|
| Cadres | 6342 | 2.2 | 1.2 | 4711 | 2.1 | 1.1 | 23.5 | 3.3 |
| Professions intermédiaires | 3545 | 2.1 | 1.1 | 2681 | 1.9 | 1.0 | 20.5 | 2.7 |
| Employés | 2553 | 2.2 | 1.2 | 1979 | 2.1 | 1.1 | 27.8 | 2.6 |
| Ouvriers | 2717 | 2.3 | 1.3 | 2092 | 2.0 | 1.1 | 28.2 | 2.2 |
| Femmes | 3410 | 2.8 | 1.9 | 2578 | 2.7 | 1.7 | 42.0 | 2.9 |
| Hommes | 3898 | 2.5 | 1.6 | 2956 | 2.4 | 1.4 | 58.0 | 2.8 |
| Industrie | 4173 | 2.7 | 1.8 | 3094 | 2.4 | 1.5 | 16.3 | 2.6 |
| Construction | 3245 | 3.4 | 2.4 | 2487 | 3.2 | 2.3 | 8.0 | 3.0 |
| Tertiaire | 3637 | 2.6 | 1.6 | 2766 | 2.4 | 1.5 | 75.7 | 2.9 |
| Ensemble | 3693 | 2.6 | 1.7 | 2797 | 2.5 | 1.5 | 100.0 | 2.8 |
plot_fig2 <- fig2 |>
ggplot(aes(x = annee, y = moyenne)) +
geom_line(color = bleu, linewidth = 1.2) +
geom_point(color = bleu, size = 1.5) +
geom_hline(yintercept = 100, color = "grey60", linetype = "dashed") +
scale_x_continuous(breaks = seq(1996, 2026, 2)) +
scale_y_continuous(breaks = seq(90, 130, 2)) +
theme_minimal(base_size = 12) +
labs(x = NULL, y = NULL,
title = "Salaire net moyen en EQTP, en euros constants",
subtitle = "Indice base 100 en 1996 - Échelle linéaire") +
theme(panel.grid.minor = element_blank())
plot_fig2
plot_fig2 +
scale_y_log10(breaks = seq(90, 130, 2)) +
labs(subtitle = "Indice base 100 en 1996 - Échelle logarithmique")
tc4 |>
ggplot(aes(x = annee, y = evol_salaire_net, fill = evol_salaire_net > 0)) +
geom_col() +
geom_hline(yintercept = 0, color = "grey40", linewidth = 0.4) +
geom_text(aes(label = evol_salaire_net,
vjust = if_else(evol_salaire_net >= 0, -0.4, 1.3)), size = 3) +
scale_fill_manual(values = c("TRUE" = bleu, "FALSE" = rouge), guide = "none") +
scale_x_continuous(breaks = seq(1997, 2025, 2)) +
theme_minimal(base_size = 12) +
labs(x = NULL, y = NULL,
title = "Évolution annuelle du salaire net moyen en EQTP, en euros constants",
subtitle = "en %") +
theme(panel.grid.minor = element_blank(),
panel.grid.major.x = element_blank())
fig3 |>
mutate(centre = coalesce((borne_inf + borne_sup) / 2, borne_inf + 50, borne_sup - 50)) |>
ggplot(aes(x = centre, y = effectifs_milliers)) +
geom_col(fill = bleu, width = 90) +
scale_x_continuous(breaks = seq(1000, 9000, 1000),
labels = scales::label_number(big.mark = " ")) +
theme_minimal(base_size = 12) +
labs(x = "Salaire mensuel net (en euros)", y = "Milliers d'EQTP",
title = "Distribution des salaires mensuels nets en EQTP, en 2025",
subtitle = "Tranches de 100 euros\npremière barre : moins de 1 300 euros ; dernière : 9 000 euros ou plus") +
theme(panel.grid.minor = element_blank(),
panel.grid.major.x = element_blank())
tc3 |>
ggplot(aes(x = centile, y = salaire_net)) +
geom_line(color = bleu, linewidth = 1.2) +
scale_x_continuous(breaks = seq(0, 100, 10)) +
scale_y_log10(breaks = c(1500, 2000, 3000, 4000, 5000, 7500, 10000),
labels = scales::label_number(big.mark = " ")) +
theme_minimal(base_size = 12) +
labs(x = "Centile", y = "Euros par mois",
title = "Salaire mensuel net par centile, en 2025",
subtitle = "Échelle logarithmique") +
theme(panel.grid.minor = element_blank())
Part des salariés dont le salaire mensuel net est supérieur ou égal à \(s\), en fonction de \(s\), les deux axes en échelle logarithmique. Si la queue de distribution est de Pareto, \(P(S \geq s) \propto s^{-\alpha}\) et les points sont alignés sur une droite de pente \(-\alpha\). Points : bornes inférieures des tranches de la figure 3 ; droite : régression sur les tranches à partir de 3 000 euros.
N_total <- sum(fig3$effectifs_milliers)
pareto <- fig3 |>
filter(!is.na(borne_inf)) |>
arrange(borne_inf) |>
mutate(part_au_dessus = rev(cumsum(rev(effectifs_milliers))) / N_total)
seuil_pareto <- 3000
fit_pareto <- lm(log(part_au_dessus) ~ log(borne_inf), data = filter(pareto, borne_inf >= seuil_pareto))
alpha <- -coef(fit_pareto)[2]
pareto |>
mutate(ajuste = exp(predict(fit_pareto, newdata = pick(everything()))),
ajuste = if_else(ajuste <= 1, ajuste, NA_real_)) |>
ggplot(aes(x = borne_inf, y = part_au_dessus)) +
geom_point(color = bleu, size = 1.5) +
geom_line(aes(y = ajuste), color = rouge, linewidth = 0.8, linetype = "dashed", na.rm = TRUE) +
annotate("text", x = 5000, y = 0.3, color = rouge, hjust = 0, size = 4,
label = paste0("Pente : -", format(round(alpha, 2), decimal.mark = ","),
"\nR² = ", format(sprintf("%.4f", summary(fit_pareto)$r.squared)) |> str_replace("\\.", ","))) +
scale_x_log10(breaks = c(1300, 1500, 2000, 3000, 4000, 5000, 7000, 9000),
labels = scales::label_number(big.mark = " ")) +
scale_y_log10(breaks = c(0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1),
labels = scales::label_percent(accuracy = 1)) +
theme_minimal(base_size = 12) +
labs(x = "Salaire mensuel net s (en euros)", y = "Part des EQTP avec un salaire ≥ s",
title = "Distribution des salaires mensuels nets en EQTP, en 2025",
subtitle = "Échelle log-log (Pareto) ; droite ajustée à partir de 3 000 euros") +
theme(panel.grid.minor = element_blank())
On ne compare la distribution de 2025 qu’à celle de 2024 (IP2079), qui a le même champ : privé, y compris apprentis et stagiaires. Les histogrammes publiés jusqu’en 2023 excluent les apprentis et stagiaires. Ils ne sont donc pas comparables à ceux de 2025 (voir plus bas, et la page sur toutes les éditions).
part_au_dessus <- function(f) {
N <- sum(f$effectifs_milliers)
f |>
filter(!is.na(borne_inf)) |>
arrange(borne_inf) |>
mutate(part_au_dessus = rev(cumsum(rev(effectifs_milliers))) / N)
}
medianes <- c("2024" = champ_2024 |> filter(quantile == "Médiane") |> pull(avec_niveau),
"2025" = fig4 |> filter(sexe == "Ensemble", quantile == "Médiane") |> pull(niveau))
pareto_2024_2025 <- bind_rows(
part_au_dessus(histogrammes |> filter(annee == 2024)) |> mutate(annee = "2024"),
part_au_dessus(fig3) |> mutate(annee = "2025")
) |>
mutate(relatif = borne_inf / medianes[annee])pareto_2024_2025 |>
ggplot(aes(x = borne_inf, y = part_au_dessus, color = annee)) +
geom_point(size = 1.3) +
scale_color_manual(values = c("2024" = bleu_clair, "2025" = bleu), name = NULL) +
scale_x_log10(breaks = c(1300, 1500, 2000, 3000, 4000, 5000, 7000, 9000),
labels = scales::label_number(big.mark = " ")) +
scale_y_log10(breaks = c(0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1),
labels = scales::label_percent(accuracy = 1)) +
theme_minimal(base_size = 12) +
labs(x = "Salaire mensuel net s (en euros courants)", y = "Part des EQTP avec un salaire ≥ s",
title = "Distribution des salaires mensuels nets en EQTP, 2024 et 2025",
subtitle = "Échelle log-log (Pareto), euros courants, même champ") +
theme(legend.position = "bottom",
panel.grid.minor = element_blank())
Diviser par la médiane de chaque année (2 190 euros en 2024, 2 238 euros en 2025) neutralise la hausse générale des salaires. Si les courbes se superposent, la dispersion relative des salaires n’a pas changé.
pareto_2024_2025 |>
ggplot(aes(x = relatif, y = part_au_dessus, color = annee)) +
geom_point(size = 1.3) +
scale_color_manual(values = c("2024" = bleu_clair, "2025" = bleu), name = NULL) +
scale_x_log10(breaks = c(0.6, 0.8, 1, 1.5, 2, 3, 4)) +
scale_y_log10(breaks = c(0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1),
labels = scales::label_percent(accuracy = 1)) +
theme_minimal(base_size = 12) +
labs(x = "Salaire mensuel net s / salaire médian", y = "Part des EQTP avec un salaire ≥ s",
title = "Distribution des salaires mensuels nets en EQTP, 2024 et 2025",
subtitle = "Échelle log-log (Pareto), en multiples du salaire médian de l'année") +
theme(legend.position = "bottom",
panel.grid.minor = element_blank())
La pente α de la queue peut, elle, être comparée malgré les changements de champ. 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 log-log se déplace, mais sa pente ne change pas.
alpha_pareto <- function(d, seuil) {
m <- lm(log(part_au_dessus) ~ log(borne_inf), data = filter(d, borne_inf >= seuil))
tibble(alpha = -unname(coef(m)[2]))
}
histogrammes |>
group_by(annee, champ) |>
group_modify(~ part_au_dessus(.x)) |>
ungroup() |>
nest(data = -c(annee, champ)) |>
crossing(seuil = c(3000, 4000, 5000)) |>
mutate(res = map2(data, seuil, alpha_pareto)) |>
unnest(res) |>
select(-data) |>
mutate(alpha = round(alpha, 3), seuil = paste0("α (à partir de ", seuil, " euros)")) |>
pivot_wider(names_from = seuil, values_from = alpha) |>
print_table_conditional()| annee | champ | α (à partir de 3000 euros) | α (à partir de 4000 euros) | α (à partir de 5000 euros) |
|---|---|---|---|---|
| 2017 | 2011-2020 : privé et entreprises publiques, hors apprentis et stagiaires | 2.671 | 2.671 | 2.652 |
| 2018 | 2011-2020 : privé et entreprises publiques, hors apprentis et stagiaires | 2.601 | 2.615 | 2.607 |
| 2019 | 2011-2020 : privé et entreprises publiques, hors apprentis et stagiaires | 2.672 | 2.685 | 2.680 |
| 2020 | 2011-2020 : privé et entreprises publiques, hors apprentis et stagiaires | 2.635 | 2.659 | 2.659 |
| 2021 | 2021-2023 : privé, hors apprentis et stagiaires | 2.635 | 2.652 | 2.644 |
| 2022 | 2021-2023 : privé, hors apprentis et stagiaires | 2.641 | 2.664 | 2.659 |
| 2023 | 2021-2023 : privé, hors apprentis et stagiaires | 2.662 | 2.684 | 2.673 |
| 2024 | 2024-2025 : privé, y compris apprentis et stagiaires | 2.668 | 2.694 | 2.684 |
| 2025 | 2024-2025 : privé, y compris apprentis et stagiaires | 2.657 | 2.683 | 2.667 |
Les centiles publiés en 2017 (IP1798) et en 2025 ne sont pas comparables. En 2017, le champ exclut les apprentis et stagiaires, mais inclut les entreprises publiques. En 2025, il inclut les apprentis et stagiaires, mais pas les entreprises publiques. L’IP2079 mesure l’effet de l’entrée des apprentis et stagiaires (et de Mayotte) en 2024. Peu payés, ils se concentrent sous le D1 et font baisser tous les quantiles, car chaque rang se décale :
champ_2024 |>
filter(type == "Quantile") |>
transmute(Quantile = quantile,
`2024, sans apprentis ni stagiaires` = sans_niveau,
`2024, avec apprentis et stagiaires` = avec_niveau,
`Effet du champ (%)` = round(100 * (avec_niveau / sans_niveau - 1), 1)) |>
print_table_conditional()| Quantile | 2024, sans apprentis ni stagiaires | 2024, avec apprentis et stagiaires | Effet du champ (%) |
|---|---|---|---|
| 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 |
On utilise donc les séries longues de l’Insee. Elles portent sur un champ homogène, y compris apprentis et stagiaires, rétropolé depuis 1996, et sont prolongées en 2024 et 2025 par les évolutions à champ constant de l’IP2079 et de l’IP2132. Les prix ont augmenté de 22,6 % entre 2017 et 2025 selon l’IPCH, et de 19,5 % selon l’IPC (ensemble des ménages), le déflateur des « euros constants » de l’Insee.
evol_quantiles_2017 <- series |>
filter(annee %in% c(2017, 2025), indicateur %in% c("D1", "D5", "D9", "C95", "C99", "MOYENNE")) |>
select(annee, indicateur, nominal) |>
pivot_wider(names_from = annee, values_from = nominal, names_prefix = "salaire_net_") |>
crossing(prix |> select(indice, deflateur)) |>
mutate(evolution = 100 * (salaire_net_2025 / (salaire_net_2017 * deflateur) - 1),
quantile = factor(recode(indicateur, D5 = "Médiane", C95 = "P95", C99 = "P99", MOYENNE = "Moyenne"),
levels = c("D1", "Médiane", "D9", "P95", "P99", "Moyenne")),
indice = paste0("Déflateur : ", indice))
evol_quantiles_2017 |>
filter(quantile != "Moyenne") |>
ggplot(aes(x = quantile, y = evolution, color = indice, group = indice)) +
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 = 3, show.legend = FALSE) +
scale_color_manual(values = c("Déflateur : IPCH" = bleu, "Déflateur : IPC" = bleu_clair), name = NULL) +
scale_y_continuous(breaks = seq(-10, 10, 1), expand = expansion(mult = c(0.1, 0.2))) +
theme_minimal(base_size = 12) +
labs(x = NULL, y = NULL,
title = "Évolution du salaire mensuel net en EQTP entre 2017 et 2025",
subtitle = "en %, en euros constants, à champ constant (séries longues de l'Insee)") +
theme(legend.position = "bottom",
panel.grid.minor = element_blank())
evol_quantiles_2017 |>
select(quantile, salaire_net_2017, salaire_net_2025, indice, evolution) |>
mutate(across(starts_with("salaire"), round), evolution = round(evolution, 1)) |>
pivot_wider(names_from = indice, values_from = evolution) |>
arrange(quantile) |>
print_table_conditional()| quantile | salaire_net_2017 | salaire_net_2025 | Déflateur : IPC | Déflateur : IPCH |
|---|---|---|---|---|
| D1 | 1232 | 1521 | 3.3 | 0.7 |
| Médiane | 1814 | 2240 | 3.3 | 0.7 |
| D9 | 3655 | 4445 | 1.8 | -0.8 |
| P95 | 4774 | 5744 | 0.7 | -1.9 |
| P99 | 8816 | 10590 | 0.5 | -2.0 |
| Moyenne | 2286 | 2801 | 2.5 | -0.1 |
La figure reprend la présentation centile par centile (en abscisse, le centile ; en ordonnée, l’évolution réelle entre 2017 et 2025) et superpose trois mesures :
correction_champ <- champ_2024 |>
filter(type == "Quantile", quantile != "Moyenne") |>
mutate(centile = c(10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99),
effet = avec_niveau / sans_niveau)
prix_2017_2025 <- prix |> select(indice, deflateur)
publies <- centiles_editions |>
filter(annee %in% c(2017, 2025)) |>
select(annee, centile, salaire_net) |>
pivot_wider(names_from = annee, values_from = salaire_net, names_prefix = "s") |>
mutate(effet = approx(correction_champ$centile, correction_champ$effet, xout = centile)$y) |>
crossing(prix_2017_2025) |>
transmute(centile, indice,
`Centiles publiés, sans correction` = 100 * (s2025 / (s2017 * deflateur) - 1),
`Centiles publiés, corrigés des apprentis` = 100 * (s2025 / effet / (s2017 * deflateur) - 1)) |>
pivot_longer(starts_with("Centiles"), names_to = "mesure", values_to = "evolution") |>
filter(!is.na(evolution))
longues <- evol_quantiles_2017 |>
filter(quantile != "Moyenne") |>
mutate(centile = c(D1 = 10, Médiane = 50, D9 = 90, P95 = 95, P99 = 99)[as.character(quantile)],
indice = str_remove(indice, "Déflateur : "),
mesure = "Séries longues de l'Insee (champ constant)")
bind_rows(publies, longues |> select(centile, indice, mesure, evolution)) |>
mutate(indice = paste0("Déflateur : ", indice),
mesure = factor(mesure, levels = c("Centiles publiés, sans correction",
"Centiles publiés, corrigés des apprentis",
"Séries longues de l'Insee (champ constant)"))) |>
ggplot(aes(x = centile, y = evolution, color = mesure, linetype = mesure)) +
geom_hline(yintercept = 0, color = "grey40", linewidth = 0.4) +
geom_line(linewidth = 1) +
geom_point(data = ~ filter(.x, mesure == "Séries longues de l'Insee (champ constant)"), size = 2.5) +
facet_wrap(~ indice) +
scale_color_manual(values = c("Centiles publiés, sans correction" = "grey65",
"Centiles publiés, corrigés des apprentis" = bleu_clair,
"Séries longues de l'Insee (champ constant)" = rouge), name = NULL) +
scale_linetype_manual(values = c("Centiles publiés, sans correction" = "solid",
"Centiles publiés, corrigés des apprentis" = "dashed",
"Séries longues de l'Insee (champ constant)" = "solid"), name = NULL) +
scale_x_continuous(breaks = c(5, seq(10, 90, 10), 99)) +
scale_y_continuous(breaks = seq(-6, 6, 1)) +
guides(color = guide_legend(ncol = 1), linetype = guide_legend(ncol = 1)) +
theme_minimal(base_size = 12) +
labs(x = "Centile de salaire net en EQTP", y = NULL,
title = "Salaire net en EQTP par centile, évolution entre 2017 et 2025",
subtitle = "en %, en euros constants") +
theme(legend.position = "bottom", panel.grid.minor = element_blank())
Jusqu’en 2023, les séries longues donnent aussi P25 et P75 et ne demandent aucun prolongement.
SLS2025_EQTP08 |>
filter(sexe == "Ensemble", annee %in% c(2017, 2023),
indicateur %in% c("D1", "Q1", "D5", "Q3", "D9", "C95", "C99")) |>
pivot_wider(names_from = annee, values_from = value, names_prefix = "s") |>
crossing(prix_2017_2023 = c("IPC", "IPCH")) |>
left_join(
bind_rows(
read_parquet("IPCH-2025.parquet") |>
filter(COICOP2018 == "00", NATURE == "INDICE", REF_AREA == "FE", FREQ == "A") |> mutate(indice = "IPCH"),
read_parquet("IPC-2025.parquet") |>
filter(COICOP2018 == "00", NATURE == "INDICE", REF_AREA == "FE", FREQ == "A",
MENAGES_IPC == "ENSEMBLE", PRIX_CONSO == "SO") |> mutate(indice = "IPC")
) |>
filter(TIME_PERIOD %in% c("2017", "2023")) |>
select(indice, TIME_PERIOD, OBS_VALUE) |>
pivot_wider(names_from = TIME_PERIOD, values_from = OBS_VALUE) |>
transmute(prix_2017_2023 = indice, deflateur = `2023` / `2017`),
by = "prix_2017_2023") |>
mutate(centile = c(D1 = 10, Q1 = 25, D5 = 50, Q3 = 75, D9 = 90, C95 = 95, C99 = 99)[indicateur],
evolution = 100 * (s2023 / (s2017 * deflateur) - 1),
prix_2017_2023 = paste0("Déflateur : ", prix_2017_2023)) |>
ggplot(aes(x = centile, y = evolution, color = prix_2017_2023)) +
geom_hline(yintercept = 0, color = "grey40", linewidth = 0.4) +
geom_line(linewidth = 1.1) +
geom_point(size = 2.5) +
geom_text(aes(label = format(round(evolution, 1), decimal.mark = ",")), vjust = -1, size = 3, show.legend = FALSE) +
scale_color_manual(values = c("Déflateur : IPC" = bleu_clair, "Déflateur : IPCH" = bleu), name = NULL) +
scale_x_continuous(breaks = c(10, 25, 50, 75, 90, 95, 99)) +
scale_y_continuous(breaks = seq(-6, 6, 1), expand = expansion(mult = c(0.1, 0.15))) +
theme_minimal(base_size = 12) +
labs(x = "Centile de salaire net en EQTP", y = NULL,
title = "Salaire net en EQTP par centile, évolution entre 2017 et 2023",
subtitle = "en %, en euros constants, séries longues de l'Insee (champ constant)") +
theme(legend.position = "bottom", panel.grid.minor = element_blank())
Il s’agit des mêmes séries longues, prolongées en 2024 et 2025 par les évolutions en euros constants à champ constant publiées dans l’IP2079 (2024/2023) et l’IP2132 (2025/2024). Ces publications ne donnent pas P25 et P75 : leur évolution est approchée par la moyenne de celles des déciles voisins (D2 et D3 pour P25, D7 et D8 pour P75). Ces deux points sont dessinés en creux.
indices_prix <- bind_rows(
read_parquet("IPCH-2025.parquet") |>
filter(COICOP2018 == "00", NATURE == "INDICE", REF_AREA == "FE", FREQ == "A") |> mutate(indice = "IPCH"),
read_parquet("IPC-2025.parquet") |>
filter(COICOP2018 == "00", NATURE == "INDICE", REF_AREA == "FE", FREQ == "A",
MENAGES_IPC == "ENSEMBLE", PRIX_CONSO == "SO") |> mutate(indice = "IPC")
) |>
transmute(indice, annee = as.integer(TIME_PERIOD), p = as.numeric(OBS_VALUE)) |>
pivot_wider(names_from = indice, values_from = p)
ratio <- function(x, a, b) x[indices_prix$annee == b] / x[indices_prix$annee == a]
# Évolution réelle (IPC) 2023 -> 2025 à champ constant, par quantile
evol_2023_2025 <- evol_quantiles_ip |>
select(annee, indicateur, evol_constants) |>
pivot_wider(names_from = annee, values_from = evol_constants, names_prefix = "e") |>
mutate(facteur = (1 + e2024 / 100) * (1 + e2025 / 100)) |>
select(indicateur, facteur)
evol_2023_2025 <- bind_rows(
evol_2023_2025 |> filter(indicateur %in% c("D1", "D5", "D9", "C95", "C99")) |> mutate(approche = FALSE),
tibble(indicateur = c("Q1", "Q3"),
facteur = c(mean(evol_2023_2025$facteur[evol_2023_2025$indicateur %in% c("D2", "D3")]),
mean(evol_2023_2025$facteur[evol_2023_2025$indicateur %in% c("D7", "D8")])),
approche = TRUE)
)
SLS2025_EQTP08 |>
filter(sexe == "Ensemble", annee %in% c(2017, 2023),
indicateur %in% c("D1", "Q1", "D5", "Q3", "D9", "C95", "C99")) |>
pivot_wider(names_from = annee, values_from = value, names_prefix = "s") |>
inner_join(evol_2023_2025, by = "indicateur") |>
mutate(reel_ipc = (s2023 / s2017) / ratio(indices_prix$IPC, 2017, 2023) * facteur,
IPC = 100 * (reel_ipc - 1),
IPCH = 100 * (reel_ipc * ratio(indices_prix$IPC, 2017, 2025) / ratio(indices_prix$IPCH, 2017, 2025) - 1),
centile = c(D1 = 10, Q1 = 25, D5 = 50, Q3 = 75, D9 = 90, C95 = 95, C99 = 99)[indicateur]) |>
pivot_longer(c(IPC, IPCH), names_to = "deflateur", values_to = "evolution") |>
mutate(deflateur = paste0("Déflateur : ", deflateur)) |>
ggplot(aes(x = centile, y = evolution, color = deflateur)) +
geom_hline(yintercept = 0, color = "grey40", linewidth = 0.4) +
geom_line(linewidth = 1.1) +
geom_point(aes(shape = approche), size = 2.5, stroke = 1.1, fill = "white") +
geom_text(aes(label = format(round(evolution, 1), decimal.mark = ",")), vjust = -1, size = 3, show.legend = FALSE) +
scale_color_manual(values = c("Déflateur : IPC" = bleu_clair, "Déflateur : IPCH" = bleu), name = NULL) +
scale_shape_manual(values = c("FALSE" = 16, "TRUE" = 21), guide = "none") +
scale_x_continuous(breaks = c(10, 25, 50, 75, 90, 95, 99)) +
scale_y_continuous(breaks = seq(-6, 6, 1), expand = expansion(mult = c(0.1, 0.15))) +
theme_minimal(base_size = 12) +
labs(x = "Centile de salaire net en EQTP", y = NULL,
title = "Salaire net en EQTP par centile, évolution entre 2017 et 2025",
subtitle = "en %, en euros constants, séries longues de l'Insee prolongées (champ constant)") +
theme(legend.position = "bottom", panel.grid.minor = element_blank())
fig4 |>
filter(quantile != "Moyenne") |>
mutate(quantile = fct_reorder(quantile, ordre),
sexe = factor(sexe, levels = c("Femmes", "Hommes", "Ensemble"))) |>
ggplot(aes(x = quantile, y = niveau, color = sexe, group = sexe)) +
geom_line(linewidth = 1) +
geom_point(size = 2) +
scale_color_manual(values = c("Femmes" = rouge, "Hommes" = bleu, "Ensemble" = "grey50"), name = NULL) +
scale_y_log10(breaks = c(1500, 2000, 3000, 4000, 6000, 8000, 12000),
labels = scales::label_number(big.mark = " ")) +
theme_minimal(base_size = 12) +
labs(x = NULL, y = "Euros par mois",
title = "Salaire mensuel net en EQTP par quantile et par sexe, en 2025",
subtitle = "Échelle logarithmique") +
theme(legend.position = "bottom",
axis.text.x = element_text(angle = 45, hjust = 1),
panel.grid.minor = element_blank())
fig4 |>
mutate(quantile = fct_reorder(quantile, ordre),
sexe = factor(sexe, levels = c("Femmes", "Hommes", "Ensemble"))) |>
ggplot(aes(x = quantile, y = evol, fill = sexe)) +
geom_col(position = position_dodge(width = 0.8), width = 0.75) +
scale_fill_manual(values = c("Femmes" = rouge, "Hommes" = bleu, "Ensemble" = "grey60"), name = NULL) +
theme_minimal(base_size = 12) +
labs(x = NULL, y = NULL,
title = "Évolution 2025/2024 des salaires nets en EQTP, en euros constants",
subtitle = "en %") +
theme(legend.position = "bottom",
axis.text.x = element_text(angle = 45, hjust = 1),
panel.grid.minor = element_blank(),
panel.grid.major.x = element_blank())
plot_fig5 <- fig5 |>
filter(indicateur != "D9_D1") |>
mutate(indicateur = recode(indicateur, D1 = "1er décile (D1)", Mediane = "Médiane", D9 = "9e décile (D9)")) |>
ggplot(aes(x = annee, y = value, color = indicateur)) +
geom_line(linewidth = 1.2) +
geom_hline(yintercept = 100, color = "grey60", linetype = "dashed") +
scale_color_manual(values = c("1er décile (D1)" = rouge, "Médiane" = "grey40", "9e décile (D9)" = bleu), name = NULL) +
scale_x_continuous(breaks = seq(1996, 2026, 2)) +
scale_y_continuous(breaks = seq(90, 130, 2)) +
theme_minimal(base_size = 12) +
labs(x = NULL, y = NULL,
title = "Déciles des salaires nets en EQTP, en euros constants",
subtitle = "Indice base 100 en 1996 - Échelle linéaire") +
theme(legend.position = "bottom",
panel.grid.minor = element_blank())
plot_fig5
plot_fig5 +
scale_y_log10(breaks = seq(90, 130, 2)) +
labs(subtitle = "Indice base 100 en 1996 - Échelle logarithmique")
fig5 |>
filter(indicateur == "D9_D1") |>
ggplot(aes(x = annee, y = value)) +
geom_line(color = bleu, linewidth = 1.2) +
geom_point(color = bleu, size = 1.5) +
scale_x_continuous(breaks = seq(1996, 2026, 2)) +
scale_y_continuous(breaks = seq(2.6, 3.2, 0.05)) +
theme_minimal(base_size = 12) +
labs(x = NULL, y = NULL,
title = "Rapport interdécile D9/D1 des salaires nets en EQTP") +
theme(panel.grid.minor = element_blank())
tc1 |>
filter(sexe != "Ensemble", categorie != "Ensemble") |>
mutate(categorie = recode(categorie, Employées = "Employés", Ouvrières = "Ouvriers"),
categorie = fct_inorder(categorie)) |>
ggplot(aes(x = categorie, y = net_2025, fill = sexe)) +
geom_col(position = position_dodge(width = 0.8), width = 0.75) +
geom_text(aes(label = net_2025), position = position_dodge(width = 0.8), vjust = -0.4, size = 3.5) +
scale_fill_manual(values = c("Femmes" = rouge, "Hommes" = bleu), name = NULL) +
theme_minimal(base_size = 12) +
labs(x = NULL, y = "Euros par heure",
title = "Salaire horaire net moyen, en 2025",
subtitle = "selon le sexe et la catégorie socioprofessionnelle") +
theme(legend.position = "bottom",
panel.grid.minor = element_blank(),
panel.grid.major.x = element_blank())
tc1 |>
print_table_conditional()| sexe | categorie | brut_2024 | brut_2025 | brut_evol_constants | net_2024 | net_2025 | net_evol_constants |
|---|---|---|---|---|---|---|---|
| Femmes | Ensemble | 21.5 | 22.2 | 1.9 | 16.3 | 16.7 | 1.7 |
| Femmes | Cadres | 36.7 | 37.6 | 1.5 | 27.1 | 27.8 | 1.4 |
| Femmes | Professions intermédiaires | 21.2 | 21.6 | 1.2 | 16.0 | 16.3 | 1.1 |
| Femmes | Employées | 16.2 | 16.5 | 1.3 | 12.5 | 12.8 | 1.2 |
| Femmes | Ouvrières | 15.6 | 16.0 | 1.4 | 12.0 | 12.3 | 1.3 |
| Hommes | Ensemble | 24.5 | 25.1 | 1.6 | 18.6 | 19.0 | 1.4 |
| Hommes | Cadres | 42.8 | 43.7 | 1.1 | 31.9 | 32.5 | 1.0 |
| Hommes | Professions intermédiaires | 23.5 | 24.0 | 1.2 | 17.9 | 18.2 | 1.0 |
| Hommes | Employés | 16.1 | 16.5 | 1.1 | 12.6 | 12.9 | 1.0 |
| Hommes | Ouvriers | 17.4 | 17.8 | 1.3 | 13.4 | 13.7 | 1.1 |
| Ensemble | Ensemble | 23.2 | 23.9 | 1.7 | 17.6 | 18.1 | 1.6 |
| Ensemble | Cadres | 40.5 | 41.3 | 1.2 | 30.1 | 30.7 | 1.1 |
| Ensemble | Professions intermédiaires | 22.4 | 22.9 | 1.2 | 17.0 | 17.3 | 1.0 |
| Ensemble | Employés | 16.2 | 16.5 | 1.2 | 12.5 | 12.8 | 1.1 |
| Ensemble | Ouvriers | 17.1 | 17.4 | 1.3 | 13.2 | 13.4 | 1.1 |
tc2 |>
filter(sexe != "Ensemble", categorie != "Ensemble") |>
mutate(categorie = recode(categorie, Employées = "Employés", Ouvrières = "Ouvriers"),
categorie = fct_inorder(categorie)) |>
ggplot(aes(x = categorie, y = net_2025, fill = sexe)) +
geom_col(position = position_dodge(width = 0.8), width = 0.75) +
geom_text(aes(label = format(net_2025, big.mark = " ")),
position = position_dodge(width = 0.8), vjust = -0.4, size = 3.5) +
scale_fill_manual(values = c("Femmes" = rouge, "Hommes" = bleu), name = NULL) +
theme_minimal(base_size = 12) +
labs(x = NULL, y = "Euros par mois",
title = "Salaire mensuel net moyen en EQTP, en 2025",
subtitle = "selon le sexe et la catégorie socioprofessionnelle") +
theme(legend.position = "bottom",
panel.grid.minor = element_blank(),
panel.grid.major.x = element_blank())
tc2 |>
print_table_conditional()| sexe | categorie | brut_2024 | brut_2025 | brut_evol_constants | net_2024 | net_2025 | net_evol_constants | part_effectifs_2024 | part_effectifs_2025 |
|---|---|---|---|---|---|---|---|---|---|
| Femmes | Ensemble | 3316 | 3410 | 1.9 | 2511 | 2578 | 1.7 | 42.0 | 42.0 |
| Femmes | Cadres | 5621 | 5757 | 1.5 | 4162 | 4258 | 1.3 | 21.0 | 21.5 |
| Femmes | Professions intermédiaires | 3262 | 3331 | 1.2 | 2465 | 2515 | 1.1 | 23.1 | 23.0 |
| Femmes | Employées | 2493 | 2549 | 1.3 | 1927 | 1968 | 1.2 | 43.6 | 43.3 |
| Femmes | Ouvrières | 2406 | 2463 | 1.4 | 1850 | 1890 | 1.3 | 12.3 | 12.1 |
| Hommes | Ensemble | 3802 | 3898 | 1.6 | 2888 | 2956 | 1.4 | 58.0 | 58.0 |
| Hommes | Cadres | 6569 | 6708 | 1.2 | 4896 | 4994 | 1.0 | 24.5 | 24.9 |
| Hommes | Professions intermédiaires | 3660 | 3737 | 1.1 | 2778 | 2830 | 0.9 | 18.7 | 18.6 |
| Hommes | Employés | 2509 | 2560 | 1.1 | 1963 | 2000 | 1.0 | 16.5 | 16.6 |
| Hommes | Ouvriers | 2713 | 2773 | 1.3 | 2095 | 2137 | 1.1 | 40.3 | 39.8 |
| Ensemble | Ensemble | 3598 | 3693 | 1.7 | 2729 | 2797 | 1.5 | 100.0 | 100.0 |
| Ensemble | Cadres | 6206 | 6342 | 1.2 | 4615 | 4711 | 1.1 | 23.0 | 23.5 |
| Ensemble | Professions intermédiaires | 3472 | 3545 | 1.1 | 2630 | 2681 | 1.0 | 20.5 | 20.5 |
| Ensemble | Employés | 2499 | 2553 | 1.2 | 1939 | 1979 | 1.1 | 27.9 | 27.8 |
| Ensemble | Ouvriers | 2657 | 2717 | 1.3 | 2050 | 2092 | 1.1 | 28.6 | 28.2 |
tc5 |>
mutate(categorie = fct_inorder(categorie),
annee = factor(annee)) |>
ggplot(aes(x = categorie, y = part, fill = annee)) +
geom_col(position = position_dodge(width = 0.8), width = 0.75) +
geom_text(aes(label = part), position = position_dodge(width = 0.8), vjust = -0.4, size = 3.2) +
scale_fill_manual(values = c("2024" = bleu_clair, "2025" = bleu), name = NULL) +
theme_minimal(base_size = 12) +
labs(x = NULL, y = NULL,
title = "Part de salariés ayant perçu la PPV",
subtitle = "en %") +
theme(legend.position = "bottom",
panel.grid.minor = element_blank(),
panel.grid.major.x = element_blank())
tc5 |>
print_table_conditional()| categorie | annee | part | montant |
|---|---|---|---|
| Femmes | 2024 | 14.9 | 875 |
| Femmes | 2025 | 15.2 | 851 |
| Hommes | 2024 | 14.3 | 954 |
| Hommes | 2025 | 15.0 | 906 |
| Cadres | 2024 | 11.5 | 1223 |
| Cadres | 2025 | 13.1 | 1173 |
| Professions intermédiaires | 2024 | 17.3 | 895 |
| Professions intermédiaires | 2025 | 17.4 | 886 |
| Employés | 2024 | 14.1 | 831 |
| Employés | 2025 | 14.4 | 761 |
| Ouvriers | 2024 | 15.4 | 868 |
| Ouvriers | 2025 | 15.8 | 832 |
| Ensemble | 2024 | 14.6 | 918 |
| Ensemble | 2025 | 15.1 | 881 |