ggplot:"size-robust" 将水平图例放置在右下角的方法

ggplot: "size-robust" way to place horizontal legend at the bottom-right

我正在尝试在绘图区域外的右下角放置一个水平图例。

我知道之前已经讨论过了。然而,经过一个漫长的令人沮丧的早晨,我无法找到一个“尺寸稳健”的解决方案。

以下是我找到的 3 个解决方案:


方案三定位不正确:


可重现代码:

# Generate random data------

sample.n = 50
sample.data = data.frame(x = runif(sample.n,0,1), y = runif(sample.n,0,1), value = runif(sample.n,0,10))

# Plot ------

library(ggplot2)

# Margins are fine. And If I change the size of the plotting area, the legend will always be bottom- centered
# Problem: Can't push it to the right side  
ggplot(sample.data, aes(x = x, y = y, color = value)) + geom_point() + 
  theme(legend.direction = "horizontal", legend.position = "bottom") 

# Pushed to the bottom-right Successfully 
# Problem: Legend inside the plot
ggplot(sample.data, aes(x = x, y = y, color = value)) + geom_point() + 
  theme(legend.direction = "horizontal",legend.position = c(1,0)) # Problem: inside plot 

# Placed at the bottom-right corner outside the plot region 
# Problem: If I change the size of the plotting region, the legend will not be cornred
ggplot(sample.data, aes(x = x, y = y, color = value)) + geom_point() + 
  theme(legend.direction = "horizontal", legend.position = c(0.9,-0.2),  
        plot.margin = unit(c(1, 1, 5, 1), "lines"))  

Theme-Legend 的文档:

legend.position the position of legends ("none", "left", "right", "bottom", "top", or two-element numeric vector)

legend.justification anchor point for positioning legend inside plot ("center" or two-element numeric vector)

Edit 使用 2.2.0,可以在主题中使用 legend.justificationlegend.margin 将图例推到右边。

# Generate random data------

sample.n = 50
sample.data = data.frame(x = runif(sample.n,0,1), y = runif(sample.n,0,1), value = runif(sample.n,0,10))

# Plot ------

library(ggplot2)
library(gtable)
library(grid)

p = ggplot(sample.data, aes(x = x, y = y, color = value)) + geom_point() + 
  theme(legend.direction = "horizontal", legend.position = "bottom",
          legend.box.background = element_rect(colour = "black"))

p + theme(legend.justification = "right",
          legend.margin = margin(t = 2, r = 0, b = 2, l = 2, unit = "mm"))


对于它的价值,我保留了原来的答案,但更新到了 ggplot 版本 2.2.0。

我在这里所做的是从绘图中提取图例,将图例包裹在视口中,使视口位于右侧,将图例放回绘图中,并删除原始图例。

# Generate random data------

sample.n = 50
sample.data = data.frame(x = runif(sample.n,0,1), y = runif(sample.n,0,1), value = runif(sample.n,0,10))

# Plot ------

library(ggplot2)
library(gtable)
library(grid)

p = ggplot(sample.data, aes(x = x, y = y, color = value)) + geom_point() + 
  theme(legend.direction = "horizontal", legend.position = "bottom")

# Get the ggplot grob
g = ggplotGrob(p)

# Get the legend
index = which(g$layout$name == "guide-box")
leg = g$grobs[[index]]

# Wrap the legend in a viewport
leg$vp = viewport(x = unit(1, "npc"), width = sum(leg$widths), just = "right")

### Put the legend back into the plot
     # First, get the current location of the legend
pos = g$layout[index, ]
g = gtable_add_grob(g, leg, t = pos$t, l = pos$l)

# Remove the original legend
g$grobs <- g$grobs[-index]
g$layout <- g$layout[-index, ]

# Draw the chart
grid.newpage()
grid.draw(g)

请注意,图例已向右移动,但图例的右边缘与绘图面板的右边缘并不完全对齐。这是因为图例具有内部边距。图例本身有边距,但该边距设置为 0 毫米。要使图例与绘图面板对齐,需要进行更多操作。在下文中,内部边距(和图例边距)设置为 0。此外,图例和图例的内部部分包裹在右对齐视口中。

# Get the ggplot grob
g = ggplotGrob(p)

# Get the legend
index = which(g$layout$name == "guide-box")
leg = g$grobs[[index]]

# Remove the legend right margin and an internal margin
leg$widths[4] = unit(0, "mm")
leg$grobs[[1]]$widths[5] = unit(0, "mm")

# Wrap the legend in a viewport
leg$vp = viewport(x = unit(1, "npc"), width = sum(leg$widths), just = "right")

# Wrap the internal part of the legend in a right justified viewport
leg$grobs[[1]]$vp = viewport(x = unit(1, "npc"), width = sum(leg$grobs[[1]]$widths), just = "right")

### Put the legend back into the plot
     # First, get the current location of the legend
pos = g$layout[index, ]
g = gtable_add_grob(g, leg, t = pos$t, l = pos$l)

# Remove the original legend
g$grobs <- g$grobs[-index]
g$layout <- g$layout[-index, ]

# Draw the chart
grid.newpage()
grid.draw(g)