Indicateurs de valeur ajoutée des collèges (IVAC)

Données - DEPP (Éducation nationale)

Code
source(here::here("_rinit.R"))
source(here::here("code", "basic_functions.R"))   # dataset_info_lines(), related_data()
invisible(Sys.setlocale("LC_TIME", "fr_FR.UTF-8"))
suppressMessages(library(sf))
# cd ~/iCloud/website/data/depp; /usr/local/bin/R -e 'quarto::quarto_render("fr-en-indicateurs-valeur-ajoutee-colleges.qmd")'

knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE,
                      fig.width = 8, fig.height = 4.8, dpi = 150)

ivac_path <- here::here("data", "depp", "fr-en-indicateurs-valeur-ajoutee-colleges.parquet")
ivac <- arrow::read_parquet(ivac_path) |>
  dplyr::mutate(
    annee   = as.integer(format(session, "%Y")),
    secteur = dplyr::recode(secteur, PU = "Public", PR = "Privé sous contrat"),
    arr     = dplyr::if_else(grepl("^PARIS \\d{2}$", commune),
                             as.integer(sub("PARIS ", "", commune)), NA_integer_),
    # nom court : on retire "COLLEGE (PRIVE) " et on passe en casse de titre
    college = stringr::str_to_title(sub("^COLL[EÈ]GE (PRIV[EÉ] )?", "",
                                        nom_de_l_etablissement))
  )

an <- max(ivac$annee)                       # dernière session (2025)
paris   <- dplyr::filter(ivac, departement == "PARIS", !is.na(arr))
paris14 <- dplyr::filter(paris, arr == 14)

theme_depp <- ggplot2::theme_minimal(base_size = 12) +
  ggplot2::theme(plot.title    = ggplot2::element_text(face = "bold"),
                 plot.subtitle = ggplot2::element_text(colour = "grey35"),
                 panel.grid.minor = ggplot2::element_blank(),
                 plot.title.position = "plot",
                 legend.position = "bottom")
pal_secteur <- c("Public" = "#2b6cb0", "Privé sous contrat" = "#dd6b20")

# Contours des arrondissements (fond de carte) — parquet de data/paris/.
wkb2sfc <- function(x) sf::st_as_sfc(structure(lapply(x, as.raw), class = "WKB"), EWKB = FALSE)
arr_sf <- arrow::read_parquet(here::here("data", "paris", "arrondissements.parquet")) |>
  dplyr::transmute(arr = as.integer(c_ar), nom = l_aroff, geometry = wkb2sfc(geom)) |>
  sf::st_as_sf(crs = 4326)

Les indicateurs de valeur ajoutée des collèges (IVAC) de la DEPP comparent, pour chaque collège, ses résultats au diplôme national du brevet (DNB) observés à ceux attendus compte tenu du profil de ses élèves (origine sociale, niveau scolaire à l’entrée, âge, sexe). La valeur ajoutée est l’écart entre les deux : elle isole l’apport propre de l’établissement, une fois neutralisé ce qui tient au recrutement.

Une valeur ajoutée positive = le collège fait mieux que ce que son public laissait attendre ; négative = moins bien. Sessions 2022 à 2025, 6 878 collèges en France.

Note

Les collèges au recrutement très favorisé (une bonne partie de Paris) ont un taux de réussite attendu déjà proche de 100 % : la marge de valeur ajoutée y est mécaniquement faible, et plus souvent légèrement négative que dans le reste du pays. La valeur ajoutée se lit donc à recrutement comparable, pas comme un palmarès de niveau.

Paris — vue d’ensemble

Code
va_arr <- paris |>
  dplyr::filter(annee == an) |>
  dplyr::group_by(arr) |>
  dplyr::summarise(va = mean(va_du_taux_de_reussite_g, na.rm = TRUE),
                   n = dplyr::n(), .groups = "drop")

arr_sf |>
  dplyr::left_join(va_arr, by = "arr") |>
  ggplot() +
  geom_sf(aes(fill = va), colour = "white", linewidth = 0.3) +
  geom_sf_text(aes(label = arr), size = 3, colour = "grey20") +
  scale_fill_gradient2(low = "#c0392b", mid = "grey92", high = "#2b6cb0",
                       midpoint = 0, name = "VA moyenne\n(points)") +
  labs(title = sprintf("Valeur ajoutée du taux de réussite au DNB (%d)", an),
       subtitle = "Moyenne des collèges de chaque arrondissement — le 14e est encadré",
       x = NULL, y = NULL) +
  geom_sf(data = dplyr::filter(arr_sf, arr == 14), fill = NA,
          colour = "black", linewidth = 0.8) +
  theme_void(base_size = 12) +
  ggplot2::theme(plot.title = ggplot2::element_text(face = "bold"),
                 plot.subtitle = ggplot2::element_text(colour = "grey35"),
                 plot.title.position = "plot")

