diff --git a/module3/exo3/exercice_fr.Rmd b/module3/exo3/exercice_fr.Rmd index 7eece5e296bb586e88166aa8a263ca75b44c2b9e..543691d0f76733f3af79a32d9cef6f3fb4e64274 100644 --- a/module3/exo3/exercice_fr.Rmd +++ b/module3/exo3/exercice_fr.Rmd @@ -1,33 +1,214 @@ --- -title: "Votre titre" -author: "Votre nom" -date: "La date du jour" +title: "Sujet 3 : L'épidémie de choléra à Londres en 1854" +author: "Mathilde Foucteau" +date: "29/05/2026" output: html_document +editor_options: + chunk_output_type: console --- ```{r setup, include=FALSE} -knitr::opts_chunk$set(echo = TRUE) +knitr::opts_chunk$set( + echo = TRUE, + message = FALSE, + warning = FALSE +) ``` -## Quelques explications -Ceci est un document R markdown que vous pouvez aisément exporter au format HTML, PDF, et MS Word. Pour plus de détails sur R Markdown consultez . +```{r packages} +library(sf) +library(leaflet) +library(htmltools) +library(leaflet.extras) # nécessaire pour addHeatmap() +library(dplyr) +library(tidyr) +``` + +# 1. Projection des données sur une carte + +## Objectif + +L'objectif est de produire une carte inspirée de celle de John Snow où les lieux de décès sont représentés par des cercles dont la taille dépend du nombre de décès et où les pompes à eau sont représentées par un symbole distinct. Un fond de carte actuel est utilisé comme support spatial pour visualiser les données historiques. + +## Source des données + +Les données proviennent du jeu de données géographiques mis à disposition sur le [site Web de Robin Wilson](https://blog.rtwilson.com/john-snows-cholera-data-in-more-formats/). Nous les récupérons sous forme d'un fichier zip. L'URL est: + +```{r} +data_url = "https://rtwilson.com/downloads/SnowGIS_SHP.zip" + +``` + +Le script télécharge une copie locale du fichier uniquement si elle n'existe pas déjà, afin de rendre l'analyse reproductible sans retélécharger inutilement les données. + +```{r} +data_file = "SnowGIS_SHP.zip" +if (!file.exists(data_file)) { +download.file(data_url, data_file, method="auto") +} + +# Dézipper dans un sous-dossier +unzip(data_file, exdir = "SnowGIS_SHP") +``` +Chaque .shp vient avec ses fichiers compagnons (.dbf, .prj, .shx) qui doivent rester dans le même dossier. + +## Lecture et préparation des données + +Le document dézippé contient un dossier *_MACOSX* et un dossier *SnowGIS_SHP*. Le premier, vu son nom, est certainement fait pour les utilisateurs de MAC. J'utilise donc, pour ma part, les documents shp du second dossier. + +Avec le package sf, on lit les shapefile: +```{r} +library(sf) + +deaths <- st_read("SnowGIS_SHP/SnowGIS_SHP/Cholera_Deaths.shp") +pumps <- st_read("SnowGIS_SHP/SnowGIS_SHP/Pumps.shp") +``` + +Les fichiers sont fournis en British National Grid (EPSG:27700). Ils sont reprojetés en WGS84 (EPSG:4326), format attendu par `leaflet` pour être superposés à un fond OpenStreetMap. +```{r} +library(sf) + +deaths_wgs84 <- st_transform(deaths, crs = 4326) +pumps_wgs84 <- st_transform(pumps, crs = 4326) +``` + +## Carte +ON utilise le package leaflet, qui charge les tuiles OpenStreetMap : + +```{r} +icone_pompe <- makeAwesomeIcon( + icon = "tint", # nom de l'icône Font Awesome (tint = goutte d'eau) + library = "fa", # utiliser Font Awesome + markerColor = "blue", # couleur du marqueur + iconColor = "white" # couleur de l'icône à l'intérieur +) + +leaflet() %>% + addTiles() %>% + addCircleMarkers(data = deaths_wgs84, + radius = ~Count, + color = "red", + label = ~paste("Décès :", Count)) %>% + addAwesomeMarkers(data = pumps_wgs84, + icon = icone_pompe, + label = "Pompe") +``` + + +## Commentaire + +La carte montre une concentration importante des décès autour du secteur de Broad Street, à proximité d'une pompe à eau. Cette représentation ne démontre pas à elle seule une relation causale, mais elle permet de visualiser spatialement l'association qui a rendu la carte de John Snow célèbre. + +# 2. Approche alternative I + +Comme suggéré dans l'énoncé de l'exercice, il est possible de réprésenter les densitées de décès sur la carte sous forme d'une heatmap : + +```{r} + +leaflet() %>% + addTiles() %>% + addHeatmap(data = deaths_wgs84, + intensity = ~Count, + blur = 20, + max = 0.05, + radius = 15)%>% + addAwesomeMarkers(data = pumps_wgs84, + icon = icone_pompe, + label = "Pompe") +``` + +# 3. Approche alternative II + +Calcul de la distance entre chaque décès et chaque pompe : +L'idée est de montrer statistiquement que la majorité des décès est plus proche de la pompe de Broad Street que de toute autre pompe. + +D'abord, je donne un nom aux pompes selon la rue à laquelle elles appartiennent (ou le lieu d'intérêt le plus proche si le nom de la rue n'est pas visible): -Lorsque vous cliquerez sur le bouton **Knit** ce document sera compilé afin de ré-exécuter le code R et d'inclure les résultats dans un document final. Comme nous vous l'avons montré dans la vidéo, on inclue du code R de la façon suivante: +```{r} +noms_pompes <- c( + "1" = "Broad Street", + "2" = "Liberty", + "3" = "Ramillies Place", + "4" = "Dean Street", + "5" = "Rupert Street", + "6" = "Bridle Lane", + "7" = "Piccadilly Circus", + "8" = "Warwick Street" +) +``` + +Je calcule ensuite les distances entre chaque décès et chaque pompe +avec identification de la pompe la plus proche : + +```{r} +distances <- st_distance(deaths_wgs84, pumps_wgs84) +deaths_wgs84$pompe_proche <- apply(distances, 1, which.min) +``` + +Je calcule des statistiques de décès par pompe (nombre total et pourcentage sur le total des décès) + +```{r} +stats_pompes <- deaths_wgs84 %>% + st_drop_geometry() %>% + group_by(pompe_proche) %>% + summarise(total_deces = sum(Count)) %>% + mutate( + pourcentage = round(total_deces / sum(total_deces) * 100, 1), + nom_pompe = noms_pompes[as.character(pompe_proche)] + ) +``` + +Je joins les statistiques avec les données des pompes: + +```{r} +pumps_wgs84$pompe_id <- as.integer(seq_len(nrow(pumps_wgs84))) +stats_pompes$pompe_proche <- as.integer(stats_pompes$pompe_proche) -```{r cars} -summary(cars) +pumps_wgs84 <- left_join(pumps_wgs84, stats_pompes, by = c("pompe_id" = "pompe_proche")) + +# Remplacer les NA pour les pompes sans décès associés +pumps_wgs84$total_deces <- ifelse(is.na(pumps_wgs84$total_deces), 0, pumps_wgs84$total_deces) +pumps_wgs84$pourcentage <- ifelse(is.na(pumps_wgs84$pourcentage), 0, pumps_wgs84$pourcentage) +pumps_wgs84$nom_pompe <- ifelse(is.na(pumps_wgs84$nom_pompe), + noms_pompes[as.character(pumps_wgs84$pompe_id)], + pumps_wgs84$nom_pompe) ``` -Et on peut aussi aisément inclure des figures. Par exemple: +Je défnis une palette de couleurs par pompe : + +```{r} +palette_pompes <- colorFactor( + palette = "Set1", + domain = deaths_wgs84$pompe_proche +) -```{r pressure, echo=FALSE} -plot(pressure) ``` +et génère la carte interactive : -Vous remarquerez le paramètre `echo = FALSE` qui indique que le code ne doit pas apparaître dans la version finale du document. Nous vous recommandons dans le cadre de ce MOOC de ne pas utiliser ce paramètre car l'objectif est que vos analyses de données soient parfaitement transparentes pour être reproductibles. +```{r} +leaflet() %>% + addTiles() %>% + addCircleMarkers(data = deaths_wgs84, + radius = ~Count, + color = ~palette_pompes(pompe_proche), + fillOpacity = 0.7, + label = ~paste("Décès :", Count)) %>% + addAwesomeMarkers(data = pumps_wgs84, + icon = icone_pompe, + label = ~paste0(nom_pompe, + " - Décès : ", total_deces, + " (", pourcentage, "%)")) %>% + addLegend(position = "bottomright", + pal = palette_pompes, + values = deaths_wgs84$pompe_proche, + title = "Pompe la plus proche", + labFormat = labelFormat( + transform = function(x) noms_pompes[as.character(x)] + )) +``` -Comme les résultats ne sont pas stockés dans les fichiers Rmd, pour faciliter la relecture de vos analyses par d'autres personnes, vous aurez donc intérêt à générer un HTML ou un PDF et à le commiter. +## Conclusion -Maintenant, à vous de jouer! Vous pouvez effacer toutes ces informations et les remplacer par votre document computationnel. +Cette dernière représentation est la plus visuelle pour se rendre compte du lien entre la pompe de broad street et les morts du choléra en 1854 ! diff --git a/module3/exo3/exercice_fr.html b/module3/exo3/exercice_fr.html new file mode 100644 index 0000000000000000000000000000000000000000..30954fc6f94d7400155812237b4c9df3d2c2a9a7 --- /dev/null +++ b/module3/exo3/exercice_fr.html @@ -0,0 +1,4772 @@ + + + + + + + + + + + + + + +Sujet 3 : L’épidémie de choléra à Londres en 1854 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
library(sf)
+library(leaflet)
+library(htmltools)
+library(leaflet.extras)  # nécessaire pour addHeatmap()
+library(dplyr)
+library(tidyr)
+
+

