1

Please consider this modified, but shamelessly filched code:

library(ggplot2)
library(gtable)
library(gridExtra)
p1 <- ggplot(
  data.frame(
    x=c("a","b","longer"),
    y=c("happy","sad","ambivalent about")),
  aes(x=factor(0),fill=x)) + 
  geom_bar() +
  geom_point(aes(y=seq(3),color=y))
p2 <- ggplot(
  data.frame(
    x=c("a","b","c"),
    y=c("happy","sad","ambivalent about life")),
    aes(x=factor(0),fill=y)) + 
geom_bar()

# Get the widths
gA <- ggplot_gtable(ggplot_build(p1))
gB <- ggplot_gtable(ggplot_build(p2))

# The parts that differs in width
leg1 <- with(gA$grobs[[8]], grobs[[1]]$widths[[4]])
leg2 <- with(gB$grobs[[8]], grobs[[1]]$widths[[4]])

# Set the widths
gA$widths <- gB$widths

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

# Arrange the two charts
grid.newpage()
grid.arrange(gA, gB, nrow = 2)

Under these conditions the placement of the legends does not work as wanted as gA$grobs[[8]] has 2 entries and the code accesses explicitly the first one to determine the needed legend adjustment.

What I accordingly want to do is iterate over all entries in gA$grobs[[8]] and find the maximal width to use.

BTW:

library(gtable)
a <- gtable(unit(1:3, c("cm")), unit(5, "cm"))
a # See, "TableGrob" exists (somewhat) ;)

I hope this clears up what I intend to do.

Thanks for any pointers, Joh

Community
  • 1
  • 1
balin
  • 1,554
  • 1
  • 12
  • 26
  • gtable and gridExtra are different packages. I'm removing the tag, and you should probably not refer to a "TableGrob" (doesn't exist in either package). – baptiste Jun 20 '13 at 13:49
  • Sorry about that. I'm following http://stackoverflow.com/a/16258375/2103880 and as one of my legends has 2 rather than one entries, I'm hitting this problem ... need to descent into the table and extract the max width for the cited approach to work; – balin Jun 20 '13 at 13:50
  • I don't understand the question: you specify the widths of your gtable `a`, then add some `grobs` that fill the cells. What sizes are you trying to compare? – baptiste Jun 20 '13 at 13:57
  • [This](http://stackoverflow.com/a/16258375/2103880) recipe allows to create "fake" faceted ggplot2s with a legend for each facet. A nifty trick in one of the answers shows how to get the resulting legends left-alined. The trick presented there breaks if a plot has not one, but two legends/guides. In that case the corresponding grob holds multiple entries in a `TableGrob` and to make the white space padding work the bigger of the two needs identification. I am thus trying to emulate here a TableGrob with 2 entries and need to iterate over its (n) entries to find the wider one ... better? – balin Jun 20 '13 at 15:10
  • 4
    _please_, edit your question to make a minimal, self-contained and reproducible example of your exact problem. Again there's no such thing as a "TableGrob", and as far as I can tell the code in the present question doesn't illustrate the problem. Referring to an another question to better describe yours is not the right way to go. – baptiste Jun 20 '13 at 15:16
  • Done. Does this now work? – balin Jun 20 '13 at 16:02
  • Good edit. And you're correct, there's such a thing as a "TableGrob" (whatever that means jn gtable...) – baptiste Jun 20 '13 at 22:16

1 Answers1

0

IMHO you didn't pick the easiest answer to work with from that thread. Consider this simpler method,

rbind_gtable_max <- function (x, y) 
{
  stopifnot(ncol(x) == ncol(y))
  if (nrow(x) == 0) 
    return(y)
  if (nrow(y) == 0) 
    return(x)
  y$layout$t <- y$layout$t + nrow(x)
  y$layout$b <- y$layout$b + nrow(x)
  x$layout <- rbind(x$layout, y$layout)
  x$heights <- gtable:::insert.unit(x$heights, y$heights)
  x$rownames <- c(x$rownames, y$rownames)
  x$widths <- grid::unit.pmax(x$widths, y$widths)
  x$grobs <- append(x$grobs, y$grobs)
  x
}


gA <- ggplotGrob(p1)
gB <- ggplotGrob(p2)

both <- rbind_gtable_max(gA, gB)
grid.draw(both)

Edit if one wants the legend boxes to be left-aligned, it's more tricky. This is because ggplot2 uses gtable to fit the various components together, and gtable has no sense of justification within one cell. It's fine for components such as the plot panel, which always extends to take the full cell (viewport), but the guide grob has a fixed size and therefore appears centered.

You probably need to resort to manual calculations, indeed. To this end, note that in your example code, before you overwrote gA$widths you could access the full legend width with sum(gA$grobs[[8]]$width.

baptiste
  • 75,767
  • 19
  • 198
  • 294
  • Very elegant, but missing the point of getting all legends left-aligned - that's what the problem really is with above. – balin Jun 21 '13 at 21:25
  • 1
    the above will at least maintain the general layout in a decent shape, but legends are centered in their own viewport, that's true. Keeping them left-aligned across plots would be trickier (see my edit); unfortunately I don't have time now to expand more. I reckon it should be easy to adapt [this code](https://gist.github.com/baptiste/2877821) though. – baptiste Jun 21 '13 at 21:47