Code
source (here:: here ("_rinit.R" ))
source (here:: here ("data" , "paris" , "_paris.R" )) # wkb2sfc(), paris_basemap(), arrondissements_sf()
invisible (Sys.setlocale ("LC_TIME" , "fr_FR.UTF-8" ))
# cd ~/iCloud/website/data/paris; /usr/local/bin/R -e 'quarto::quarto_render("occupation-des-creneaux-sportifs.qmd")'
knitr:: opts_chunk$ set (echo = TRUE , message = FALSE , warning = FALSE ,
fig.width = 8 , fig.height = 5 , dpi = 150 )
cren_path <- here:: here ("data" , "paris" , "occupation-des-creneaux-sportifs.parquet" )
jours_fr <- c ("lundi" , "mardi" , "mercredi" , "jeudi" , "vendredi" , "samedi" , "dimanche" )
cren <- arrow:: read_parquet (cren_path) |>
dplyr:: mutate (
jour = factor (jour_de_la_semaine, levels = jours_fr,
labels = stringr:: str_to_title (jours_fr)),
heure = as.integer (substr (heure_de_debut_du_creneau, 1 , 2 )),
ar = suppressWarnings (as.integer (substr (code_postal_de_l_equipement, 4 , 5 )))
)
n_tot <- nrow (cren)
n_equip <- dplyr:: n_distinct (cren$ nom_de_l_equipement)
n_clubs <- dplyr:: n_distinct (cren$ nom_de_l_association_club)
arr_sf <- arrondissements_sf ()
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 ())
theme_carte <- theme_void (base_size = 12 ) +
theme (plot.title = element_text (face = "bold" ),
plot.subtitle = element_text (colour = "grey35" ),
plot.margin = margin (5 , 12 , 5 , 5 ))
L’emploi du temps hebdomadaire des équipements sportifs municipaux mis à disposition des clubs et associations : 18 768 créneaux récurrents, répartis sur 372 équipements (gymnases, piscines, courts de tennis…) et 1 750 clubs/associations .
Code
cren |>
dplyr:: filter (! is.na (nom_de_l_association_club)) |>
dplyr:: transmute (
Club = nom_de_l_association_club, Discipline = discipline, Jour = jour,
Horaire = paste0 (heure_de_debut_du_creneau, "–" , heure_de_fin_du_creneau),
Équipement = nom_de_l_equipement, Payant = payant
) |>
dplyr:: slice_sample (n = 8 ) |>
gt:: gt () |>
gt:: tab_header (title = "8 créneaux tirés au hasard" ) |>
gt:: tab_options (table.width = "100%" )
Club
Discipline
Jour
Horaire
Équipement
Payant
ART ET CULTURE 15
TENNIS
Dimanche
10:00:00–11:00:00
TEP René et André Mourlon
oui
ASSOCIATION SPORTIVE MAINE MONTPARNASSE -(ASSM)
TENNIS
Lundi
18:00:00–19:00:00
Tennis du Commandant Mouchotte
non
SPORTS LOISIRS EDUCATIFS CULTURELS 16EME
TENNIS
Mardi
20:00:00–21:00:00
TEP Suchet - TEP Couvert
oui
AGAM ASSOCIATION POUR LA GYMNASTIQUE ET LES ARTS MARTIAUX
TAEKWONDO
Jeudi
20:30:00–22:30:00
Salles de sport Marguerite Broquedis (ex Reims)
oui
PARIS AMICALE CAMOU
VOLLEY BALL
Mercredi
16:30:00–18:30:00
Gymnase Dupleix
oui
CLUB D'ESCRIME DE LA TOUR D'AUVERGNE TA
ESCRIME ARTISTIQUE
Vendredi
12:00:00–14:00:00
Salles de sport Buffault
non
WADO PARIS MONTROUGE KARATE JUTSU
ARTS MARTIAUX
Samedi
10:00:00–12:00:00
Gymnase des Grands Moulins
oui
TENNIS CLUB DU XIII TC XIII
TENNIS
Mardi
20:00:00–21:00:00
Tennis Charles Moureu
non
Quand et quoi
Code
cren |>
dplyr:: count (discipline, sort = TRUE ) |>
head (15 ) |>
dplyr:: mutate (discipline = forcats:: fct_reorder (stringr:: str_to_title (discipline), n)) |>
ggplot (aes (n, discipline)) +
geom_col (fill = "#2f6f4f" , width = 0.7 ) +
geom_text (aes (label = n), hjust = - 0.2 , size = 3 ) +
scale_x_continuous (expand = expansion (mult = c (0 , 0.1 ))) +
labs (title = "15 disciplines les plus programmées" ,
subtitle = "Le tennis représente à lui seul plus d'un quart des créneaux" ,
x = NULL , y = NULL ) +
theme_barres
Code
cren |>
dplyr:: filter (! is.na (heure)) |>
dplyr:: count (jour, heure) |>
ggplot (aes (heure, jour, fill = n)) +
geom_tile (colour = "white" , linewidth = 0.4 ) +
scale_fill_viridis_c (option = "mako" , direction = - 1 , name = "Créneaux" ) +
scale_x_continuous (breaks = seq (6 , 23 , 2 )) +
scale_y_discrete (limits = rev (levels (cren$ jour))) +
labs (title = "Quand ont lieu les créneaux" ,
subtitle = "Les soirées de semaine (17h-20h) concentrent l'essentiel ; le mercredi élargit aussi sur l'après-midi" ,
x = "Heure de début" , y = NULL ) +
theme_minimal (base_size = 12 ) +
theme (plot.title = element_text (face = "bold" ), plot.subtitle = element_text (colour = "grey35" ),
panel.grid = element_blank ())
Accès et tarification
Code
cren |>
dplyr:: filter (! is.na (payant)) |>
dplyr:: count (payant) |>
dplyr:: mutate (payant = ifelse (payant == "oui" , "Payant" , "Gratuit" )) |>
ggplot (aes (n, payant)) +
geom_col (fill = "#3b6f8a" , width = 0.55 ) +
geom_text (aes (label = scales:: percent (n / sum (n), 1 )), hjust = - 0.2 , size = 3.5 ) +
scale_x_continuous (expand = expansion (mult = c (0 , 0.12 ))) +
labs (title = "Créneaux payants vs gratuits" , x = NULL , y = NULL ) +
theme_barres
Code
cren |>
dplyr:: filter (! is.na (prix_moyen_annuel_en_eur), prix_moyen_annuel_en_eur > 0 ,
prix_moyen_annuel_en_eur < 1000 ) |>
ggplot (aes (prix_moyen_annuel_en_eur)) +
geom_histogram (bins = 40 , fill = "#3b6f8a" ) +
scale_x_continuous (labels = scales:: label_number (suffix = " €" )) +
labs (title = "Prix moyen annuel, créneaux payants" ,
subtitle = "Cotisation annuelle indicative renseignée par le club, hors valeurs extrêmes" ,
x = NULL , y = "Créneaux" ) +
theme_barres
Code
cren |>
dplyr:: filter (! is.na (genre)) |>
dplyr:: count (genre, sort = TRUE ) |>
dplyr:: mutate (genre = forcats:: fct_reorder (genre, n)) |>
ggplot (aes (n, genre)) +
geom_col (fill = "#8a5a2b" , width = 0.55 ) +
geom_text (aes (label = scales:: percent (n / sum (n), 1 )), hjust = - 0.2 , size = 3.5 ) +
scale_x_continuous (expand = expansion (mult = c (0 , 0.12 ))) +
labs (title = "Créneaux mixtes, non-mixtes hommes/femmes" ,
x = NULL , y = NULL ) +
theme_barres
Où
Code
cnt <- cren |> dplyr:: filter (! is.na (ar)) |> dplyr:: count (ar, name = "n" )
arr_sf |>
dplyr:: left_join (cnt, by = c ("c_ar" = "ar" )) |>
ggplot () +
geom_sf (aes (fill = n), colour = "white" , linewidth = 0.2 ) +
scale_fill_viridis_c (option = "rocket" , direction = - 1 , na.value = "grey92" , name = "Créneaux" ) +
labs (title = "Créneaux sportifs par arrondissement (adresse de l'équipement)" ,
subtitle = "15ᵉ, 12ᵉ et 14ᵉ en tête — arrondissements les plus dotés en équipements" ) +
theme_carte
Clubs et équipements les plus actifs
Code
cren |>
dplyr:: filter (! is.na (nom_de_l_association_club)) |>
dplyr:: count (nom_de_l_association_club, sort = TRUE ) |>
head (8 ) |>
dplyr:: mutate (nom_de_l_association_club = forcats:: fct_reorder (
stringr:: str_trunc (nom_de_l_association_club, 40 ), n)) |>
ggplot (aes (n, nom_de_l_association_club)) +
geom_col (fill = "#7a1f3d" , width = 0.65 ) +
geom_text (aes (label = n), hjust = - 0.2 , size = 3 ) +
scale_x_continuous (expand = expansion (mult = c (0 , 0.12 ))) +
labs (title = "Clubs avec le plus de créneaux" , x = NULL , y = NULL ) +
theme_barres
Rechercher un créneau
Table cherchable : recherche plein texte, filtres par colonne sous chaque en-tête, tri en cliquant sur l’en-tête.
Code
cren |>
dplyr:: transmute (
Club = nom_de_l_association_club, Discipline = stringr:: str_to_title (discipline),
Jour = as.character (jour),
Horaire = paste0 (heure_de_debut_du_creneau, "–" , heure_de_fin_du_creneau),
Équipement = nom_de_l_equipement, Payant = payant, Genre = genre
) |>
reactable:: reactable (
searchable = TRUE ,
filterable = TRUE ,
pagination = TRUE ,
defaultPageSize = 25 ,
showPageSizeOptions = TRUE ,
pageSizeOptions = c (10 , 25 , 50 , 100 ),
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 (
Club = reactable:: colDef (minWidth = 180 ),
Discipline = reactable:: colDef (minWidth = 110 , maxWidth = 160 ),
Jour = reactable:: colDef (minWidth = 80 , maxWidth = 110 ),
Horaire = reactable:: colDef (minWidth = 90 , maxWidth = 120 ),
` Équipement ` = reactable:: colDef (minWidth = 160 ),
Payant = reactable:: colDef (minWidth = 60 , maxWidth = 90 ),
Genre = reactable:: colDef (minWidth = 70 , maxWidth = 100 )
)
)
Source & données
Code
tibble:: tibble (
` ` = c ("Jeu de données" , "Producteur" , "Licence" , "Fichiers" , "Mise à jour" , "Observations" ),
` ` = c (
"[Occupation des créneaux sportifs](https://opendata.paris.fr/explore/dataset/occupation-des-creneaux-sportifs/information/)" ,
"Direction de la Jeunesse et des Sports — Ville de Paris" ,
"Open Database License (ODbL)" ,
sprintf ("`data/paris/occupation-des-creneaux-sportifs.parquet` (%s ko) + `arrondissements.parquet`" ,
round (file.size (cren_path) / 1024 )),
format (as.Date (file.mtime (cren_path)), "%d %B %Y" ),
sprintf ("%s créneaux, %d équipements, %s clubs" , format (n_tot, big.mark = " " ), n_equip,
format (n_clubs, big.mark = " " ))
)
) |>
gt:: gt () |> gt:: fmt_markdown (columns = ` ` ) |>
gt:: tab_options (table.width = "100%" , column_labels.hidden = TRUE )
Jeu de données
Occupation des créneaux sportifs
Producteur
Direction de la Jeunesse et des Sports — Ville de Paris
Licence
Open Database License (ODbL)
Fichiers
data/paris/occupation-des-creneaux-sportifs.parquet (344 ko) + arrondissements.parquet
Mise à jour
04 septembre 2026
Observations
18 768 créneaux, 372 équipements, 1 750 clubs