LPP-AM 2016-2025 — dispositifs médicaux remboursés par ligne

Données - Assurance Maladie (Cnam)

Code
source(here::here("_rinit.R"))
invisible(Sys.setlocale("LC_TIME", "fr_FR.UTF-8"))
# cd ~/iCloud/website/data/cnam; /usr/local/bin/R -e 'quarto::quarto_render("lpp_2016_2025.qmd")'
# (données : Rscript --vanilla cnam.R)

knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE,
                      fig.width = 9, fig.height = 5.5, dpi = 150)
library(dplyr); library(tidyr); library(ggplot2); library(stringr)

path <- here::here("data", "cnam", "lpp_2016_2025.parquet")
d <- arrow::read_parquet(path)

mdd <- scales::label_number(scale = 1e-9, suffix = " Md€", accuracy = 0.1, big.mark = " ", decimal.mark = ",")
mde <- scales::label_number(scale = 1e-6, suffix = " M€", accuracy = 1, big.mark = " ", decimal.mark = ",")
pct <- scales::label_percent(accuracy = 0.1, decimal.mark = ",")
nb  <- scales::label_number(big.mark = " ", decimal.mark = ",")

theme_cnam <- theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold", size = 13), plot.title.position = "plot",
        plot.subtitle = element_text(colour = "grey35"),
        panel.grid.minor = element_blank(), legend.position = "bottom")
col1 <- "#1b7f79"; col2 <- "#c0392b"

# barres horizontales triées
barh <- function(df, lab, val, fmt = mdd, fill = col1, ...) {
  df |>
    mutate(.l = forcats::fct_reorder({{ lab }}, {{ val }})) |>
    ggplot(aes(.l, {{ val }})) +
    geom_col(fill = fill, ...) +
    geom_text(aes(label = fmt({{ val }})), hjust = -0.08, size = 3) +
    coord_flip() +
    scale_y_continuous(labels = fmt, expand = expansion(mult = c(0, 0.2))) +
    labs(x = NULL, y = NULL) + theme_cnam
}
Code
lpp <- d
nat <- lpp |> group_by(annee) |>
  summarise(qte = sum(quantite, na.rm = TRUE), remb = sum(montant_rembourse, na.rm = TRUE),
            base = sum(montant_remboursable, na.rm = TRUE), .groups = "drop")

Dépenses de l’Assurance maladie (tous régimes, France entière) pour les dispositifs médicaux de la LPP : pour chaque ligne de la nomenclature (code LPP), la quantité remboursée, le montant remboursable (base) et le montant remboursé, de 2016 à 2025 (en date de remboursement). Le champ est limité aux dispositifs médicaux codés (les appareils générateurs d’aérosol, orthoprothèses et orthèses non codés en sont exclus ; ils figurent dans l’onglet NAT_PREST — voir la page « lpp_agregats »). Le codage des dispositifs n’est pas stable d’une année à l’autre (décret 2019-571 sur l’obligation de code individuel), ce qui peut créer des ruptures de série. 35 736 lignes LPP, 15 chapitres (TS1), 182 sous-sections (TS3).

Dépense totale

Code
nat |> ggplot(aes(annee, remb)) +
  geom_col(fill = col1) + geom_text(aes(label = mdd(remb)), vjust = -0.4, size = 3.3) +
  scale_x_continuous(breaks = 2016:2025) + scale_y_continuous(labels = mdd, expand = expansion(mult = c(0, .12))) +
  labs(x = NULL, y = NULL, title = "Dispositifs médicaux : montant remboursé par an") + theme_cnam

Code
cagr <- (nat$remb[nat$annee == 2025] / nat$remb[nat$annee == 2016])^(1 / 9) - 1

Le montant remboursé passe de 6,7 Md€ (2016) à 10,4 Md€ (2025), soit 5,0% par an en moyenne (54,7% sur la période).

Par chapitre de la nomenclature (TS1)

Code
lpp |> group_by(annee, ts1, libelle_ts1) |> summarise(remb = sum(montant_rembourse, na.rm = TRUE), .groups = "drop") |>
  mutate(l = paste0(ts1, " — ", str_to_sentence(str_trunc(libelle_ts1, 40)))) |>
  ggplot(aes(annee, remb, fill = l)) + geom_area(colour = "white", linewidth = 0.2) +
  scale_x_continuous(breaks = 2016:2025) + scale_y_continuous(labels = mdd) +
  labs(x = NULL, y = NULL, fill = NULL, title = "Montant remboursé par chapitre TS1") +
  theme_cnam + guides(fill = guide_legend(ncol = 2))

Sous-sections qui tirent la croissance (TS3)

Code
lpp |> group_by(ts3, libelle_ts3, annee) |> summarise(remb = sum(montant_rembourse, na.rm = TRUE), .groups = "drop") |>
  filter(annee %in% c(2016, 2025)) |> pivot_wider(names_from = annee, values_from = remb, names_prefix = "a") |>
  mutate(delta = a2025 - a2016) |> slice_max(delta, n = 15) |>
  mutate(l = paste0(ts3, " — ", str_to_sentence(str_trunc(libelle_ts3, 45)))) |>
  barh(l, delta, fill = col2) +
  labs(title = "Sous-sections dont le remboursé a le plus augmenté, 2016 → 2025")

