Arbres abattus pour raisons sanitaires et essence de remplacement

Données - Ville de Paris

Code
source(here::here("_rinit.R"))
invisible(Sys.setlocale("LC_TIME", "fr_FR.UTF-8"))
invisible(Sys.setenv(TZ = "Europe/Paris"))
# cd ~/iCloud/website/data/paris; /usr/local/bin/R -e 'quarto::quarto_render("arbres-abattus-pour-raison-sanitaires-et-essence-de-remplacement.qmd")'
# sh ~/iCloud/website/website_short.sh

library(lubridate)
source(here::here("data", "paris", "_paris.R"))   # wkb2sfc(), paris_basemap()

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

P <- here::here("data", "paris")
abattus_path <- file.path(P, "arbres-abattus-pour-raison-sanitaires-et-essence-de-remplacement.parquet")
abattus <- arrow::read_parquet(abattus_path)
arrondissements <- arrow::read_parquet(file.path(P, "arrondissements.parquet"))

# Motif d'abattage : ~12 phrases-types -> libellé court (détection par mots-clés,
# robuste aux retouches de rédaction) + regroupement en 4 grandes causes.
motif_court <- function(x) {
  dplyr::case_when(
    stringr::str_detect(x, stringr::regex("déclin physiologique", ignore_case = TRUE)) ~ "Déclin physiologique irréversible",
    stringr::str_detect(x, stringr::regex("partie aérienne.*lignivores|houppier.*délitement", ignore_case = TRUE)) ~ "Pourriture / cavités du houppier",
    stringr::str_detect(x, stringr::regex("ancrage ou le système racinaire", ignore_case = TRUE)) ~ "Pourriture racinaire / ancrage",
    stringr::str_detect(x, stringr::regex("épisode climatique", ignore_case = TRUE)) ~ "Épisode climatique (tempête, crue)",
    stringr::str_detect(x, stringr::regex("chocs, feu, pollution", ignore_case = TRUE)) ~ "Chocs, feu, pollution, vandalisme",
    stringr::str_detect(x, stringr::regex("Jeune arbre dépérissant", ignore_case = TRUE)) ~ "Échec de reprise après plantation",
    stringr::str_detect(x, stringr::regex("défauts structurels propres", ignore_case = TRUE)) ~ "Défauts structurels propres",
    stringr::str_detect(x, stringr::regex("désordres aux structures", ignore_case = TRUE)) ~ "Désordres aux trottoirs / sépultures",
    stringr::str_detect(x, stringr::regex("lutte obligatoire", ignore_case = TRUE)) ~ "Pathogène réglementé (lutte obligatoire)",
    stringr::str_detect(x, stringr::regex("manque de lumière", ignore_case = TRUE)) ~ "Dépérissement par manque de lumière",
    stringr::str_detect(x, stringr::regex("section des racines|tranchées, excavations", ignore_case = TRUE)) ~ "Travaux à proximité (tranchées…)",
    is.na(x) ~ "Non précisé",
    TRUE ~ "Autre"
  )
}
cause_large <- function(motif) {
  dplyr::case_when(
    motif %in% c("Pourriture / cavités du houppier", "Pourriture racinaire / ancrage",
                 "Pathogène réglementé (lutte obligatoire)") ~ "Agents pathogènes (pourriture)",
    motif %in% c("Déclin physiologique irréversible", "Dépérissement par manque de lumière",
                 "Échec de reprise après plantation") ~ "Déclin physiologique",
    motif == "Épisode climatique (tempête, crue)" ~ "Épisode climatique",
    TRUE ~ "Structurel & divers"
  )
}

abattus <- abattus |>
  dplyr::mutate(
    date   = as.Date(datedernierabattage),
    mois   = lubridate::floor_date(date, "month"),
    motif  = motif_court(motifabattagearbreprecedent),
    cause  = cause_large(motif),
    ar_num = suppressWarnings(as.integer(stringr::str_extract(arrondissement, "\\d+"))),
    ar_num = dplyr::if_else(stringr::str_detect(arrondissement, "PARIS"), ar_num, NA_integer_),
    genre_abattu       = stringr::str_to_title(genrearbreprecedent),
    genre_futur        = stringr::str_to_title(genrefutur),
    espece_fr          = stringr::str_to_sentence(libellefrancaisarbreprecedent),
    remplacement_connu = !is.na(genrefutur),
    meme_genre         = remplacement_connu & (genre_abattu == genre_futur)
  )

n_tot     <- nrow(abattus)
date_min  <- format(min(abattus$date), "%d %B %Y")
date_max  <- format(max(abattus$date), "%d %B %Y")

arr_sf <- arrondissements |>
  dplyr::transmute(c_ar, nom = l_aroff, geometry = wkb2sfc(geom)) |>
  sf::st_as_sf(crs = 4326)
pts_sf <- abattus |>
  dplyr::mutate(geometry = wkb2sfc(geo_point_2d)) |>
  sf::st_as_sf(crs = 4326)

theme_carte <- theme_void(base_size = 12) +
  theme(plot.title = element_text(face = "bold"),
        plot.subtitle = element_text(colour = "grey35"),
        legend.position = "right")
theme_barres <- theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold"),
        plot.subtitle = element_text(colour = "grey35"),
        panel.grid.major.y = element_blank())
pal_cause <- c(
  "Déclin physiologique"              = "#4c6d3b",
  "Agents pathogènes (pourriture)"    = "#8a5a2b",
  "Épisode climatique"                = "#2f6f9f",
  "Structurel & divers" = "#8a8a8a"
)

Ce jeu de données géolocalise les arbres récemment abattus par la Ville de Paris pour des raisons de sécurité : chute ou contamination probable. Pour chaque arbre sont précisés le motif (pathologie, défaut mécanique, épisode climatique…) et l’essence prévue en remplacement. Il est construit à partir des tournées de surveillance des agents sylvicoles menées depuis le printemps 2021.

C’est un instantané glissant, pas un historique : un arbre y figure jusqu’à sa replantation, ou au plus 6 mois après son abattage. À la dernière mise à jour, 421 arbres, abattus entre le 06 mars 2026 et le 03 septembre 2026.

Code
abattus |>
  dplyr::arrange(dplyr::desc(date)) |>
  dplyr::transmute(
    Date = date, Arrondissement = arrondissement, Adresse = adresse,
    `Domanialité` = domanialite,
    `Essence abattue` = espece_fr, `Genre abattu` = genre_abattu,
    Motif = motif,
    `Genre de remplacement` = dplyr::coalesce(genre_futur, "— à définir —")
  ) |>
  head(12) |>
  gt::gt() |>
  gt::tab_header(title = paste0("Aperçu — abattages les plus récents (", date_max, ")")) |>
  gt::tab_options(table.width = "100%")
Aperçu — abattages les plus récents (03 septembre 2026)
Date Arrondissement Adresse Domanialité Essence abattue Genre abattu Motif Genre de remplacement
2026-09-03 PARIS 15E ARRDT RUE BALARD Alignement Sophora Styphnolobium Pourriture / cavités du houppier Gleditsia
2026-09-03 PARIS 13E ARRDT RUE NEUVE TOLBIAC Alignement Sophora Styphnolobium Pourriture / cavités du houppier Gleditsia
2026-09-03 PARIS 13E ARRDT VILLA NIEUPORT Alignement Cerisier à fleurs Prunus Déclin physiologique irréversible Prunus
2026-09-03 PARIS 13E ARRDT RUE NEUVE TOLBIAC Alignement Sophora Styphnolobium Désordres aux trottoirs / sépultures Gleditsia
2026-09-02 PARIS 15E ARRDT RUE DE LA CONVENTION Alignement Photinia Photinia Déclin physiologique irréversible X Chitalpa
2026-09-02 PARIS 15E ARRDT AVENUE EMILE ZOLA Alignement Sophora Styphnolobium Pourriture / cavités du houppier Ginkgo
2026-09-02 PARIS 13E ARRDT RUE FERNAND BRAUDEL Alignement Cerisier à fleurs Prunus Déclin physiologique irréversible Prunus
2026-09-02 PARIS 15E ARRDT AVENUE EMILE ZOLA Alignement Sophora Styphnolobium Épisode climatique (tempête, crue) Ginkgo
2026-09-02 PARIS 13E ARRDT RUE BOBILLOT Alignement Tilleul Tilia Chocs, feu, pollution, vandalisme Tilia
2026-09-02 PARIS 13E ARRDT RUE DUNOIS Alignement Chêne Quercus Déclin physiologique irréversible Quercus
2026-09-02 PARIS 7E ARRDT PORT DES SAINTS PERES / QUAI VOLTAIRE Alignement Peuplier Populus Pourriture racinaire / ancrage Ulmus
2026-09-02 PARIS 13E ARRDT RUE MADELEINE BRES Alignement Erable Acer Pourriture racinaire / ancrage Acer

Carte — où sont les arbres abattus

Code
cnt_ar <- abattus |>
  dplyr::filter(!is.na(ar_num)) |>
  dplyr::count(ar_num, name = "n")

arr_sf |>
  dplyr::left_join(cnt_ar, by = c("c_ar" = "ar_num")) |>
  ggplot() +
  geom_sf(aes(fill = n), colour = "white", linewidth = 0.2) +
  scale_fill_viridis_c(option = "rocket", direction = -1, na.value = "grey92",
                       name = "Arbres\nabattus") +
  labs(title = "Arbres abattus pour raisons sanitaires, par arrondissement",
       subtitle = paste0("Instantané au ", date_max,
                         " — hors bois de Boulogne et de Vincennes")) +
  theme_carte

Les arrondissements riches en arbres d’alignement et en grands jardins (13e, 15e, 14e, 20e) concentrent les abattages ; le centre dense en compte très peu.

Carte interactive

Chaque point = un arbre abattu. Survol / clic pour le détail (adresse, essence, motif, remplacement prévu).

Code
pal_lf <- colorFactor(unname(pal_cause), domain = names(pal_cause))

leaflet(pts_sf, options = leafletOptions(minZoom = 11)) |>
  paris_basemap() |>
  addPolygons(data = arr_sf, fill = FALSE, color = "#999", weight = 1) |>
  addCircleMarkers(
    radius = 4, stroke = FALSE, fillOpacity = 0.8,
    fillColor = ~pal_lf(cause),
    label = ~sprintf("%s — %s", espece_fr, adresse),
    popup = ~sprintf(
      "<b>%s</b> <i>(%s)</i><br>%s<br>Abattu le %s<br><b>Motif :</b> %s<br><b>Remplacement prévu :</b> %s",
      espece_fr, genrearbreprecedent, adresse, format(date, "%d/%m/%Y"), motif,
      dplyr::coalesce(paste(genre_futur, dplyr::coalesce(especefutur, "")), "à définir"))
  ) |>
  addLegend("bottomright", pal = pal_lf, values = ~cause, title = "Cause", opacity = 0.9)

Par grande cause

Code
ggplot() +
  geom_sf(data = arr_sf, fill = "grey96", colour = "white") +
  geom_sf(data = pts_sf, aes(colour = cause), size = 1.5, alpha = 0.8) +
  scale_colour_manual(values = pal_cause, name = NULL) +
  labs(title = "Motif d'abattage",
       subtitle = "Un point = un arbre abattu pour raison sanitaire") +
  theme_carte +
  theme(legend.position = "bottom") +
  guides(colour = guide_legend(nrow = 2, override.aes = list(size = 3)))

Pourquoi ces arbres sont abattus

Code
abattus |>
  dplyr::count(cause, motif) |>
  dplyr::mutate(motif = forcats::fct_reorder(motif, n, sum)) |>
  ggplot(aes(n, motif, fill = cause)) +
  geom_col(width = 0.7) +
  geom_text(aes(label = n), hjust = -0.3, size = 3) +
  scale_fill_manual(values = pal_cause, name = NULL) +
  scale_x_continuous(expand = expansion(mult = c(0, 0.08))) +
  labs(title = "Motif d'abattage",
       subtitle = paste0(n_tot, " arbres — instantané au ", date_max),
       x = NULL, y = NULL) +
  theme_barres +
  theme(legend.position = "bottom") +
  guides(fill = guide_legend(nrow = 2))

Code
p_cause <- abattus |>
  dplyr::count(cause) |>
  dplyr::mutate(pct = n / sum(n)) |>
  dplyr::arrange(dplyr::desc(n))
cat(sprintf(
  "\nEn pratique, **%s** relèvent de la sénescence (déclin physiologique) ou d'agents pathogènes (pourritures du bois) ; seuls **%s** sont directement imputés à un épisode climatique.\n",
  scales::percent(sum(p_cause$pct[p_cause$cause %in%
    c("Déclin physiologique", "Agents pathogènes (pourriture)")]), 1),
  scales::percent(p_cause$pct[p_cause$cause == "Épisode climatique"], 1)))

En pratique, 81% relèvent de la sénescence (déclin physiologique) ou d’agents pathogènes (pourritures du bois) ; seuls 7% sont directement imputés à un épisode climatique.

Essences abattues

Code
abattus |>
  dplyr::filter(!is.na(genre_abattu)) |>
  dplyr::count(genre_abattu, sort = TRUE) |>
  head(12) |>
  dplyr::mutate(genre_abattu = forcats::fct_reorder(genre_abattu, n)) |>
  ggplot(aes(n, genre_abattu)) +
  geom_col(fill = "#8a5a2b", width = 0.7) +
  geom_text(aes(label = n), hjust = -0.3, size = 3) +
  scale_x_continuous(expand = expansion(mult = c(0, 0.08))) +
  labs(title = "Genres les plus touchés",
       subtitle = "Platane, sophora, cerisier, érable, marronnier en tête",
       x = NULL, y = NULL) +
  theme_barres

Le remplacement

Code
pc  <- mean(abattus$remplacement_connu)
sub <- dplyr::filter(abattus, remplacement_connu)
pm  <- mean(sub$meme_genre)
cat(sprintf(
  "\nUne essence de remplacement est déjà renseignée pour **%s** des arbres (les autres restent « à définir »). Parmi ceux-ci, **%s sont replantés avec le même genre** ; le reste change d'essence.\n",
  scales::percent(pc, 1), scales::percent(pm, 1)))

