1

I would like to have plots generated by ggplot2 inserted into a LaTeX document with the panel as wide as \textwidth (or \columnwidth in a two column document). I have the following solution:

\documentclass{article}
\usepackage{lipsum, graphicx}

<<knitrOpts, echo=FALSE>>=
knitr::opts_chunk$set(echo = FALSE,
                      fig.show = 'hide',
                      fig.width=general_fig_width <- 5, 
                      fig.height=3, 
                      out.width = out_width <- "5in",
                      out.height="3in"
                      )
@
\usepackage{geometry}

\setlength{\textwidth}{\Sexpr{out_width}}

<<loadPackages>>=
library(ggplot2)
library(dplyr)
library(grid)
@

\begin{document}
<<plot>>=
diamonds %>%
  sample_frac(0.3) %>%
  ggplot(aes(x = carat, y = price)) + 
  geom_point() + 
  theme_dark() + 
  theme(plot.margin = unit(c(0,0,0,0), "pt"))

grid.ls(view=TRUE,grob=FALSE)
current.vpTree()
seekViewport('panel.3-4-3-4')
a <- convertWidth(unit(1,'npc'), 'inch', TRUE)


width_factor <-  general_fig_width / a
@

\lipsum
\begin{figure}[t]
\makebox[\textwidth][r]{\includegraphics[width=\Sexpr{width_factor}\textwidth]{figure/plot-1}}
\end{figure}
\lipsum

\end{document}

enter image description here

However, the solution does not work when a legend is added:

diamonds %>%
  sample_frac(0.3) %>%
  ggplot(aes(x = carat, y = price, color = price)) + 
  geom_point() + 
  theme_dark() + 
  theme(plot.margin = unit(c(0,0,0,0), "pt"))

enter image description here

The legend messes up the alignment. Setting the pos argument in \makebox won't work as the background is off-centre. I understand I could put the legend atop the chart, but I'd prefer to have the option to have the legend intrude into the margin.

Hugh
  • 15,521
  • 12
  • 57
  • 100

1 Answers1

4

you'll probably find it easier to query sizes if you work with the gtable,

---
title: "Untitled"
header-includes:
   - \usepackage{lipsum}
output: 
  pdf_document: 
    fig_caption: yes
    fig_crop: no
    keep_tex: yes
geometry: width=5in
---

```{r setup, include=FALSE}
library(ggplot2)
library(dplyr)
library(grid)
library(knitr)
general_fig_width <- 5
```

```{r plot, fig.show=FALSE}
p <- diamonds %>%
  sample_frac(0.3) %>%
  ggplot(aes(x = carat, y = price, color = price)) + 
  geom_point() + 
  theme_dark() + 
  theme(plot.margin = unit(c(0,0,0,0), "pt"))

g <- ggplotGrob(p)

if(getRversion() < "3.3.0"){
  g$widths <- grid:::unit.list(g$widths)
  g$widths[4] <- list(unit(general_fig_width, "in"))
} else {
  g$widths[4] <- unit(general_fig_width, "in")
}
fig_width <- convertWidth(sum(g$widths), "in", valueOnly = TRUE)
left_width <- convertWidth(sum(g$widths[1:3]), "in", valueOnly = TRUE)
ggsave('plot-tmp.pdf', width=fig_width, height=2)
```

\begin{figure}[!hb]
\hspace{`r -left_width`in}\includegraphics[width=`r fig_width`in]{plot-tmp}
\end{figure}

\lipsum[2]

enter image description here

baptiste
  • 75,767
  • 19
  • 198
  • 294
  • I get the error `Error in grid.Call(L_convert, x, as.integer(whatfrom), as.integer(whatto), : INTEGER() can only be applied to a 'integer', not a 'NULL'` on the line `fig_width <- `. It looks like the conversion `g$widths[4] <- unit(...)` has no effect. – Hugh Mar 19 '16 at 07:13
  • try `g$widths[4] <- list(unit(general_fig_width, "in"))` instead, I'm using R-devel and there was a recent change re `[<-.unit` – baptiste Mar 19 '16 at 07:16
  • Weirdly `fig_width <- convertWidth(sum(g$widths[-4])+unit(g$widths[4], "in"), "in", valueOnly = TRUE)` works just fine. – Hugh Mar 19 '16 at 07:22
  • that doesn't make much sense (and doesn't work for me). Did you try the updated version? – baptiste Mar 19 '16 at 07:26
  • Your version works too! I was working on a (kludgy) solution and found that replacing the offending line with that one avoided the error. – Hugh Mar 19 '16 at 07:31
  • which now makes sense to me because the issue was with `[<-.uni` – Hugh Mar 19 '16 at 07:32