Skip to content

Instantly share code, notes, and snippets.

@aster-hu
Last active July 10, 2023 23:55
Show Gist options
  • Save aster-hu/c406864c31b07d3b914059d4a88d5236 to your computer and use it in GitHub Desktop.
Save aster-hu/c406864c31b07d3b914059d4a88d5236 to your computer and use it in GitHub Desktop.
Create interactive 2023 Toronto mayoral election map in R with leaflet
# 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