Une essence de remplacement est déjà renseignée pour 89% des arbres (les autres restent « à définir »). Parmi ceux-ci, 71% sont replantés avec le même genre ; le reste change d’essence.

Code
top_g <- abattus |>
  dplyr::filter(!is.na(genre_abattu)) |>
  dplyr::count(genre_abattu, sort = TRUE) |>
  head(12) |>
  dplyr::pull(genre_abattu)

dplyr::bind_rows(
  abattus |> dplyr::filter(genre_abattu %in% top_g) |>
    dplyr::count(genre = genre_abattu) |> dplyr::mutate(type = "Abattu"),
  abattus |> dplyr::filter(genre_futur %in% top_g) |>
    dplyr::count(genre = genre_futur) |> dplyr::mutate(type = "Remplacement prévu")
) |>
  dplyr::mutate(genre = forcats::fct_reorder(genre, n, sum),
                type = factor(type, levels = c("Abattu", "Remplacement prévu"))) |>
  ggplot(aes(n, genre, fill = type)) +
  geom_col(position = position_dodge(width = 0.7), width = 0.65) +
  scale_fill_manual(values = c("Abattu" = "#8a5a2b", "Remplacement prévu" = "#4c6d3b"),
                    name = NULL) +
  scale_x_continuous(expand = expansion(mult = c(0, 0.05))) +
  labs(title = "Abattu vs replanté, par genre",
       subtitle = "Peuplier et marronnier reculent le plus",
       x = NULL, y = NULL) +
  theme_barres +
  theme(legend.position = "bottom")

Le total « remplacement prévu » est plus petit que le total « abattu » (essence encore à définir pour 11% des arbres). Les genres qui perdent le plus en proportion — peuplier, marronnier — sont ceux que la Ville réduit volontairement (fragilité, maladies), au profit d’une palette plus diversifiée.

Calendrier des abattages

Code
abattus |>
  dplyr::count(mois, cause) |>
  ggplot(aes(mois, n, fill = cause)) +
  geom_col(width = 25) +
  scale_fill_manual(values = pal_cause, name = NULL) +
  scale_x_date(date_labels = "%b %Y", date_breaks = "1 month") +
  labs(title = "Abattages par mois",
       subtitle = paste0("Fenêtre glissante d'environ 6 mois (", date_min, " – ", date_max, ")"),
       x = NULL, y = "Arbres abattus") +
  theme_barres +
  theme(legend.position = "bottom")

Par type d’espace

Code
abattus |>
  dplyr::count(domanialite, sort = TRUE) |>
  dplyr::mutate(domanialite = forcats::fct_reorder(domanialite, n)) |>
  ggplot(aes(n, domanialite)) +
  geom_col(fill = "#4c6d3b", width = 0.65) +
  geom_text(aes(label = n), hjust = -0.3, size = 3) +
  scale_x_continuous(expand = expansion(mult = c(0, 0.08))) +
  labs(title = "Arbres abattus par gestionnaire / type d'espace",
       subtitle = "Alignements de voirie et jardins en tête",
       x = NULL, y = NULL) +
  theme_barres

Source & données

Code
tibble::tibble(
  ` ` = c("Jeu de données", "Producteur", "Licence", "Fichiers", "Mise à jour", "Observations"),
  `  ` = c(
    "[Arbres abattus pour raisons sanitaires et essence de remplacement](https://opendata.paris.fr/explore/dataset/arbres-abattus-pour-raison-sanitaires-et-essence-de-remplacement/)",
    "Direction des Espaces Verts et de l'Environnement (DEVE) — Ville de Paris",
    "Open Database License (ODbL)",
    sprintf("`data/paris/arbres-abattus-pour-raison-sanitaires-et-essence-de-remplacement.parquet` (%s ko) + `arrondissements.parquet`",
            round(file.size(abattus_path) / 1024)),
    format(as.Date(file.mtime(abattus_path)), "%d %B %Y"),
    sprintf("%s arbres — instantané glissant (~6 mois), pas un historique cumulé", n_tot)
  )
) |>
  gt::gt() |>
  gt::fmt_markdown(columns = `  `) |>
  gt::tab_options(table.width = "100%", column_labels.hidden = TRUE)
Jeu de données Arbres abattus pour raisons sanitaires et essence de remplacement
Producteur Direction des Espaces Verts et de l’Environnement (DEVE) — Ville de Paris
Licence Open Database License (ODbL)
Fichiers data/paris/arbres-abattus-pour-raison-sanitaires-et-essence-de-remplacement.parquet (32 ko) + arrondissements.parquet
Mise à jour 04 septembre 2026
Observations 421 arbres — instantané glissant (~6 mois), pas un historique cumulé