Code
paris |>
  dplyr::filter(annee == an) |>
  dplyr::mutate(zone = dplyr::if_else(arr == 14, "Paris 14e", "Autres arrondissements")) |>
  ggplot(aes(taux_de_reussite_g, va_du_taux_de_reussite_g)) +
  geom_hline(yintercept = 0, colour = "grey60") +
  geom_point(aes(colour = secteur, size = zone, alpha = zone)) +
  ggrepel::geom_text_repel(
    data = ~ dplyr::filter(.x, arr == 14),
    aes(label = college), size = 2.9, max.overlaps = 20, min.segment.length = 0,
    box.padding = 0.4, seed = 1) +
  scale_x_continuous(expand = ggplot2::expansion(mult = c(0.03, 0.16))) +
  scale_colour_manual(values = pal_secteur, name = NULL) +
  scale_size_manual(values = c("Paris 14e" = 3, "Autres arrondissements" = 1.8), guide = "none") +
  scale_alpha_manual(values = c("Paris 14e" = 1, "Autres arrondissements" = 0.45), guide = "none") +
  labs(title = "Réussite observée et valeur ajoutée — collèges parisiens",
       subtitle = sprintf("Session %d · un taux observé élevé ne signifie pas une valeur ajoutée élevée", an),
       x = "Taux de réussite au DNB observé (%)",
       y = "Valeur ajoutée (points)") +
  theme_depp

Focus : les collèges du 14e arrondissement

Code
paris14 |>
  dplyr::filter(annee == an) |>
  dplyr::arrange(secteur, college) |>
  dplyr::transmute(
    Collège = college, Secteur = secteur, Candidats = nb_candidats_g,
    `Réussite (%)` = taux_de_reussite_g,
    `VA réussite` = va_du_taux_de_reussite_g,
    `Note écrit /20` = note_a_l_ecrit_g,
    `VA note` = va_de_la_note_g,
    `Accès 6e→3e (%)` = taux_d_acces_6eme_3eme) |>
  reactable::reactable(
    pagination = FALSE, compact = TRUE, resizable = TRUE, highlight = TRUE, wrap = TRUE,
    defaultColDef = reactable::colDef(align = "left",
      headerStyle = list(fontWeight = "bold"),
      style = list(whiteSpace = "normal", wordBreak = "break-word")),
    columns = list(
      Collège = reactable::colDef(minWidth = 170),
      `VA réussite` = reactable::colDef(
        style = function(v) if (!is.na(v)) list(color = if (v >= 0) "#2b6cb0" else "#c0392b",
                                                fontWeight = "bold") else NULL),
      `VA note` = reactable::colDef(
        style = function(v) if (!is.na(v)) list(color = if (v >= 0) "#2b6cb0" else "#c0392b") else NULL)))
Code
paris14 |>
  ggplot(aes(annee, va_du_taux_de_reussite_g, colour = college, group = college)) +
  geom_hline(yintercept = 0, colour = "grey60") +
  geom_line(linewidth = 0.8) +
  geom_point(aes(shape = secteur), size = 2) +
  scale_x_continuous(breaks = sort(unique(paris14$annee))) +
  scale_shape_manual(values = c("Public" = 16, "Privé sous contrat" = 17), name = NULL) +
  scale_colour_brewer(palette = "Paired", name = NULL) +
  labs(title = "Valeur ajoutée du taux de réussite au DNB — collèges du 14e",
       subtitle = paste0("Sessions ", min(paris14$annee), "–", an,
                         " · ▲ = privé sous contrat"),
       x = NULL, y = "Valeur ajoutée (points)") +
  theme_depp +
  ggplot2::theme(legend.text = ggplot2::element_text(size = 9)) +
  ggplot2::guides(colour = ggplot2::guide_legend(ncol = 3), shape = "none")

Code
paris14 |>
  dplyr::filter(annee == an, nb_mentions_global_g > 0) |>
  dplyr::mutate(college = forcats::fct_reorder(college, nb_mentions_tb_g / nb_candidats_g)) |>
  tidyr::pivot_longer(c(nb_mentions_ab_g, nb_mentions_b_g, nb_mentions_tb_g),
                      names_to = "mention", values_to = "n") |>
  dplyr::mutate(mention = factor(dplyr::recode(mention,
    nb_mentions_ab_g = "Assez bien", nb_mentions_b_g = "Bien", nb_mentions_tb_g = "Très bien"),
    levels = c("Très bien", "Bien", "Assez bien")),
    part = n / nb_candidats_g) |>
  ggplot(aes(part, college, fill = mention)) +
  geom_col(width = 0.7) +
  scale_x_continuous(labels = scales::label_percent()) +
  scale_fill_manual(values = c("Assez bien" = "#a8c5e0", "Bien" = "#5b8fc9", "Très bien" = "#1f4e8c"),
                    name = NULL) +
  labs(title = sprintf("Mentions au DNB dans le 14e (%d)", an),
       subtitle = "Part des candidats de la série générale, par collège",
       x = NULL, y = NULL) +
  theme_depp

