5

I am wondering if there is an easy way to produce a bunch of tables or graphics with variable captions in knitr. The only way I know is this: (simplified from https://github.com/yihui/knitr-examples/blob/master/075-knit-expand.Rnw). But it is a drag to collect the output into src and then print it after the loop, because I want to write a function to produce such a loop from an arbitrary dataset.

\documentclass{article}
\title{Using knit\_expand() for templates}
\author{Yihui Xie}
\begin{document}

\maketitle
\tableofcontents

<<lm-mtcars, tidy.opts=list(width.cutoff=55)>>=
# the template
tpl = c("\\subsection{Regression on {{xvar}}}",
        "<<lm-{{xvar}}>>=",
        "lm(mpg~{{xvar}}, data=mtcars)",
        "@")
# expand to knitr source and pass to knit()
src = lapply(names(mtcars)[-1], function(xvar) {knit_expand(text = tpl)})
@

\Sexpr{knit(text = unlist(src))}

\end{document}

So what I want to be able to do instead is something like this:

\documentclass{article}
\title{Using knit\_expand() for templates}
\author{Yihui Xie}
\begin{document}

\maketitle
\tableofcontents

<<lm, tidy.opts=list(width.cutoff=55)>>=
    myLfFun=function(dataset){
... some function definition which produces say an lm for each variable in dataset ...
}
@

\Sexpr{myLfFun(Titanic}
...
\Sexpr{myLfFun(mtcars}
... etc
\end{document}

... Which if I ran brew() on it would produce ...

\documentclass{article}
\title{Brew + knitR}
\author{Ramnath Vaidyanathan}
\begin{document}

\maketitle
\tableofcontents



<<lm-cyl >>=
lm(mpg ~ cyl, data = mtcars)
@

<<lm-disp >>=
lm(mpg ~ disp, data = mtcars)
@

<<lm-hp >>=
lm(mpg ~ hp, data = mtcars)
@

<<lm-drat >>=
lm(mpg ~ drat, data = mtcars)
@

<<lm-wt >>=
lm(mpg ~ wt, data = mtcars)
@

<<lm-qsec >>=
lm(mpg ~ qsec, data = mtcars)
@

<<lm-vs >>=
lm(mpg ~ vs, data = mtcars)
@

<<lm-am >>=
lm(mpg ~ am, data = mtcars)
@

<<lm-gear >>=
lm(mpg ~ gear, data = mtcars)
@

<<lm-carb >>=
lm(mpg ~ carb, data = mtcars)
@

((... same for Titanic database ...))

\end{document}

... and the output of the this I could then knit2pdf(). So if the template were called tmpl.Rnw, I would run brew('tmpl.Rnw','doc.Rnw');knit2pdf('doc.Rnw)

Steve Powell
  • 1,646
  • 16
  • 26
  • I do not get what exactly you are asking -- do you want "variable table/figure captions" or "a loop for arbitrary dataset"? You are not restricted to `mtcars` in this example. – Yihui Xie Jan 14 '13 at 20:12
  • Thanks. I would like "a function for an arbitrary dataset" - which would include a loop and would add appropriate captions for each variable. I work in survey research so I frequently have very wide datasets each containing blocks of related variables which all need processing in similar ways (show histograms and tables for each variable in the block, etc). I thought this is a frequent use case for knitr? – Steve Powell Jan 16 '13 at 07:29

3 Answers3

3

I don't see why you need knit_expand when good old sprintf can do the same. Here is the output: http://www.anst.uu.se/chrba104/stackoverflow/output.pdf.

Although my template is also custom made for the mtcars dataset, I don't see how you could make it simpler without losing flexibility.

\documentclass{article}
\title{Not using knit\_expand() for templates}
\author{Yihui Xie}
\begin{document}

\maketitle
\tableofcontents

<<lm-mtcars, tidy.opts=list(width.cutoff=55)>>=
vars <- setdiff(names(mtcars), 'mpg')
src <- sprintf(
    paste('\\subsection{Regression on %s}',
          '<<lm-%s>>=',
          'lm(mpg ~ %s, data=mtcars)',
          '@', sep='\n'),
    vars, vars, vars)
@
\Sexpr{knit(text = src)}

\end{document}
Backlin
  • 14,612
  • 2
  • 49
  • 81
  • you are absolutely right, but I do not know if this answers the question – Yihui Xie Jan 14 '13 at 20:03
  • Thanks for the answer, I hadn't thought of that, but no, it doesn't really answer the question. I just want to write a function myfun=function(someVarsPerhapsWithAttributesForUsingAsCaptions){... produce e.g. a histogram of each var and use e.g. the label attribute as a caption and actually print it as latex, end of story} – Steve Powell Jan 14 '13 at 20:17
  • I don't really see what you want to do I am afraid. Am I correct in thinking your example (and mine) give you the desired output, but you are looking for a simpler way to write it? – Backlin Jan 15 '13 at 09:09
  • Thanks - yes both examples give me the desired output. But I want to have the result printed before the end of the chunk so I don't need the `\Sexpr{knit(text = src)}` outside the chunk. This is all because I just want to write a function for use with knitr which either a) will loop through some vars and print tables and/or graphics based on each var, with their captions, or b) can be used inside a loop and will print tables and/or graphics for one var and its caption. Neither example achieves this because of the necessity for the separate \Sexpr at the end. Hope I am understandable now ;-) – Steve Powell Jan 15 '13 at 09:21
  • Thanks, I understand what you want to do now but not why. Is the problem **A)** that there are situations where building up a character vector and calling `\Sexpr` is not able produce what you want or **B)** that it is tedious to do it when the template gets long? Perhaps several pages with many figures, tables and text. – Backlin Jan 15 '13 at 09:46
  • Basically **B**, which is why I need to write a function for looping, (and potentially also call that function from a higher-level loop). I work in survey research so I frequently have very wide datasets which all need processing in similar ways. I guess this is a frequent use case for knitr? If only I could put the `knit(text = src)` inside the chunk instead of after it - but if I put it inside the chunk, I just get garbage output. – Steve Powell Jan 15 '13 at 14:47
  • I see... I can't figure out how to do that unfortunately. Personally I would just make do with what you already have. – Backlin Jan 16 '13 at 13:38
3

I prefer to use dedicated templating libraries like whisker and brew to achieve what you are seeking, since trying to write latex code using an R function IMHO is plain ugly. The template file is shown below and named tpl.Rnw. You can turn it into a pdf by running the following commands. You can easily writeup a function to encapsulates this logic that transforms brew templates into pdf using knitr.

brew('tpl.Rnw', 'doc.Rnw') 
knit2pdf('doc.Rnw')        

Template File tpl.Rnw

\documentclass{article}
\title{Brew + knitR}
\author{Ramnath Vaidyanathan}
\begin{document}

\maketitle
\tableofcontents


<% for (xvar in names(mtcars)[-1]) { %>

\subsection{Regression on <%= xvar %>}

<<lm-<%= xvar %> >>=
lm(mpg ~ <%= xvar %>, data = mtcars)
@

<% } %>

\end{document}
Ramnath
  • 54,439
  • 16
  • 125
  • 152
  • that is another great idea, and it also works. But I still don't know how to make any of these suggestions reusable, i.e. to turn it into a function. – Steve Powell Jan 15 '13 at 20:54
  • Can you post a better example, since I am not clear what exactly you are looking for? Why don't you use the `mtcars` dataset and describe the output you are looking for. We will be able to help you better once you do that. – Ramnath Jan 15 '13 at 21:33
  • I followed your suggestion and added to the original question. What the three of you have helped me clarify is that my problem isn't really with the loop, it is with writing functions for use with knitr. Thanks! – Steve Powell Jan 15 '13 at 21:52
  • The details help. I am still not clear what your function returns. Can you chalk up a simple function using mtcars to show us what are the outputs. I believe the solutions is really simple, once you provide a sample function. – Ramnath Jan 15 '13 at 22:01
0

I found out why I couldn't put the \Sexpr{knit(text = unlist(src))} line inside the previous normal code chunk. I needed to set opts_knit$set(progress = F, verbose = F) at the start of the doc and set at least some of comment=NA, warning=FALSE,message=FALSE,echo=FALSE for the chunk. This simple move makes it much to paste lines like knit(text = unlist(src)) wherever I want and as many times I want in a chunk. This obviates the need for a dedicated function.

Steve Powell
  • 1,646
  • 16
  • 26