0

I am trying to produce a document using Sweave or Knitr which contains plots.

Here is a minimal example for Sweave:

\documentclass{article}
\title{Plot example}
\begin{document}
\maketitle
<<setup>>=
library("graphics")
myplot = function(n) {
    plot(cumsum(rnorm(n)),type="l",main="The stock market",ylab="Money",xlab="Time")
}
@
This is what the stock market looks like over the past month:
\begin{center}
<<fig=T,echo=F,width=10,height=4>>=
myplot(10)
@
\end{center}
and over the past decade:
\begin{center}
<<fig=T,echo=F,width=10,height=4>>=
myplot(1000)
@
\end{center}
\end{document}

The Knitr version has slightly different chunk headers:

<<echo=F,fig.width=10,fig.height=4>>=

The output looks like this:

example output

In both cases, the width and height of the plots are hard-coded in the source file. This seems to me like bad software design. I would prefer to specify the width and height of the plots within the function myplot(). Otherwise, if I want to change the way the plots look, or if I want to switch from letter paper to A4 paper, then I have the cumbersome task of changing several hard-coded width and height values using search and replace.

In Knitr, it is possible to change the default width and height through R code:

<<cache=FALSE>>=
opts_chunk$set(fig.width=3,fig.height=4);
@

However, this configuration must be separate from the code chunk in which the plot is executed, because Knitr (like Sweave) opens the plotting device before evaluating any of the chunk code. What I need is a way to specify the height and width within myplot().

What is an elegant way to specify the plot width and height together with the rest of the plot in Sweave or Knitr?

Metamorphic
  • 732
  • 6
  • 16
  • 1
    Chunk options can take arbitrary R expressions as their values. These values don't have to be hard-coded (e.g. as numbers). More info here: https://bookdown.org/yihui/rmarkdown-cookbook/chunk-variable.html That said, I don't think it is possible to change the option values from inside `myplot()`, because that would be too late (**knitr** has to open the graphical device before evaluating the code chunk, and the size needs to be specified at that time). – Yihui Xie Aug 24 '20 at 16:27

1 Answers1

0

Here is a new example I came up with, which attempts to be more illustrative of a practical need for programmatic control over plot size. In this example, which uses knitr, myplot writes a plot to a PDF file, and then uses asis_output to emit an \includegraphics statement that references the same PDF file. This statement includes a width parameter that is calculated from the dimensions of the PDF, so that the two diagrams render with elements of the same size.

\documentclass{article}
\title{knitr example with variable plot width using asis\_output}
\begin{document}
\maketitle
\begin{center}
<<echo=F>>=
library("graphics")
myplot=function(n, trim=0.4) {
  fn=sprintf("myplot-%d.pdf",n)
  nds=letters[1:n]
  ijs=expand.grid(i=1:n,j=1:n)
  edges=matrix(nrow = length(nds), ncol= length(nds),
    ijs$i == ijs$j+1)
  arrowlabels=ifelse(edges,"","_"); # "_" == absent, below
  width=n+1
  height=2
  pdfwidth=width-2*trim
  pdfheight=height-2*trim
  # plotmat expects coordinates in [0,1]
  repos=function(pos) {
    cbind((pos[,1]-trim)/pdfwidth,(pos[,2]-trim)/pdfheight)
  }
  pos=repos(cbind(1:n,1));
  pdf(fn, height=pdfheight, width=pdfwidth);
  par(mai=0*(1:4))
  plotmat(arrowlabels, pos=pos, name=nds,
    box.size=0.3/pdfwidth,
    curve=0, arr.lwd=1, arr.type="simple", arr.length=0.5,
    arr.pos=0.68,
    box.type="circle", box.col="white",
    shadow.size=0, absent="_",
    txt.font=3 # italic
  )
  dev.off();
  renderwidth=sprintf("%0.2f\\textwidth",pdfwidth/8);
  asis_output(paste0("\\fbox{\\includegraphics[width=",renderwidth,"]{",fn,"}}"))
}
@
\end{center}

\begin{center}
  \noindent A linked list could have as many as four elements: \\
\Sexpr{myplot(4)} \\
or even seven: \\
\Sexpr{myplot(7)}
\end{center}

\end{document}

And the output:

example output

I made the following changes to get it to work with Sweave. The Sweave version is slightly less concise because I couldn't figure out how to invoke \Sexpr in results=tex mode.

   dev.off();
   renderwidth=sprintf("%0.2f\\textwidth",pdfwidth/8);
-  asis_output(paste0("\\fbox{\\includegraphics[width=",renderwidth,"]{",fn,"}}"))
+  cat(paste0("\\fbox{\\includegraphics[width=",renderwidth,"]{",fn,"}}"))
 }
 @
 \end{center}

 \begin{center}
   \noindent A linked list could have as many as four elements: \\
-\Sexpr{myplot(4)} \\
+<<results=tex,echo=F>>=
+  myplot(4)
+@
+\\
 or even seven: \\
-\Sexpr{myplot(7)}
+<<results=tex,echo=F>>=
+  myplot(7)
+@
+\\
 \end{center}

 \end{document}
Metamorphic
  • 732
  • 6
  • 16