I’m working with several plots where I compare “Pre” and “Post” slopes for different cities. For one of them (retail), I’ve already added alternating shaded bands behind the points using geom_rect()
.
Example (simplified):
bg_retail <- data.frame(
ymin = seq(0.5, max(df_retail_long$city_num), by = 2),
ymax = seq(1.5, max(df_retail_long$city_num) + 1, by = 2)
)
p_retail <- ggplot(df_retail_long, aes(x = slope, y = city_num, group = city)) +
geom_rect(data = bg_retail,
aes(xmin = -Inf, xmax = Inf, ymin = ymin, ymax = ymax),
inherit.aes = FALSE,
fill = "lightgrey", alpha = 0.2) +
geom_line(color = "lightgrey", linewidth = 1, alpha = 0.7) +
geom_point(aes(color = period), size = 4) +
scale_y_continuous(
breaks = unique(df_retail_long$city_num),
labels = unique(df_retail_long$city),
expand = expansion(add = c(0.5, 0.5))
)
This works fine for shading alternating rows in the plot panel, but what I’d really like is to also shade the y-axis labels themselves (so that the label text and its corresponding row of points are highlighted together).
How can I do this in ggplot
?
Full code (including my dataset):
pacman::p_load(ggplot2, patchwork, dplyr, stringr)
# airport data
df_airport <- data.frame(
city = c("Brisbane, Australia", "Delhi, India", "London, UK", "Manchester, UK",
"Shenzhen, China", "Guangzhou, China", "Los Angeles, USA", "Melbourne, Australia",
"Pune, India", "Mumbai, India", "New York, USA", "Santiago, Chile",
"Cairo, Egypt", "Milan, Italy", "Almaty, Kazakhstan", "Nairobi, Kenya",
"Amsterdam, Netherlands", "Lahore, Pakistan", "Jeddah, Saudi Arabia",
"Riyadh, Saudi Arabia", "Cape Town, South Africa", "Madrid, Spain",
"Abu Dhabi, UAE", "Dubai, UAE", "Sydney, Australia", "Hong Kong, China"),
pre_slope = c(-0.550, 0.0405, 0.263, 0.424, 0.331, -0.786, 0.187, -0.0562,
0.0187, 0.168, 0.0392, 0.0225, 0.0329, -0.0152, 0.174, -0.0931,
-0.121, -0.246, 0.294, 0.865, -0.503, 0.0466, 0.524, 0.983, 0.0440, -0.295),
post_slope = c(-0.393, 0.00300, 0.00839, -0.642, -0.595, -0.447, -0.0372, -0.0993,
-0.0426, -1.94, 0.00842, -0.903, -0.0127, -0.0468, 1.29, -0.337,
-0.435, -0.00608, -0.305, 0.203, 0.193, -0.202, -0.0637, 0.564, -0.0916, 0.768)
)
# industrial data
df_industrial <- data.frame(
city = c("Beijing, China", "Brisbane, Australia", "Chicago, USA", "Dallas, USA",
"Delhi, India", "London, UK", "Manchester, UK", "Shenzhen, China",
"Guangzhou, China", "Wuhan, China", "Los Angeles, USA", "Melbourne, Australia",
"Pune, India", "Mumbai, India", "New York, USA", "Buenos Aires, Argentina",
"Vienna, Austria", "Baku, Azerbaijan", "Santiago, Chile", "Cairo, Egypt",
"Paris, France", "Berlin, Germany", "Frankfurt, Germany", "Munich, Germany",
"Athens, Greece", "Rome, Italy", "Milan, Italy", "Almaty, Kazakhstan",
"Nairobi, Kenya", "Mexico City, Mexico", "Amsterdam, Netherlands", "Lahore, Pakistan",
"Lima, Peru", "Jeddah, Saudi Arabia", "Riyadh, Saudi Arabia", "Johannesburg, South Africa",
"Cape Town, South Africa", "Madrid, Spain", "Istanbul, Turkey", "Abu Dhabi, UAE",
"Dubai, UAE", "Caracas, Venezuela", "Rio de Janeiro, Brazil", "Shanghai, China",
"Sao Paulo, Brazil", "Sydney, Australia", "Toronto, Canada", "Washington DC, USA",
"Hong Kong, China"),
pre_slope = c(-0.00621, -0.851, -0.378, 0.0846, -0.0133, 0.361, -0.276, 0.175,
0.0299, -0.0127, 0.0874, -0.0666, 0.0245, 0.285, 0.0524, -0.0150,
-0.220, -0.137, 0.444, -0.0354, -0.00491, -0.0300, -0.816, -0.507,
-0.176, -0.237, -0.0117, 0.325, -0.110, 0.122, -2.45, -0.125,
0.126, -0.570, -0.590, -0.0271, -0.170, 0.0690, -0.158, -0.120,
0.310, -0.0893, -0.528, 0.647, 0.000298, 0.0735, 0.236, 0.0237, -0.521),
post_slope = c(0.0395, 0.594, 0.322, 0.248, 0.0337, 0.00941, -0.502, 0.154,
0.789, -0.0532, 0.0400, 0.0439, 0.0249, -1.14, -0.00410, 0.0205,
-0.821, 0.142, 0.219, -0.00623, -0.0432, -0.0191, -0.370, -0.328,
0.577, 0.0164, -0.00493, 0.841, 0.0101, -0.000736, 0.717, 0.00221,
-0.245, 0.0487, 0.363, -0.000446, -0.0949, -0.218, 0.0188, 0.356,
0.545, 1.21, -0.0900, -0.209, 0.212, 0.0787, -0.129, -0.587, 1.03)
)
# retail data
df_retail <- data.frame(
city = c("Brisbane, Australia", "Chicago, USA", "Dallas, USA", "Manchester, UK",
"Wuhan, China", "Los Angeles, USA", "Melbourne, Australia", "New York, USA",
"Buenos Aires, Argentina", "Baku, Azerbaijan", "Paris, France", "Rome, Italy",
"Milan, Italy", "Almaty, Kazakhstan", "Mexico City, Mexico", "Amsterdam, Netherlands",
"Lima, Peru", "Warsaw, Poland", "Riyadh, Saudi Arabia", "Johannesburg, South Africa",
"Madrid, Spain", "Caracas, Venezuela", "Sao Paulo, Brazil", "Sydney, Australia",
"Toronto, Canada"),
pre_slope = c(-0.321, -0.934, 0.831, -0.359, 0.0154, 0.0113, -0.100, 0.0510,
0.00658, 0.00571, -0.0320, -0.512, -0.00924, 0.0852, 0.154, 0.179,
0.151, -0.217, -0.798, -0.0394, 0.0503, 0.475, -0.0377, -0.0110, 0.438),
post_slope = c(-0.404, 0.391, 0.119, -1.05, -0.138, 0.0592, 0.0834, -0.0451,
-0.0296, 0.170, -0.112, 0.150, -0.0557, 0.114, -0.0217, 0.642,
-0.376, -0.0210, 0.663, -0.00313, -0.425, 1.45, 0.233, -0.0950, -0.686)
)
# prep data for plotting
prepare_data <- function(df) {
df$city_num <- 1:nrow(df)
df_long <- data.frame(
city = rep(df$city, 2),
city_num = rep(df$city_num, 2),
slope = c(df$pre_slope, df$post_slope),
period = rep(c("Pre", "Post"), each = nrow(df))
)
return(df_long)
}
df_airport_long <- prepare_data(df_airport)
df_industrial_long <- prepare_data(df_industrial)
df_retail_long <- prepare_data(df_retail)
# airport
p_airport <- ggplot(df_airport_long, aes(x = slope, y = city_num, group = city)) +
geom_line(color = "lightgrey", linewidth = 1, alpha = 0.7) +
geom_point(aes(color = period), size = 4) +
geom_vline(xintercept = 0, linetype = "dashed", color = "dark grey") +
scale_color_manual(values = c("Pre" = "#18685D", "Post" = "#B0280B"),
breaks = c("Pre", "Post")) +
scale_y_continuous(
breaks = unique(df_airport_long$city_num),
labels = unique(df_airport_long$city),
expand = expansion(add = c(0.5, 0.5))
) +
# ggtitle("Airport") +
theme_minimal(base_size = 18) +
theme(
panel.grid = element_blank(),
axis.line.x.bottom = element_line(color = "black", linewidth = .7),
axis.line.y.left = element_line(color = "black", linewidth = .7),
axis.title = element_blank(),
legend.position = "none"
)
# industrial
p_industrial <- ggplot(df_industrial_long, aes(x = slope, y = city_num, group = city)) +
geom_line(color = "lightgrey", linewidth = 1, alpha = 0.7) +
geom_point(aes(color = period), size = 4) +
geom_vline(xintercept = 0, linetype = "dashed", color = "dark grey") +
scale_color_manual(values = c("Pre" = "#18685D", "Post" = "#B0280B"),
breaks = c("Pre", "Post")) +
scale_y_continuous(
breaks = unique(df_industrial_long$city_num),
labels = unique(df_industrial_long$city),
expand = expansion(add = c(0.5, 0.5))
) +
# ggtitle("Industrial") +
theme_minimal(base_size = 18) +
theme(
panel.grid = element_blank(),
axis.line.x.bottom = element_line(color = "black", linewidth = .7),
axis.line.y.left = element_line(color = "black", linewidth = .7),
axis.title = element_blank(),
legend.title = element_blank(),
legend.position = "bottom",
legend.direction = "horizontal",
legend.spacing.y = unit(0, "cm"),
legend.margin = margin(t = -5, unit = "pt")
)
# retail
bg_retail <- data.frame(
ymin = seq(0.5, max(df_retail_long$city_num), by = 2),
ymax = seq(1.5, max(df_retail_long$city_num) + 1, by = 2)
)
p_retail <- ggplot(df_retail_long, aes(x = slope, y = city_num, group = city)) +
geom_rect(data = bg_retail,
aes(xmin = -Inf, xmax = Inf, ymin = ymin, ymax = ymax),
inherit.aes = FALSE,
fill = "lightgrey", alpha = 0.2) +
geom_line(color = "lightgrey", linewidth = 1, alpha = 0.7) +
geom_point(aes(color = period), size = 4) +
geom_vline(xintercept = 0, linetype = "dashed", color = "dark grey") +
scale_color_manual(values = c("Pre" = "#18685D", "Post" = "#B0280B"),
breaks = c("Pre", "Post")) +
scale_y_continuous(
breaks = unique(df_retail_long$city_num),
labels = unique(df_retail_long$city),
expand = expansion(add = c(0.5, 0.5))
) +
# ggtitle("Retail") +
theme_minimal(base_size = 18) +
theme(
panel.grid = element_blank(),
axis.line.x.bottom = element_line(color = "black", linewidth = .7),
axis.line.y.left = element_line(color = "black", linewidth = .7),
axis.title = element_blank(),
legend.position = "none"
)
# Combine plots
p_airport + p_industrial + p_retail + plot_layout(ncol = 3)
sessionInfo()
R version 4.5.1 (2025-06-13 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 11 x64 (build 26100)
Matrix products: default
LAPACK version 3.12.1
locale:
[1] LC_COLLATE=English_United States.utf8 LC_CTYPE=English_United States.utf8 LC_MONETARY=English_United States.utf8
[4] LC_NUMERIC=C LC_TIME=English_United States.utf8
time zone: Europe/Bucharest
tzcode source: internal
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] ggtext_0.1.2 patchwork_1.3.2 ggplot2_4.0.0 tidyplots_0.3.1 stringr_1.5.2 dplyr_1.1.4 sf_1.0-21
loaded via a namespace (and not attached):
[1] gtable_0.3.6 compiler_4.5.1 tidyselect_1.2.1 Rcpp_1.1.0 xml2_1.4.0 dichromat_2.0-0.1 systemfonts_1.3.1
[8] scales_1.4.0 textshaping_1.0.3 R6_2.6.1 labeling_0.4.3 generics_0.1.4 classInt_0.4-11 tibble_3.3.0
[15] units_0.8-7 DBI_1.2.3 svglite_2.2.1 pillar_1.11.1 RColorBrewer_1.1-3 rlang_1.1.6 stringi_1.8.7
[22] S7_0.2.0 cli_3.6.5 withr_3.0.2 magrittr_2.0.4 class_7.3-23 gridtext_0.1.5 grid_4.5.1
[29] rstudioapi_0.17.1 lifecycle_1.0.4 vctrs_0.6.5 KernSmooth_2.23-26 proxy_0.4-27 glue_1.8.0 farver_2.1.2
[36] ragg_1.5.0 e1071_1.7-16 pacman_0.5.1 purrr_1.1.0 tools_4.5.1 pkgconfig_2.0.3