3

I'd like to align the width of the panel in some ggplots in an RMarkdown document. I can do it by moving the legend to the top or bottom, but that's not ideal. Is there a way to specify panel width with the legend to the right?

---
output: html_document
---

```{r}
library(ggplot2)
d <- data.frame(x = c("a", "b"), fill = c("short", "labels"))
ggplot(d, aes(x = x, fill = fill)) + 
  geom_bar()
```

```{r}
d$fill <- c("Now the labels are longer", "which compresses the plotting area")
ggplot(d, aes(x = x, fill = fill)) + 
  geom_bar()
```

Produces: html output

mlevy
  • 315
  • 2
  • 12

3 Answers3

2

egg::set_panel_size might help. It's a bit inconvenient though because you'll need to adjust the chunk's fig.width accordingly, which requires creating the plots in an earlier chunk.

---
output: html_document
---

```{r}
library(ggplot2)
library(egg)
library(grid)
d <- data.frame(x = c("a", "b"), fill = c("short", "labels"))
p1 <- ggplot(d, aes(x = x, fill = fill)) + geom_bar()
d$fill <- c("Now the labels are longer", "which compresses the plotting area")
p2 <- ggplot(d, aes(x = x, fill = fill)) + 
  geom_bar()
g1 <- egg::set_panel_size(p1, width=unit(4,"in"))
g2 <- egg::set_panel_size(p2, width=unit(4,"in"))
w1 <- convertWidth(sum(g1$widths), "in", TRUE)
w2 <- convertWidth(sum(g2$widths), "in", TRUE)
```

```{r, fig.width=w1}
p1
```

Some text.

```{r,fig.width=w2}
p2
```

enter image description here

baptiste
  • 75,767
  • 19
  • 198
  • 294
  • This is awesome. Unfortunately when output is html_notebook the variable-referenced `fig.width`s don't seem to be respected. – mlevy Oct 30 '17 at 23:37
1

Another option would be to extract the legend and lay it out as a separate grob (graphical object) using grid.arrange from the the gridExtra package. That way, you get the same panel size for the plot area of both plots so long as the widths argument is the same in grid.arrange for both plots.

---
output: html_document
---

```{r, include=FALSE}
library(ggplot2)
library(gridExtra)
knitr::opts_chunk$set(echo=FALSE)

# Extract the plot legend as a separate grob
# http://stackoverflow.com/questions/12539348/ggplot-separate-legend-and-plot
g_legend<-function(a.gplot){
  tmp <- ggplot_gtable(ggplot_build(a.gplot))
  leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
  legend <- tmp$grobs[[leg]]
  legend
}

thm = theme(legend.justification="left")

widths = c(8,5)
```

```{r}
d <- data.frame(x = c("a", "b"), fill = c("short", "labels"))
p1 = ggplot(d, aes(x = x, fill = fill)) + 
  geom_bar() +
  thm

leg = g_legend(p1)

grid.arrange(p1 + guides(fill=FALSE), leg, widths=widths)
```

```{r}
d$fill <- c("Now the labels are longer", "which compresses the plotting area")
p1 = ggplot(d, aes(x = x, fill = fill)) + 
  geom_bar() +
  thm

leg = g_legend(p1)

grid.arrange(p1 + guides(fill=FALSE), leg, widths=widths)
```

enter image description here

eipi10
  • 91,525
  • 24
  • 209
  • 285
  • Thanks, this works great. For plots that don't have a legend, `grid.arrange(p, nullGrob(), widths = widths)` is needed. – mlevy Oct 30 '17 at 23:57
  • Also, for html_notebook output at least, the `grid.arrange` line has to be in a different code chunk than the `g_legend` call or an empty canvas shows up. – mlevy Oct 31 '17 at 00:05
  • 1
    If you don't have a legend, you actually don't need `nullGrob()`. You can just do this: `grid.arrange(p, widths=widths)`. The `widths` argument results in the same spatial layout even if there isn't a second explicit grob. – eipi10 Oct 31 '17 at 05:53
0

There are some packages that allow you to arrange plots as desired. Take a look at egg:ggarrange function.

---
output: html_document
---

```{r}
library(ggplot2)
library(grid)
library(gridExtra)
library(egg)


d <- data.frame(x = c("a", "b"), fill = c("short", "labels"))
p1 <- ggplot(d, aes(x = x, fill = fill)) + 
  geom_bar() 
```

```{r}
d$fill <- c("Now the labels are longer", "which compresses the plotting area")
p2 <- ggplot(d, aes(x = x, fill = fill)) + 
  geom_bar() 
```

```{r}
grid.draw(ggarrange(p1, p2, heights = c(1, 1)))
```

ggarrange example

Enrique Pérez Herrero
  • 3,699
  • 2
  • 32
  • 33