Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

knitr plots, labels, and captions within one chunk

Tags:

r

knitr

I am producing a latex report which produces multiple plots in a dlply call. The dlply call is of course in a single chunk and in order to get labels and captions to change I am using a snippet from Steve Powell below. The approach works but it seems knitr doesnt quite format the output correctly. A simple example that demonstrates:

\documentclass{article}

\begin{document}
<startup,echo=FALSE,results='hide',message=FALSE,tidy=FALSE,warning=FALSE,fig.keep='all',comment=NA>>=
require(knitr)
require(ggplot2)
opts_knit$set(progress = F, verbose = F)
opts_chunk$set(comment=NA,
           tidy=FALSE,
           warning=FALSE, 
           message=FALSE, 
           echo=FALSE, 
           dpi=600,
           fig.width=6.75, fig.height=4, # Default figure widths
           dev=c("pdf",'tiff'),
           dev.args=list(pdf=list(NULL),tiff=list(compression='lzw')),
           error=FALSE)
@
<<plotloop,results='asis'>>=
for(x in seq(1,20)){
  x1<-data.frame(x=seq(1,10),y=seq(1,10))
  plt<-ggplot(data=x1,aes(x,y))+geom_point()
  figLabel=paste('Figure',x,sep='')
  capt<-paste('Caption for fig.',x)
  cat(knit(text=(paste("<<",figLabel,",fig.pos='ht',fig.cap='",capt,"'>>=\nplt\n@",sep=''))))
}
@
\end{document}

This almost works. The trouble is that knitr places the closing \caption brace outside the \label brace which can be seen in the snippet from the .tex file below:

\begin{knitrout}
\definecolor{shadecolor}{rgb}{0.969, 0.969, 0.969}
\color{fgcolor}
\begin{figure}[ht]
\includegraphics[width=\maxwidth]{figure/Figure1} \caption[Caption for fig]{Caption for fig. 1\label{fig:Figure1}}
\end{figure}
\end{knitrout}

latex can handle this if there are only a few figures like this but with larger numbers of plots, it starts to place them incorrectly. I have also tried this with a

fig.cap=paste('testLoop',seq(1,20))

approach and get the same result.

Further clarification: I found this on wikipedia's Latex/Floats... page:

If you want to label a figure so that you can reference it later, you have to add the label after the caption (inside seems to work in LaTeX 2e) but inside the floating environment. If it is declared outside, it will give the section number.

The 'inside seems to work in LaTeX 2e' part caught my attention. It seems it works only because the error is ignored a number of times? I am using LaTeX2e <2005/12/01>. I think the bit of code is in hook_plot_tex function line 120 of hooks-latex.R:

fig2 = sprintf('\\caption%s{%s\\label{%s}}\n\\end{%s}\n', scap, cap,
                 paste(lab, if (mcap) fig.cur, sep = ''), options$fig.env)  

This would fix it?

fig2 = sprintf('\\caption%s{%s}\\label{%s}\n\\end{%s}\n', scap, cap,
                 paste(lab, if (mcap) fig.cur, sep = ''), options$fig.env)

Suggestions? I am not familiar with the github process... Thanks!

like image 940
JAllen Avatar asked Feb 10 '14 19:02

JAllen


1 Answers

Short answer is it seems to be a LaTeX issue caused by too many \includegraphics commands and no pagebreaks. Function to accomplish multiple figures with captions and labels from within loop (with credit to Steve Powell and Yihui):

plot.knit<-function(chunkLabel,#text for chunk label which is also used for figure file name
                capt,#text for caption
                plt)#plot object to be placed
   {
    cat(knit(text=(paste("<<",chunkLabel,",fig.pos='h',fig.cap='",capt,"'>>=\nplt\n@",sep=''))))
}
cat('\\newpage')#some sort of page break must be inserted along the way to keep latex from breaking.

This can be modified to add any of chunk options you would like.

Long Answer: Here is what I did to get it to work. I downloaded knitr from github, made the suggested alteration above, compiled, and ran example. The altered code did not change the outcome. Further investigation of latex error took me to the LaTeX FAQ where it states:

The error also occurs in a long sequence of float environments, with no intervening text. Unless the environments will fit “here” (and you’ve allowed them to go “here”), there will never be a page break, and so there will never be an opportunity for LaTeX to reconsider placement. (Of course, the floats can’t all fit “here” if the sequence is sufficiently prolonged: once the page fills, LaTeX won’t place any more floats, leading to the error.

Techniques for resolution may involve redefining the floats using the float package’s [H] float qualifier, but you are unlikely to get away without using \clearpage from time to time.

So, I added

cat('\\clearpage')

after the plots are generated in each step of the loop. This resulted in no errors being thrown and the figures in correct locations. Also,

cat('\\newpage')

works and seems to do a better job at placing the figures 2 on a page in my actual document.

The working code:

\documentclass{article}

    \begin{document}
<<startup,echo=FALSE,results='hide',message=FALSE,tidy=FALSE,warning=FALSE,fig.keep='all',comment=NA>>=
require(knitr)
require(ggplot2)
opts_knit$set(progress = F, verbose = F)
opts_chunk$set(comment=NA,
           tidy=FALSE,
           warning=FALSE, 
           message=FALSE, 
           echo=FALSE, 
           dpi=600,
           fig.width=6.75, fig.height=4, # Default figure widths
           dev=c("pdf",'tiff'),
           dev.args=list(pdf=list(NULL),tiff=list(compression='lzw')),
           error=FALSE)
@
<<plotloop,results='asis'>>=
for(x in seq(1,20)){
  x1<-data.frame(x=seq(1,10),y=seq(1,10))
  plt<-ggplot(data=x1,aes(x,y))+geom_point()
  figLabel=paste('Figure',x,sep='')
  capt<-paste('Caption for fig.',x)
  cat(knit(text=(paste("<<",figLabel,",fig.pos='h',fig.cap='",capt,"'>>=\nplt\n@",sep=''))))
cat('\\newpage')
}
@

\end{document}
like image 80
JAllen Avatar answered Oct 10 '22 06:10

JAllen