France — où se situe Paris

Code
ctx <- ivac |>
  dplyr::filter(annee == an, !is.na(va_du_taux_de_reussite_g)) |>
  dplyr::mutate(grp = dplyr::if_else(departement == "PARIS", "Paris", "France entière"))
va14 <- mean(dplyr::filter(paris14, annee == an)$va_du_taux_de_reussite_g, na.rm = TRUE)

ggplot(ctx, aes(va_du_taux_de_reussite_g, colour = grp)) +
  geom_vline(xintercept = 0, colour = "grey60") +
  geom_density(linewidth = 0.9) +
  geom_rug(data = dplyr::filter(ctx, arr == 14), colour = "#2b6cb0", linewidth = 0.7) +
  geom_vline(xintercept = va14, linetype = "dashed", colour = "#2b6cb0") +
  annotate("text", x = va14, y = 0, label = " moyenne 14e", hjust = 0, vjust = -0.4,
           size = 3.1, colour = "#2b6cb0") +
  scale_colour_manual(values = c("France entière" = "grey55", "Paris" = "#dd6b20"), name = NULL) +
  labs(title = "Distribution de la valeur ajoutée du taux de réussite au DNB",
       subtitle = sprintf("Session %d — médiane nationale nulle par construction ; barres bleues = les 9 collèges du 14e", an),
       x = "Valeur ajoutée (points)", y = "densité") +
  theme_depp

Toutes les données

Code
ivac |>
  dplyr::transmute(
    Session = annee, UAI = uai, Collège = nom_de_l_etablissement,
    Commune = commune, Département = departement, Académie = academie, Secteur = secteur,
    Candidats = nb_candidats_g, `Réussite (%)` = taux_de_reussite_g,
    `VA réussite` = va_du_taux_de_reussite_g,
    `Note /20` = note_a_l_ecrit_g, `VA note` = va_de_la_note_g,
    `Accès 6e→3e (%)` = taux_d_acces_6eme_3eme) |>
  dplyr::arrange(dplyr::desc(Session), Commune, Collège) |>
  reactable::reactable(
    searchable = TRUE, filterable = TRUE, pagination = TRUE,
    defaultPageSize = 15, compact = TRUE, resizable = TRUE, highlight = TRUE, wrap = TRUE,
    defaultColDef = reactable::colDef(align = "left",
      headerStyle = list(fontWeight = "bold"),
      style = list(whiteSpace = "normal", wordBreak = "break-word")))

Filtrer la colonne Commune sur PARIS 14 pour retrouver le focus ci-dessus.

Source & données

Code
tibble::tibble(
  ` ` = c("Jeu de données", "Producteur", "Champ", "Indicateurs", "Fichier", "Mise à jour", "Observations"),
  `  ` = c(
    "[Indicateurs de valeur ajoutée des collèges](https://data.education.gouv.fr/explore/dataset/fr-en-indicateurs-valeur-ajoutee-colleges/) — DEPP",
    "Direction de l'Évaluation, de la Prospective et de la Performance (DEPP), ministère de l'Éducation nationale",
    "France ; collèges publics et privés sous contrat ; série générale du DNB",
    "Taux de réussite et note à l'écrit observés / attendus, valeur ajoutée associée, taux d'accès de la 6e à la 3e, mentions",
    sprintf("`data/depp/fr-en-indicateurs-valeur-ajoutee-colleges.parquet` (%s ko)", round(file.size(ivac_path) / 1024)),
    format(as.Date(file.mtime(ivac_path)), "%d %B %Y"),
    sprintf("%s lignes, sessions %d–%d",
            format(nrow(ivac), big.mark = " "), min(ivac$annee), an)
  )
) |>
  gt::gt() |> gt::fmt_markdown(columns = `  `) |>
  gt::tab_options(table.width = "100%", column_labels.hidden = TRUE)
Jeu de données Indicateurs de valeur ajoutée des collèges — DEPP
Producteur Direction de l’Évaluation, de la Prospective et de la Performance (DEPP), ministère de l’Éducation nationale
Champ France ; collèges publics et privés sous contrat ; série générale du DNB
Indicateurs Taux de réussite et note à l’écrit observés / attendus, valeur ajoutée associée, taux d’accès de la 6e à la 3e, mentions
Fichier data/depp/fr-en-indicateurs-valeur-ajoutee-colleges.parquet (721 ko)
Mise à jour 08 septembre 2026
Observations 26 869 lignes, sessions 2022–2025