1. Projection des données sur une carte

+
+

Objectif

+

L’objectif est de produire une carte inspirée de celle de John Snow +où les lieux de décès sont représentés par des cercles dont la taille +dépend du nombre de décès et où les pompes à eau sont représentées par +un symbole distinct. Un fond de carte actuel est utilisé comme support +spatial pour visualiser les données historiques.

+
+
+

Source des données

+

Les données proviennent du jeu de données géographiques mis à +disposition sur le site +Web de Robin Wilson. Nous les récupérons sous forme d’un fichier +zip. L’URL est:

+
data_url = "https://rtwilson.com/downloads/SnowGIS_SHP.zip" 
+

Le script télécharge une copie locale du fichier uniquement si elle +n’existe pas déjà, afin de rendre l’analyse reproductible sans +retélécharger inutilement les données.

+
data_file = "SnowGIS_SHP.zip"
+if (!file.exists(data_file)) {
+download.file(data_url, data_file, method="auto")
+}
+
+# Dézipper dans un sous-dossier
+unzip(data_file, exdir = "SnowGIS_SHP")
+

Chaque .shp vient avec ses fichiers compagnons (.dbf, .prj, .shx) qui +doivent rester dans le même dossier.

+
+
+

Lecture et préparation des données

+

Le document dézippé contient un dossier *_MACOSX* et un dossier +SnowGIS_SHP. Le premier, vu son nom, est certainement fait pour +les utilisateurs de MAC. J’utilise donc, pour ma part, les documents shp +du second dossier.