Code
lpp |> filter(annee == 2025) |> group_by(ts3, libelle_ts3) |>
  summarise(remb = sum(montant_rembourse, na.rm = TRUE), qte = sum(quantite, na.rm = TRUE),
            base = sum(montant_remboursable, na.rm = TRUE), .groups = "drop") |>
  arrange(desc(remb)) |> slice_head(n = 25) |>
  transmute(Code = ts3, Libellé = libelle_ts3, `Remboursé 2025` = remb, Quantité = qte,
            `Prix moyen remboursable` = base / qte, `Taux de remb.` = remb / base) |>
  gt::gt() |>
  gt::fmt_number(c(`Remboursé 2025`, Quantité), decimals = 0, sep_mark = " ") |>
  gt::fmt_number(`Prix moyen remboursable`, decimals = 2) |>
  gt::fmt_percent(`Taux de remb.`, decimals = 1) |> gt::tab_options(table.width = "100%")
Code Libellé Remboursé 2025 Quantité Prix moyen remboursable Taux de remb.
R20A Pression positive continue pour apnée du sommeil, prestation seule 1 100 482 588 111 670 566 13.73 71.8%
H60B Autocontrôle du glucose interstitiel, consommable dont capteurs et transmetteurs 583 063 926 14 132 818 42.32 97.5%
A10B Nutriments pour supplémentation orale, adulte 405 457 787 48 652 452 9.67 86.2%
S40B Audioprothèses, plus 20 ans 400 262 437 1 541 536 400.64 64.8%
D10A Pansements hydrocellulaires 380 859 975 14 916 924 32.53 78.5%
T10A Perfusion, diffuseurs portables et divers 332 849 777 3 811 667 103.12 84.7%
T30A Lits médicaux, location 312 774 049 25 032 150 13.31 93.9%
G30B Génito-urinaire, matériel pour sondage vésical 277 814 149 3 990 558 74.74 93.1%
R10A Oxygénothérapie seule, long terme gazeux 272 953 163 4 825 742 61.15 92.5%
M20 Implants orthopédiques du genou 237 598 429 386 703 616.04 99.7%
R30B Traitements respiratoires, prestations avec oxygène 233 526 217 3 446 365 73.20 92.6%
K20A VPH Achat neuf 206 744 951 476 078 438.03 99.1%
H20B Autotraitement du diabète, pompes externes sans tubulure 202 859 279 28 584 469 7.16 99.1%
H10E Autocontrôle de la glycémie/corps cétoniques, bandelettes et autres 197 472 339 6 021 747 34.54 94.9%
O20A Attelles (correction orthopédique) 185 265 139 5 139 740 55.64 64.8%
H50B Boucle semi-fermée, capteurs, transmetteurs, algorithme 178 122 806 30 710 620 5.84 99.3%
R30A Traitements respiratoires, autres prestations seules (ventilation) 173 600 929 3 433 363 56.46 89.6%
T20B Perfusion, pompes externes, sauf insuline, consommables 170 370 162 1 591 582 109.75 97.5%
M30 Implants orthopédiques de la hanche 169 185 407 389 628 434.63 99.9%
A30A Matériel de stomie digestive 142 096 463 5 797 405 25.55 95.9%
K20C VPH Location courte durée (dont option d'achat) 138 486 550 10 272 015 15.31 88.0%
H20A Autotraitement du diabète, pompes externes avec tubulure 133 852 023 18 841 072 7.16 99.3%
D20A Bandes élastiques de contention 131 915 517 9 235 642 16.94 84.3%
H50C Boucle semi-fermée, pompe à insuline 129 741 116 19 359 896 6.75 99.3%
K10 Dispositifs d'aide à la vie dont béquilles, déambulateurs 128 256 732 8 487 370 17.47 86.5%

Concentration : lignes les plus dépensières

Code
lpp |> filter(annee == 2025) |> group_by(code_lpp, libelle_lpp) |>
  summarise(remb = sum(montant_rembourse, na.rm = TRUE), .groups = "drop") |>
  arrange(desc(remb)) |> mutate(cum = cumsum(remb) / sum(remb), rang = row_number()) -> lig

top <- lig |> slice_head(n = 15) |> mutate(l = str_to_sentence(str_trunc(libelle_lpp, 60)))
barh(top, l, remb, fill = col2) + labs(title = "Les 15 lignes LPP les plus remboursées en 2025")

Les 100 premières lignes (sur 35 736) représentent 55,4% du remboursé 2025 ; les 1 000 premières 89,2%.

Source

Caisse nationale de l’Assurance Maladie (Cnam) — Cnam — LPP-AM (liste des produits et prestations remboursables), séries annuelles 2016-2025. Conversion Excel → Parquet : cnam.R (dossier data/cnam).