2

I have 2 plots of the exact same thing, excepts the colors filling the bars are different on each plot. Since the legend on the different plots have different widths because of the size of the names on it, the ratio between graph and legend becomes different on each plot. I need to make both look the same.

This is an example:

library(ggplot2)

x = c(rep("a",20),rep("b",10))
y = c(x = c(rep("BIGTEXT",20),rep("EVENBIGGERTEXT",10)))
df = data.frame(x,y)

p1 = ggplot(df,aes(x,fill=x)) + geom_bar()
p2 = ggplot(df,aes(y,fill=y)) + geom_bar()

p1
p2
VFreguglia
  • 2,129
  • 4
  • 14
  • 35

2 Answers2

3

You can set the gtable widths to a common value,

library(gtable)
library(grid)
gl <- lapply(list(p1, p2), ggplotGrob)

gwidth <- do.call(unit.pmax, lapply(gl, "[[", "widths"))
gl <- lapply(gl, "[[<-", "widths", value = gwidth)
gridExtra::grid.arrange(grobs=gl)

Alternatively, you can set the panel size to a fixed value.

Community
  • 1
  • 1
baptiste
  • 75,767
  • 19
  • 198
  • 294
  • 1
    very elegant, but this results the legend keys in slightly different positions between the plots. Is there a way to force the equating of widths down to the legend gtable? – user20650 Apr 15 '16 at 22:38
  • 2
    @user20650 it's certainly possible, but somewhat fiddly because i) the whole thing is poorly documented (one might expect `legend.margin` or `legend.justification` to help with this, but no), ii) ggplot keeps changing its layout structure over time; iii) gtable never addressed justification in a convenient way, requiring deeper and deeper levels of nested gtables that are hard to navigate. The question wasn't specific on this issue, so I'm not sure what is the intended use case. – baptiste Apr 15 '16 at 22:53
0

Following up @baptiste's comment: Fiddly but yes, it can be done. But no guarantees that this will work in future versions. The solution was taken from here, but that solution too needed updating.

library(ggplot2)   # v2.2.1
library(gtable)    # v0.2.0
library(grid)
library(gridExtra) # v2.2.1

x = c(rep("a",20),rep("b",10))
y = c(x = c(rep("BIGTEXT",20),rep("EVENBIGGERTEXT",10)))
df = data.frame(x,y)

p1 = ggplot(df,aes(x,fill=x)) + geom_bar()
p2 = ggplot(df,aes(y,fill=y)) + geom_bar()

# Get the grobs
gA <- ggplotGrob(p1)
gB <- ggplotGrob(p2)

# Get the widths of the legends
index = which(gA$layout$name == "guide-box")
leg1 <- convertX(sum(with(gA$grobs[[index]], grobs[[1]]$widths)), "mm")
leg2 <- convertX(sum(with(gB$grobs[[index]], grobs[[1]]$widths)), "mm")

# Add an empty column of width "abs(diff(c(leg1, leg2))) mm" to the right of 
# legend box for gA (the smaller legend box)
gA$grobs[[index]] <- gtable_add_cols(gA$grobs[[index]], unit(abs(diff(c(leg1, leg2))), "mm"))

# Set widths to maximums of corresponding widths
gl <- list(gA, gB)
gwidth <- do.call(unit.pmax, lapply(gl, "[[", "widths"))
gl <- lapply(gl, "[[<-", "widths", value = gwidth)


# Draw the plots
grid.newpage()
grid.draw(gl[[1]])

grid.newpage()
grid.draw(gl[[2]])
Community
  • 1
  • 1
Sandy Muspratt
  • 31,719
  • 12
  • 116
  • 122