Last active
November 22, 2024 02:12
-
-
Save USMortality/30655480e602001cb4399899975ed428 to your computer and use it in GitHub Desktop.
US Presidential Elections: Seven Major Polling-Based Forecasts
This file contains hidden or 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
# Load required libraries | |
library(ggplot2) | |
library(ggrepel) | |
library(grid) | |
library(dplyr) | |
library(magick) | |
library(gridExtra) | |
sf <- 2 | |
width <- 600 * sf | |
height <- 335 * sf | |
options(vsc.dev.args = list(width = width, height = height, res = 72 * sf)) | |
# Create the data frame with absolute forecast differences | |
data <- tibble( | |
Model = c( | |
"538", | |
"Economist", | |
"Princeton", | |
"270toWin", | |
"RCP", | |
"RaceToWH", | |
"SilverBulletin" | |
), | |
Difference = c( | |
49 - 50, | |
((262 / 276) - 1) * 100, | |
(((538 - 265) / 265) - 1) * 100, | |
((268 / 251) - 1) * 100, | |
((287 / 251) - 1) * 100, | |
((262.6 / 275.4) - 1) * 100, | |
49.6 - 50.0 | |
) | |
) |> | |
mutate(Favor = ifelse(Difference > 0, "Trump", "Harris")) | |
# Calculate the average absolute difference | |
avg_difference <- median(data$Difference) | |
# Create the lollipop chart | |
p <- ggplot(data, aes(x = Model, y = Difference)) + | |
geom_segment(aes(x = Model, xend = Model, y = 0, yend = Difference), | |
color = "gray", size = 1 | |
) + | |
geom_hline( | |
yintercept = avg_difference, linetype = "dashed", | |
color = "gray", size = 0.5 | |
) + | |
geom_hline( | |
yintercept = 0, linetype = "solid", | |
color = "black", size = 0.5 | |
) + | |
geom_point(aes(color = Favor), size = 8) + | |
geom_text(aes(label = abs(round(Difference, 1))), | |
color = "white", size = 3.5 | |
) + | |
scale_color_manual( | |
values = c("Harris" = "#00ba38", "Trump" = "#f8766d"), | |
guide = "none" | |
) + | |
annotate( | |
"text", | |
x = 8, y = avg_difference, | |
label = paste0("Median: ", abs(round(avg_difference, 1)), "%"), | |
color = "black", hjust = -.2, vjust = 2 | |
) + | |
labs( | |
title = "US Presidential Elections: Seven Major Polling-Based Forecasts", | |
subtitle = paste( | |
"Absolute forecast differences", | |
"Updated: 11/5/2024 10:00 AM EDT", | |
"@USMortality", | |
sep = " · " | |
), | |
y = "Lead" | |
) + | |
scale_y_continuous(labels = scales::percent_format(scale = 1)) + | |
coord_flip() + | |
theme_minimal() | |
# Load and resize the images using magick | |
img_trump <- image_read( | |
"https://www.economist.com/interactive/us-2024-election/Donald%20Trump.png" | |
) | |
img_harris <- image_read( | |
"https://www.economist.com/interactive/us-2024-election/Kamala%20Harris.png" | |
) | |
# Convert the images to grobs | |
grob_trump <- rasterGrob(as.raster(img_trump)) | |
grob_harris <- rasterGrob(as.raster(img_harris)) | |
png("chart1.png", width, height, res = 72 * sf) | |
grid.arrange( | |
grob_harris, p, grob_trump, | |
ncol = 3, widths = c(1, 5, 1) | |
) | |
dev.off() | |
# Betting Markets | |
# Create the data frame with absolute forecast differences | |
data <- tibble( | |
Model = c( | |
"Smarkets", | |
"Polymarket", | |
"PredictIt", | |
"Kalshi" | |
), | |
Difference = c( | |
60.24 - 39.68, | |
62.8 - 37.3, | |
57 - 50, | |
58 - 42 | |
) | |
) |> | |
mutate(Favor = ifelse(Difference > 0, "Trump", "Harris")) | |
# Calculate the average absolute difference | |
avg_difference <- median(data$Difference) | |
# Create the lollipop chart | |
p <- ggplot(data, aes(x = Model, y = Difference)) + | |
geom_segment(aes(x = Model, xend = Model, y = 0, yend = Difference), | |
color = "gray", size = 1 | |
) + | |
geom_hline( | |
yintercept = avg_difference, linetype = "dashed", | |
color = "gray", size = 0.5 | |
) + | |
geom_hline( | |
yintercept = 0, linetype = "solid", | |
color = "black", size = 0.5 | |
) + | |
geom_point(aes(color = Favor), size = 8) + | |
geom_text(aes(label = abs(round(Difference, 0))), | |
color = "white", size = 3.5 | |
) + | |
scale_color_manual( | |
values = c("Harris" = "#00ba38", "Trump" = "#f8766d"), | |
guide = "none" | |
) + | |
annotate( | |
"text", | |
x = 5, y = avg_difference, | |
label = paste0("Median: ", abs(round(avg_difference, 1)), "%"), | |
color = "black", hjust = -.1, vjust = 2 | |
) + | |
labs( | |
title = "US Presidential Elections: Four Major Betting-Based Odds", | |
subtitle = paste( | |
"Absolute forecast differences", | |
"Updated: 11/5/2024 10:00 AM EDT", | |
"@USMortality", | |
sep = " · " | |
), | |
y = "Lead" | |
) + | |
scale_y_continuous(labels = scales::percent_format(scale = 1)) + | |
coord_flip() + | |
theme_minimal() | |
# Load and resize the images using magick | |
img_trump <- image_read( | |
"https://www.economist.com/interactive/us-2024-election/Donald%20Trump.png" | |
) | |
img_harris <- image_read( | |
"https://www.economist.com/interactive/us-2024-election/Kamala%20Harris.png" | |
) | |
# Convert the images to grobs | |
grob_trump <- rasterGrob(as.raster(img_trump)) | |
grob_harris <- rasterGrob(as.raster(img_harris)) | |
png("chart2.png", width, height, res = 72 * sf) | |
grid.arrange( | |
grob_harris, p, grob_trump, | |
ncol = 3, widths = c(1, 5, 1) | |
) | |
dev.off() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment