pianoplunkster pianoplunkster - 3 months ago 33
LaTeX Question

Knitr: Use nested loops to generate multiple reports

A previous post on StackOverflow had a very useful guide on how to use a template .Rnw file to produce multiple reports. I'd like to duplicate this, except I'd have 4 loops rather than just the one used in the example.

In my case, these are the loops that I'm using to produce the reports:


  • outermost loop: looping through a vector that contains the names of the variables I want to analyze

  • inner 3 loops: looping through different seasons/geographic locations



Here is a sample of the template code, which works without any issues when I supply all of the necessary variables that would be provided in the loops:

\begin{document}
This is a test in which the \texttt{\Sexpr{varname}} variable is used in this report. If successful, further reports can be generated using a loop with this same script!

First, a plot of the CDFs, with Kolmogorov-Smirnov statistics:
\begin{figure}[h]
\centering
<<cdf-plots,dev='png',out.width='0.5\\linewidth',echo=FALSE,warning=FALSE,fig.align='centering'>>=
chart_stats(varname,data_vec,labs,season,s,h,colvec=colvec,cdf_plot=TRUE)
cap1=paste('Cumulative distribution functions for the counts,',season,s,h)
@
\caption{\Sexpr{cap1}}
\end{figure}
<<ks_calc,warning=FALSE,results='asis',echo=FALSE>>=
library(xtable)
ks=chart_stats(varname,data_vec,labs,season,s,h,ks_test=TRUE,dval=TRUE)
cap=paste('D-values for',season,h,s)
print(xtable(ks,caption=cap))
@


For the sake of being able to see my output, each file is currently generated as a separate .tex file as shown:

library(knitr)
setwd("~/data_netcdf")
load("loaded_data.Rdata")
source("~/tempestextremes/test/chart_stats.R")

data_vec<-c("ERA", "climo", "2xCO2","SSTplus2","SSTplus2_2xCO2")

seasons_vec<-c("DJF","JJA","MAM","SON")
sec=c("ATL","PAC")
hemi=c("NH","SH")
var=c('centlat','centlon','area')

for (varname in var){
}
for (season in seasons_vec){
for (h in hemi){
for (s in sec){
output_name=paste(varname,'_',season,'_',h,'_',s,'_report.tex',sep="")
knit2pdf("~/data_netcdf/report_test.Rnw",output=output_name)
}
}
}
}


There is a knitr example that makes use of child files to combine outputs into one document, which sounds similar to what I'd like to do, and here's what I've tried:

<<test-main,include=FALSE>>=
[removed for length]

#testing on just one variable name
var=c('centlat')
for (varname in var){
out=NULL
for (season in seasons_vec){
for (h in hemi){
for (s in sec){
out=c(out,knit_child("~/data_netcdf/report_test.Rnw"))
}
}
}
}
@

\Sexpr(paste(out,collapse='\n'))


but I'm getting the following error, probably due to the fact that I have multiple levels of loops which are all using the same chunk label names as references for the figures:

Error in parse_block(g[-1], g[1], params.src) :
duplicate label 'cdf-plots'
Calls: knit ... process_file -> split_file -> lapply -> FUN -> parse_block


Can someone please explain this error message? Do I need to somehow alter the label names so that it differentiates between each time it loops? Or should I implement the 3 inner loops within the template file? Or, perhaps combining all of the .tex files after the fact?

I would appreciate help in figuring out the best workflow for this.

Answer

I have come up with a solution! Some notes:

1) the template file cannot have the \begin{document}...\end{document} tags or anything in the preamble, this must be in the main .Rnw file.

2) As of this moment, it only works for a single variable (rather than looping over the vector of variables), but it's a trivial matter to scale this up.

So here is an example of the template:

<<{{prefix}}-setup>>=
varname='{{varname}}'
season='{{season}}'
h='{{h}}'
s='{{s}}'
@

First, a plot of the CDFs, with Kolmogorov-Smirnov statistics:
\begin{figure}[h]
\centering
<<'{{prefix}}-cdf-plots',dev='png',fig.lp='{{prefix}}',out.width='0.5\\linewidth',echo=FALSE,warning=FALSE,fig.align='centering'>>=
colvec=c("blue","red","green","purple","pink")
chart_stats(varname,data_vec,labs,season,s,h,colvec=colvec,cdf_plot=TRUE)
cap1=paste('Cumulative distribution functions for the counts,',season,s,h)
@
\caption{\Sexpr{cap1}}
\end{figure}

And then the main document:

\documentclass{article}
\usepackage[margin=0.5in]{geometry}
\begin{document}

<<test-main,include=FALSE>>=
library(knitr)
setwd("~/data_netcdf")
load("loaded_data.Rdata")
source("~/tempestextremes/test/charts_stats.R")
[other stuff]
@
...
<<generate-code,echo=FALSE>>=
  varname='centlat'
  out=NULL
  for (season in seasons_vec){
    for (h in hemi){
      for (s in sec){
        prefix=paste(season,h,s,sep="_")
        out=c(out,knit_expand("~/data_netcdf/report_test_1.Rnw"))
      }
    }
  }
@

\Sexpr{paste(knit(text=out),collapse='\n')}
\end{document}

I should note that the inspiration came from the knitr-examples section, specifically this one.