Last active
July 10, 2023 23:55
-
-
Save aster-hu/c406864c31b07d3b914059d4a88d5236 to your computer and use it in GitHub Desktop.
Create interactive 2023 Toronto mayoral election map in R with leaflet
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# See the blog post for details: | |
# https://asterhu.com/post/2023-07-11-toronto-mayor-by-election-analysis/ | |
library(opendatatoronto) | |
library(dplyr) | |
library(tidyr) | |
library(sf) | |
library(leaflet) | |
library(htmltools) | |
# Importing data from Open Data Toronto ----------------------------------- | |
# Import from open data toronto and convert to data frame | |
elections <- | |
list_package_resources("6b1a2631-9b12-4242-a76a-1a707b5c00e4") %>% | |
tail(1) %>% | |
get_resource() %>% | |
as.data.frame() | |
# Flattening and cleaning the data ---------------------------------------- | |
# Flattening list-column to regular columns | |
elections <- elections %>% | |
unnest(office.candidate.ward) | |
# Remove unnecessary columns and rename ward related columns | |
elections <- elections %>% | |
select(7:15) %>% | |
rename(wardName = name, | |
wardNum = num) | |
# Change data types to numeric except candidate and ward | |
elections <- elections %>% | |
mutate(across(-c(office.candidate.name, wardName), as.numeric)) | |
# Verify the updated data types | |
str(elections) | |
# Data wrangling to prepare for visualization ----------------------------- | |
# Filter the top five candidates | |
top_candidates <- elections %>% | |
group_by(wardName, office.candidate.name) %>% | |
mutate(ward_votes = sum(votesReceived)) %>% | |
group_by(wardName) %>% | |
slice_max(ward_votes, n = 5) | |
# Get the winner for each ward | |
ward_winner <- top_candidates %>% | |
group_by(wardName) %>% | |
slice_max(ward_votes) | |
# Check the number of winners | |
unique(ward_winner$office.candidate.name) | |
# Define colours for each winner | |
ward_winner <- ward_winner %>% | |
mutate(winner_colour = if_else( | |
office.candidate.name == "Ana Bailão", | |
"#9dbd89", | |
"#a989bd")) %>% | |
select(wardName, winner_colour) | |
names_label <- top_candidates %>% | |
group_by(wardName) %>% | |
arrange(desc(ward_votes)) %>% | |
mutate(names = ifelse( | |
row_number() == 1, | |
paste("<b>", wardName, "</b><br>", paste(office.candidate.name, ":", votesReceived, collapse = "<br>")), | |
paste(office.candidate.name, ":", votesReceived)), | |
collapse = "<br>") %>% | |
mutate(names = lapply(names, htmltools::HTML)) %>% | |
distinct(wardName, .keep_all = TRUE) %>% | |
select(wardName, names) | |
to_shapes <- read_sf("Input/25-ward-model-december-2018-wgs84-latitude-longitude/WARD_WGS84.shp") | |
top_sf <- | |
left_join(top_candidates, to_shapes,by = c("wardName" = "AREA_NAME")) %>% | |
left_join(., ward_winner, by = "wardName") %>% | |
left_join(., names_label, by = "wardName") %>% | |
st_as_sf() | |
# Creating the interactive map with leaflet ------------------------------- | |
# Define the legend | |
legends <- tibble(lg_labels = c("Olivia Chow", | |
"Ana Bailão"), | |
lg_colours = c("#a989bd", | |
"#9dbd89")) | |
# Create the interactive map | |
map <- leaflet(options = leafletOptions(minZoom = 10.5, maxZoom = 18)) %>% | |
addProviderTiles("CartoDB.Positron") %>% | |
addPolygons(data = top_sf, fillColor = ~winner_colour, | |
fillOpacity = 0.2, color = "white", weight = 0.5, smoothFactor = 1, | |
label = ~names, | |
labelOptions = labelOptions(textsize = "12px")) %>% | |
addLegend(position = "bottomright", colors = legends$lg_colours, | |
labels = legends$lg_labels, title = "The candidate won the most votes") | |
# Print the map | |
map |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment