ggplot2:在图例栏附近添加分布抖动

ggplot2: add distribution jitter near the legend bar

一个不平凡的 ggplot 挑战

我使用具有固定色标的地图比较变量在两个时刻的空间分布 - 以显示实际变化。最好将图例条附近的变量分布添加为抖动点。

所需的图应该看起来像图片:假设的 red 抖动点是手动添加的(我刚刚使用 paint.net)到 [= 生成的图12=].


复制地图

要复制地图,需要一个名为 fortITR 对象。这是一个强化的(使用 ggplot2::fortify)意大利 NUTS-2 区域的 SpatialPolygonsDataFrame,附有数据。 RData文件可以下载here[89KB]

地图代码:

require(dplyr)
require(ggplot2)
require(ggthemes)
require(gridExtra)
require(rgeos)
require(maptools)
require(cowplot)
require(viridis)

# load the data
load(url("https://ikashnitsky.github.io/share/1602-so-q-map-jitter/fortIT.RData"))

# produce the first map
gIT1 <- ggplot()+
        geom_polygon(data = fortIT, aes(x=long, y=lat, group=group, fill=tsr03),
                     color='grey30',size=.1)+
        scale_fill_viridis('TSR\n2003',limits=range(fortIT[,9:10]))+ # !!! limits fix the color scale
        
        coord_equal(xlim=c(4000000, 5500000), ylim=c(1500000,3000000))+
        guides(fill = guide_colorbar(barwidth = 1.5, barheight = 15))+
        
        theme_map()+
        theme(panel.border=element_rect(color = 'black',size=.5,fill = NA),
              legend.position = c(1, 1),
              legend.justification = c(1, 1),
              legend.background = element_rect(colour = NA, fill = NA),
              legend.title = element_text(size=15),
              legend.text = element_text(size=15))+
        scale_x_continuous(expand=c(0,0)) +
        scale_y_continuous(expand=c(0,0)) +
        labs(x = NULL, y = NULL)


# produce the second map
gIT2 <- ggplot()+
        geom_polygon(data = fortIT, aes(x=long, y=lat, group=group, fill=tsr43),
                     color='grey30',size=.1)+
        scale_fill_viridis('TSR\n2043',limits=range(fortIT[,9:10]))+
        
        coord_equal(xlim=c(4000000, 5500000), ylim=c(1500000,3000000))+
        guides(fill = guide_colorbar(barwidth = 1.5, barheight = 15))+
        
        theme_map()+
        theme(panel.border=element_rect(color = 'black',size=.5,fill = NA),
              legend.position = c(1, 1),
              legend.justification = c(1, 1),
              legend.background = element_rect(colour = NA, fill = NA),
              legend.title = element_text(size=15),
              legend.text = element_text(size=15))+
        scale_x_continuous(expand=c(0,0)) +
        scale_y_continuous(expand=c(0,0)) +
        labs(x = NULL, y = NULL)

# align both maps side by side
gIT <- plot_grid(gIT1,gIT2,ncol=2,labels=LETTERS[1:2],label_size=20)

ggsave('italy.png',gIT,width=12,height=7,dpi=192)

附加信息

地图中可视化的变量是 2003 年(面板 A)和 2043 年(面板 B,欧盟统计局区域预测)的总支持率。总支持率是工作年龄人口(15-64 岁)与非工作年龄人口(15 岁以下和 15 岁以上)的比率65).

您可以用带有密度信息的绘图面板替换图例,

g <- ggplotGrob(p)
leg = gtable_filter(g, "guide-box")

dd <- ddply(fortIT, "group", summarise, fill=unique(tsr03))
dum <- ggplot(dd, aes(0,y=fill)) +
  geom_dotplot(fill="red", binaxis = "y", dotsize=0.5, stackdir = "down")+
  scale_y_continuous(lim=range(fortIT[,c("tsr03", "tsr43")]), expand=c(0,0)) +
  theme_void() 

dummy_panel <- gtable_filter(ggplotGrob(dum), "panel")
dummy_panel$layout$clip <- FALSE

a <- leg[[1]][[1]][[1]][[1]]
a <- gtable_add_cols(a, unit(1,"cm"), 0)
a <- gtable_add_grob(a, dummy_panel, 4, 1)
a$layout$clip <- FALSE
grid.newpage()
grid.draw(a)

leg[[1]][[1]][[1]][[1]] <- a
g$grobs[g$layout$name=="guide-box"] <- list(leg)

library(grid)
grid.newpage()
grid.draw(g)

每当需要自定义图例时,我发现最好将图例绘制为单独的图,然后再合并。

例如,我们可以定义如下函数:

plot_legend <- function(dots, limits, title, bins = 20) {
  n <- 100
  tiles <- data.frame(x = rep(0.5, n),
                      y = seq(limits[1], limits[2], length.out = n))

  ggplot() +
    geom_raster(data=tiles, aes(x = x, y = y, fill = y), interpolate = TRUE) +
    geom_dotplot(data = data.frame(x = dots), aes(x = -.05, y = x, fill = ..y..),
                 stackdir = "down", binaxis = "y", binwidth = diff(limits)/bins, dotsize = .8) +
    scale_x_continuous(limits = c(-5, 1), expand = c(0, 0)) +
    scale_y_continuous(expand = c(0, 0), position = "right") +
    ggtitle(title) +
    theme_cowplot(12) +
    theme(axis.text.x = element_blank(),
          axis.ticks.x = element_blank(),
          axis.line = element_blank(),
          axis.title = element_blank(),
          plot.title = element_text(face = "plain", hjust = 1),
          legend.position = "none")
}

我们可以这样使用:

require(ggplot2)
require(cowplot)
require(viridis)

dots <- 3*runif(100)
range <- c(0, 3)
plot_legend(dots, range, "random numbers") + scale_fill_viridis()

现在我们将其与地图代码一起使用。需要对图例的最终位置进行一些调整,但并不过分复杂。

require(dplyr)
load(url("https://ikashnitsky.github.io/misc/160227-SO-question/fortIT.RData"))

# extract tsr03 and tsr43 data
fortIT %>% group_by(group) %>%
  summarize(tsr03 = tsr03[1], tsr43 = tsr43[1]) -> df_tsr

# get color range limits
limits <- range(fortIT[,9:10])

# make the legends
legIT1 <- plot_legend(df_tsr$tsr03, limits, "TSR 2003") + scale_fill_viridis()
legIT2 <- plot_legend(df_tsr$tsr43, limits, "TSR 2043") + scale_fill_viridis()

# produce the first map
gIT1 <- ggplot()+
  geom_polygon(data = fortIT, aes(x=long, y=lat, group=group, fill=tsr03),
               color='grey30', size=.1) +
  scale_x_continuous(expand=c(0,0)) +
  scale_y_continuous(expand=c(0,0)) +
  scale_fill_viridis('TSR\n2003', limits = limits, guide = "none") +
  coord_equal(xlim=c(4000000, 5500000), ylim=c(1500000, 3000000)) +
  theme_map() +
  theme(panel.border=element_rect(color = 'black',size=.5,fill = NA))

# produce the second map
gIT2 <- ggplot()+
  geom_polygon(data = fortIT, aes(x=long, y=lat, group=group, fill=tsr43),
               color='grey30',size=.1)+
  scale_x_continuous(expand=c(0,0)) +
  scale_y_continuous(expand=c(0,0)) +
  scale_fill_viridis('TSR\n2043', limits = limits, guide = "none") +
  coord_equal(xlim=c(4000000, 5500000), ylim=c(1500000, 3000000)) +
  theme_map() +
  theme(panel.border=element_rect(color = 'black',size=.5,fill = NA))

# put everything together
plot_grid(ggdraw(gIT1) + draw_plot(legIT1, .62, .35, .35, .55),
          ggdraw(gIT2) + draw_plot(legIT2, .62, .35, .35, .55),
          ncol=2, labels="AUTO")

两条评论:

  1. 堆叠点的大小可以通过plot_legend()函数的bins参数来控制。 bins越大点越小

  2. 我通常会删除每张地图周围的边框,但我在这里尝试尽可能接近地重现原始图形。