+

Avec le package sf, on lit les shapefile:

+
library(sf)
+
+deaths <- st_read("SnowGIS_SHP/SnowGIS_SHP/Cholera_Deaths.shp")
+
## Reading layer `Cholera_Deaths' from data source 
+##   `C:\Users\foucteau\Documents\8. Encadrements & formations\MOOC-Recherche_reproductible\training_directory\mooc-rr\module3\exo3\SnowGIS_SHP\SnowGIS_SHP\Cholera_Deaths.shp' 
+##   using driver `ESRI Shapefile'
+## Simple feature collection with 250 features and 2 fields
+## Geometry type: POINT
+## Dimension:     XY
+## Bounding box:  xmin: 529160.3 ymin: 180857.9 xmax: 529655.9 ymax: 181306.2
+## Projected CRS: OSGB36 / British National Grid
+
pumps  <- st_read("SnowGIS_SHP/SnowGIS_SHP/Pumps.shp")
+
## Reading layer `Pumps' from data source 
+##   `C:\Users\foucteau\Documents\8. Encadrements & formations\MOOC-Recherche_reproductible\training_directory\mooc-rr\module3\exo3\SnowGIS_SHP\SnowGIS_SHP\Pumps.shp' 
+##   using driver `ESRI Shapefile'
+## Simple feature collection with 8 features and 1 field
+## Geometry type: POINT
+## Dimension:     XY
+## Bounding box:  xmin: 529183.7 ymin: 180660.5 xmax: 529748.9 ymax: 181193.7
+## Projected CRS: OSGB36 / British National Grid
+

Les fichiers sont fournis en British National Grid (EPSG:27700). Ils +sont reprojetés en WGS84 (EPSG:4326), format attendu par +leaflet pour être superposés à un fond OpenStreetMap.

+
library(sf)
+
+deaths_wgs84 <- st_transform(deaths, crs = 4326)
+pumps_wgs84  <- st_transform(pumps, crs = 4326)
+
+
+

Carte

+

ON utilise le package leaflet, qui charge les tuiles OpenStreetMap +:

+
icone_pompe <- makeAwesomeIcon(
+  icon = "tint",        # nom de l'icône Font Awesome (tint = goutte d'eau)
+  library = "fa",       # utiliser Font Awesome
+  markerColor = "blue", # couleur du marqueur
+  iconColor = "white"   # couleur de l'icône à l'intérieur
+)
+
+leaflet() %>%
+  addTiles() %>%
+  addCircleMarkers(data = deaths_wgs84,
+                   radius = ~Count,
+                   color = "red",
+                   label = ~paste("Décès :", Count)) %>%
+  addAwesomeMarkers(data = pumps_wgs84,
+                    icon = icone_pompe,
+                    label = "Pompe")
+
+ +
+
+

