Skip to content

Instantly share code, notes, and snippets.

@averissimo
Last active February 23, 2024 14:26
Show Gist options
  • Save averissimo/8804d3ffed0613f9f912c4ef3465fbc1 to your computer and use it in GitHub Desktop.
Save averissimo/8804d3ffed0613f9f912c4ef3465fbc1 to your computer and use it in GitHub Desktop.
Electricity plot
#' @examples
#' renv::install("isocountry")
#' renv::install("readr")
#' renv::install("dplyr")
#' renv::install("forcats")
#' renv::install("rlang")
#' renv::install("glue")
#' renv::install("readr")
#' renv::install("clauswilke/ggtextures")
library(isocountry)
library(ggplot2)
library(ggtextures)
library(dplyr)
library(forcats)
library(glue)
library(rlang)
library(readr)
country_emoji <- function(country_codes) {
vapply(
country_codes,
function(country_code) {
if (is.null(country_code) || isTRUE(is.na(country_code))) {
return("")
}
intToUtf8(127397 + strtoi(charToRaw(toupper(country_code)), 16L))
},
character(1)
)
}
#
# Order of Fuel Types (from most pollutent to least)
levels_factor <- c(
"Coal", "Gas", "Other Fossil", "Nuclear", "Bioenergy", "Hydro", "Wind",
"Solar", "Other Renewables"
)
levels_ordered <- set_names(seq_along(levels_factor), levels_factor)
colors_fuel <- c(
"#060607", "#292f34" , "#eeeeee", # Fossil
"#56B4E9", # Nuclear
"#E69F00", "#CC79A7", "#F0E442", "#D55E00", "#0072B2" # Renewables
)
textured_mask <- file.path(
"https://gist.githubusercontent.com",
"averissimo/8804d3ffed0613f9f912c4ef3465fbc1/raw",
"50b92ab34c8d8c7e73a4d5cfeb847b35b75ff417/noisy-texture.png"
)
text_color <- "#36454f"
#
#
url <- file.path(
"https://ember-climate.org",
"app/uploads/2022/07/yearly_full_release_long_format.csv"
)
dat <- read_csv(url) |>
filter(.data[["Area type"]] == "Country") |>
filter(.data[["EU"]] == 1) |>
left_join(
isocountry |>
select(iso_2 = "alpha_2", "Country code" = "alpha_3") |>
bind_rows(
tibble(
iso_2 = "XK",
"Country code" = "XKX"
)
)
) |>
relocate(iso_2, .after = "Country code") |>
# filter(.data[["Country code"]] %in% c("DEU", "PRT")) |>
filter(.data$Year == "2023")
# Filtered data to work with
dat_filtered <- dat |>
filter(.data$Unit != "%") |>
filter(
.data$Category == "Electricity generation",
.data$Subcategory == "Fuel"
) |>
mutate(total = sum(.data$Value)) |>
group_by(.data$Area) |>
mutate(
subtotal = sum(.data$Value),
polluting_order = sum(if_else(.data$Variable %in% c("Coal", "Gas", "Other Fossil"), .data$Value, 0))
) |>
ungroup() |>
mutate(
polluting_order = .data$polluting_order / .data$subtotal,
relative = .data$subtotal / .data$total,
Variable = fct(.data$Variable, levels = levels_factor)
) |>
select(-c("Area type", "Ember region", "EU", "OECD", "G7", "ASEAN"))
# Data for first plot
dat_plot <- dat_filtered |>
mutate(
Value = .data$Value / .data$subtotal * 100,
emoji = unlist(country_emoji(.data$iso_2)),
.after = "iso_2"
) |>
mutate(
relative_rounded = relative * 100,
relative_rounded = if_else(relative_rounded >= 1, round(relative_rounded, digits = 1), round(relative_rounded, digits = 2)),
label = glue("{.data$Area} {.data$emoji}"),
.before = "Area"
) |>
as_tibble() |>
mutate(
label = fct_reorder(.data$label, .data$polluting_order)
)
# ggplot(dat_plot) +
# aes(x = label, y = Value, fill = Variable) +
# geom_bar(stat = "identity", position = "stack") +
# coord_flip() +
# scale_fill_manual(values = colors_fuel) +
# theme_minimal()
# Data for manual rectangular plot
dat_plot_2 <- dat_plot |>
select(label, Area, Variable, Value, subtotal, total, relative, polluting_order, emoji) |>
mutate(levels_order = levels_ordered[.data$Variable]) |>
group_by(Area) |>
arrange(desc(polluting_order), subtotal, Area, levels_order) |>
mutate(
ymax = cumsum(lag(.data$Value, default = 0)), ymax = cumsum(.data$Value),
ymin = ymax - .data$Value
) |>
rowwise() |>
mutate(
# subtotal_norm = log(1 + subtotal)
subtotal_norm = max(subtotal, 50) # Minimal cap for the bar height
) |>
group_by(Area) |>
mutate(x = cumsum(if_else(row_number() == n(), subtotal_norm, 0))) |>
ungroup() |>
mutate(
xmin = cumsum(lag(x, default = 0)),
xmax = xmin + subtotal_norm
)
# Flip the plot manually
dat_plot_2_min <- dat_plot_2 |>
group_by(Area, label, relative, subtotal, emoji) |>
summarise(
ymin = min(ymin),
ymax = max(ymax),
xmin = first(xmin),
xmax = last(xmax),
.groups = "keep"
)
dat_plot_2_ready <- dat_plot_2 |>
rename(
xmin = ymin,
xmax = ymax,
ymin = xmin,
ymax = xmax
) |>
select(label, Variable, xmin, xmax, ymin, ymax, subtotal, emoji) |>
filter(xmin != xmax) # Bug in geom_textured_*
dat_plot_2_min_ready <- dat_plot_2_min |> rename(
xmin = ymin,
xmax = ymax,
ymin = xmin,
ymax = xmax
) |>
group_by(Area, label, relative) |>
mutate(
y = ymin + (ymax - ymin) / 2,
label_2 = glue("{emoji} {.data$subtotal} TWh ({round(relative * 100, digits = 2)}%)")
)
ggplot(dat_plot_2_ready) +
aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax, fill = Variable) +
geom_vline(xintercept = seq(0, 100, 25), color = "#777777ff", linetype = "dashed") +
geom_rect() +
geom_textured_rect(image = textured_mask, img_width = unit(.5, "in"), color = "#ffffff") +
geom_rect(data = dat_plot_2_min_ready, fill = "transparent", color = "white") +
geom_vline(xintercept = seq(0, 100, 25), color = "#eeeeee88", linetype = "dashed") +
scale_x_continuous(breaks = seq(0, 100, 25)) +
geom_text(
data = dat_plot_2_min_ready,
aes(y = y, label = label),
x = -.5,
size = 4,
inherit.aes = FALSE,
hjust = 1,
nudge_x = 2,
xlim = c(-15, 0),
force_pull = 10,
max.overlaps = Inf,
min.segment.length = 0,
segment.alpha = .4,
color = text_color
) +
geom_text(
data = dat_plot_2_min_ready,
aes(y = y, label = label_2),
x = 100.5,
size = 4,
inherit.aes = FALSE,
hjust = 0,
color = text_color
) +
geom_text(
data = data.frame(label = glue("Country total in TWh (% of EU)")),
aes(label = label),
y = max(dat_plot_2_ready$ymax) + 10,
x = 100.5,
hjust = 0,
size = 3,
inherit.aes = FALSE,
color = text_color
) +
scale_fill_manual(values = colors_fuel) +
theme_minimal() +
theme(
axis.title.y = element_blank(),
axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
legend.position = "top",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
text = element_text(family="Helvetica-Narrow"),
plot.margin = margin(0, 0, 0, 0, "pt"),
legend.title = element_text(
family = "Helvetica-Narrow",
color = text_color,
size = 10
),
legend.text = element_text(
family = "Helvetica-Narrow",
color = text_color,
size = 10
),
title = element_text(family = "Helvetica-Narrow", color = text_color)
) +
expand_limits(x = c(-6, 113)) +
# change legend so it only has 1 row
guides(fill = guide_legend(nrow = 1)) +
theme(plot.margin = margin(1, 1, 1, 1, "cm")) +
labs(
title = glue("2023 {country_emoji('eu')} Rankings: Electricity generation by fuel type in EU countries"),
subtitle = "Height of bars represents electricity generation per country in 2023",
fill = "Fuel type:",
x = "Share of electricity generation (%)",
caption = "Source: Anual electricity data, Ember."
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment