Deena
Deena

Reputation: 6213

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

I am trying to place a horizontal legend at the bottom-right corner, outside of the plotting area.

I realize that this has been discussed before. However, after a long frustrating morning, I wasn't able to reach a "size-robust" solution.

Here are the 3 solutions I've found:


Incorrect positioning of third solution:

enter image description hereenter image description here


Reproducible code:

# 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's documentation:

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)

Upvotes: 4

Views: 3666

Answers (1)

Sandy Muspratt
Sandy Muspratt

Reputation: 32789

Edit Using 2.2.0, the legend can be pushed to the right using legend.justification and legend.margin in the theme.

# 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"))

enter image description here


For what it is worth, I leave the original answer but updated to ggplot version 2.2.0.

What I have done here is extract the legend from the plot, wrap the legend in a viewport so that the viewport is positioned to the right, put the legend back into the plot, and remove the original legend.

# 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)

enter image description here

Note that the legend has been moved to the right, but that the right edge of the legend does not quite align with the right edge of the plot panel. This is because the legend has an internal margin. The legend itself has a margin but that margin is set to 0 mm. To get the legend to align with the plot panel requires a bit more fiddling. In what follows, the internal margin (and the legend margin) are set to 0. Also, the legend and the internal part of the legend are wrapped in right justified viewports.

# 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)

enter image description here

Upvotes: 3

Related Questions