Commentaire

+

La carte montre une concentration importante des décès autour du +secteur de Broad Street, à proximité d’une pompe à eau. Cette +représentation ne démontre pas à elle seule une relation causale, mais +elle permet de visualiser spatialement l’association qui a rendu la +carte de John Snow célèbre.

+
+
+
+

2. Approche alternative I

+

Comme suggéré dans l’énoncé de l’exercice, il est possible de +réprésenter les densitées de décès sur la carte sous forme d’une heatmap +:

+
leaflet() %>%
+  addTiles() %>%
+  addHeatmap(data = deaths_wgs84,
+             intensity = ~Count,
+             blur = 20,
+             max = 0.05,
+             radius = 15)%>%
+  addAwesomeMarkers(data = pumps_wgs84,
+                    icon = icone_pompe,
+                    label = "Pompe")
+
+ +
+
+

3. Approche alternative II

+

Calcul de la distance entre chaque décès et chaque pompe : L’idée est +de montrer statistiquement que la majorité des décès est plus proche de +la pompe de Broad Street que de toute autre pompe.

+

D’abord, je donne un nom aux pompes selon la rue à laquelle elles +appartiennent (ou le lieu d’intérêt le plus proche si le nom de la rue +n’est pas visible):

+
noms_pompes <- c(
+  "1" = "Broad Street",
+  "2" = "Liberty",
+  "3" = "Ramillies Place",
+  "4" = "Dean Street",
+  "5" = "Rupert Street",
+  "6" = "Bridle Lane",
+  "7" = "Piccadilly Circus",
+  "8" = "Warwick Street"
+)
+

Je calcule ensuite les distances entre chaque décès et chaque pompe +avec identification de la pompe la plus proche :

+
distances <- st_distance(deaths_wgs84, pumps_wgs84)
+deaths_wgs84$pompe_proche <- apply(distances, 1, which.min)
+

Je calcule des statistiques de décès par pompe (nombre total et +pourcentage sur le total des décès)

+
stats_pompes <- deaths_wgs84 %>%
+  st_drop_geometry() %>% 
+  group_by(pompe_proche) %>%
+  summarise(total_deces = sum(Count)) %>%
+  mutate(
+    pourcentage = round(total_deces / sum(total_deces) * 100, 1),
+    nom_pompe = noms_pompes[as.character(pompe_proche)]
+  )
+

Je joins les statistiques avec les données des pompes:

+
pumps_wgs84$pompe_id <- as.integer(seq_len(nrow(pumps_wgs84)))
+stats_pompes$pompe_proche <- as.integer(stats_pompes$pompe_proche)
+
+pumps_wgs84 <- left_join(pumps_wgs84, stats_pompes, by = c("pompe_id" = "pompe_proche"))
+
+# Remplacer les NA pour les pompes sans décès associés
+pumps_wgs84$total_deces <- ifelse(is.na(pumps_wgs84$total_deces), 0, pumps_wgs84$total_deces)
+pumps_wgs84$pourcentage <- ifelse(is.na(pumps_wgs84$pourcentage), 0, pumps_wgs84$pourcentage)
+pumps_wgs84$nom_pompe   <- ifelse(is.na(pumps_wgs84$nom_pompe),
+                                   noms_pompes[as.character(pumps_wgs84$pompe_id)],
+                                   pumps_wgs84$nom_pompe)
+

Je défnis une palette de couleurs par pompe :

+
palette_pompes <- colorFactor(
+  palette = "Set1",
+  domain = deaths_wgs84$pompe_proche
+)
+

et génère la carte interactive :

+
leaflet() %>%
+  addTiles() %>%
+  addCircleMarkers(data = deaths_wgs84,
+                   radius = ~Count,
+                   color = ~palette_pompes(pompe_proche),
+                   fillOpacity = 0.7,
+                   label = ~paste("Décès :", Count)) %>%
+  addAwesomeMarkers(data = pumps_wgs84,
+                    icon = icone_pompe,
+                    label = ~paste0(nom_pompe,
+                                    " - Décès : ", total_deces,
+                                    " (", pourcentage, "%)")) %>%
+  addLegend(position = "bottomright",
+            pal = palette_pompes,
+            values = deaths_wgs84$pompe_proche,
+            title = "Pompe la plus proche",
+            labFormat = labelFormat(
+              transform = function(x) noms_pompes[as.character(x)]
+            ))
+
+ +
+

Conclusion

+

Cette dernière représentation montre bien le taux de décès beaucoup +plus important autour de la pompe de Broad street !

+
+
+ + + + +
+ + + + + + + + + + + + + + +