I was looking for something similar and ended up using gridExtra
package to arrange several choropleth maps. The result is the following plot, which resembles the one by Gelman:
I divided the code in 3 steps:
First: Create a list of choropleth maps for each category:
library(ggplot2)
library(dplyr)
library(maps)
library(gridExtra)
library(RGraphics)
# create a dataset ----
d1 <- map_data("state")
group_idx <- unique(d1$group)
n <- length(group_idx)
c1 = paste0("Income ", 1:5)
c2 = paste0("Age ", 1:4)
len_c1 = length(c1)
len_c2 = length(c2)
d2 <- data.frame(
group=sort(rep(group_idx, each=20)),
g1=rep(c1, n*len_c1*len_c2),
g2=rep(rep(c2, each=len_c1), n),
value=runif(n*20)
)
d <- merge(d1, d2, by="group")
# a list with several choropleth maps ----
plot_list <- lapply(1:len_c1, function(i) lapply(1:len_c2, function(j)
# the code below produces one map for category1=i and category2=j
ggplot(d[d$g1 == c1[i] & d$g2 == c2[j],])+
geom_polygon(aes(x=long, y=lat, group=group, fill=value))+
scale_fill_gradient(limits=c(min(d$value), max(d$value)))+
# aesthetics and remove legends
labs(x = NULL, y = NULL)+
theme(line = element_blank(),
axis.text = element_blank(),
axis.title = element_blank(),
panel.background = element_blank(),
legend.position="none")
)
)
Second: Extract a legend to use for all the maps (function to extract legend found here):
get_legend <- function(myggplot){
tmp <- ggplot_gtable(ggplot_build(myggplot))
leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
legend <- tmp$grobs[[leg]]
return(legend)
}
big_legend <- (ggplot(data.frame(x=1:4, y=runif(4)))+
geom_point(aes(x=x, y=y, fill=y))+
scale_fill_gradient(limits=c(min(d$value),
max(d$value)), name="")+
theme(legend.position="bottom",
legend.box = "horizontal")+
guides(fill = guide_colourbar(barwidth = 40,
barheight = 1.5))) %>%
get_legend()
grid.arrange(big_legend)
Third: Arrange maps and legend using gridExtra package:
# the plots can be organized using gridExtra:
grob_list <- lapply(1:len_c1, function(x) arrangeGrob(grobs=plot_list[[x]],
top = c1[x], ncol=1))
grob_c2 <- arrangeGrob(grobs=lapply(1:len_c2, function(x) textGrob(c2[x])),
ncol=1, top = " ")
maps_arranged <- arrangeGrob(grobs=union(list(grob_c2), grob_list),nrow=1)
# A layout matrix to the final arrange - each row with maps takes 2 rows,
# and the legend takes 1 row. The first grob (maps_arranged) have 6 cols,
# and the legend grob will ocupy 5 cols - lay is a (2*len_c2+1)x(len_c1+1) matrix
lay=matrix(1, nrow=2*len_c2+1, ncol=len_c1+1)
lay[9,1] <- NA
lay[9, 2:6] <- 2
grid.arrange(maps_arranged, big_legend, layout_matrix=lay)