With an amazing amount of assistance from Yihui I have been very
successful in customizing knitr to make good use of the listings
package and to automatically create figure environments when a
caption= parameter is specified inside <<>>=. I have slightly updated
the Sweavel LaTeX style file at
http://biostat.mc.vanderbilt.edu/wiki/pub/Main/SweaveTemplate/Sweavel.sty,
and I use the following knitr hooks/customizations:
# All this goes into file setup.r
spar <- function(mar=if(!axes)
c(2.25+bot-.45*multi,1.5+left,.5+top+.25*multi,.5+rt)
else
c(3.25+bot-.45*multi,3.5+left,.5+top+.25*multi,.
5+rt),
lwd = if(multi)1 else 1.75,
mgp = if(!axes) mgp=c(.75, .1, 0) else
if(multi) c(1.5, .365, 0) else c(2.4-.4, 0.475,
0),
tcl = if(multi)-0.25 else -0.4,
bot=0, left=0, top=0, rt=0, ps=if(multi) 14 else 10,
mfrow=NULL, axes=TRUE, ...)
{
multi <- length(mfrow) > 0
par(mar=mar, lwd=lwd, mgp=mgp, tcl=tcl, ps=ps, ...)
if(multi) par(mfrow=mfrow)
}
knit_hooks$set(chunk = function(x, options) x) # do not wrap output in
kframe
## for those who read source code here: I cannot use echo=TRUE here
because
## the code \begin{lstlistings} and \end{lstlistings} will confuse the
## listings package and cause LaTeX errors
hook_lst_bf = function(x, options)
paste('\\begin{lstlisting}[basicstyle={\\bfseries}]\n', x,
'\\end{lstlisting}\n', sep = '')
## Scode and Soutput are defined in Sweavel.sty using listings
unlink('messages.txt') # Start fresh with each run
hook_log = function(x, options) cat(x, file='messages.txt',
append=TRUE)
knit_hooks$set(source = function(x, options)
paste('\\begin{Scode}\n', x, '\\end{Scode}\n', sep=''),
output = function(x, options)
paste('\\begin{Soutput}\n', x, '\\end{Soutput}\n',
sep=''),
warning = hook_log, message = hook_log, error =
hook_lst_bf)
set_header(highlight = '')
knit_hooks$set(plot=function (x, options) {
if (!options$include)
return("")
## Begin FH
cap <- options$caption
bfig <- efig <- ''
if(length(cap)) {
if(
is.na(cap)) cap <- ''
lp <- options$label.prefix
lab <- paste(lp, options$label, sep='')
bfig <- sprintf('\\begin{figure}\\label{%s}\n', lab)
scap <- options$scaption
scap <- if(!length(scap) ||
is.na(scap)) '' else
paste('[', scap, ']', sep='')
efig <- sprintf('\\caption%s{\\smaller %s}\n\\end{figure}\n',
scap, cap)
} # End FH
rw = options$resize.width
rh = options$resize.height
resize1 = resize2 = ""
if (!is.null(rw) || !is.null(rh)) {
resize1 = sprintf("\\resizebox{%s}{%s}{", ifelse(is.null(rw),
"!", rw), ifelse(is.null(rh), "!", rh))
resize2 = "} "
}
tikz <- FALSE # FH
a = options$fig.align
fig.cur = options$fig.cur
fig.num = options$fig.num
if (is.null(fig.cur))
fig.cur = 0L
animate = options$fig.show == "animate"
if (!tikz && animate && fig.cur < fig.num)
return("")
align1 = switch(a, left = "\n\n", center = "\n\n\\centering{}",
right = "\n\n\\hfill{}", "")
align2 = switch(a, left = "\\hfill{}\n\n", center = "\n\n",
right = "\n\n", "")
hold = options$fig.show == "hold"
if (hold && fig.cur > 1L)
align1 = ""
if (hold && fig.cur > 0L && fig.cur < fig.num)
align2 = ""
size = paste(c(sprintf("width=%s", options$out.width),
sprintf("height=%s",
options$out.height)), collapse = ",")
paste(bfig, align1, resize1, if (tikz) { # FH added bfig,
sprintf("\\input{%s.tikz}", x[1])
}
else if (animate) {
aniopts = options$aniopts
aniopts = if (
is.na(aniopts))
NULL
else gsub(";", ",", aniopts)
if (nzchar(size))
size = paste(size, sprintf("%s", aniopts), sep = ",")
if (nzchar(size))
size = sprintf("[%s]", size)
sprintf("\\animategraphics%s{%s}{%s}{%s}{%s}", size,
1/options$interval, sub(str_c(fig.num, "$"), "",
x[1]), 1L, fig.num)
}
else {
if (nzchar(size))
size = sprintf("[%s]", size)
sprintf("\\includegraphics%s{%s} ", size, x[1])
}, resize2, align2, efig, sep = "") # FH added efig,
})
knit_hooks$set(par=function(before, options, envir)
if(before && options$fig.show != 'none')
{
p <-
c('bty','mfrow','ps','bot','top','left','rt','lwd',
'mgp','tcl', 'axes')
pars <- opts_current$get(p)
pars <- pars[!
is.na(names(pars))]
if(length(pars)) do.call('spar', pars) else spar()
})
opts_knit$set(eval.opts = c('eval', 'echo', 'caption', 'scaption',
'mfrow', 'mgp', 'tcl'))
## allows symbolic captions, mfrow, etc., short captions for table of
figures
## see
http://yihui.name/knitr/options#package_options
## Allow symbolic mfrom, mgp, etc. because user may specify e.g.
c(1,2)
options(replace.assign=TRUE)
I don't put warnings in the report output but rather to a separate
file messages.txt
In knit_hooks$set(plot=function ....) see lines marked with FH to see
what I changed over what is provided with knitr.
Then in my individual documents I use:
<<echo=FALSE>>=
source('../setup.r')
@
\SweaveOpts{highlight=FALSE, fig.path=PREFIX-, fig.align=center,
fig.width=4, fig.height=3, fig.show=hold, comment=NA,
label.prefix=fig:gen-, par=TRUE}
. . .
<<rcscomp,mfrow=c(1\,2),fig.height=4,fig.width=6,caption="Restricted
cubic spline component variables for $k = 5$ and knots at $X = .05\, .
275\, .5\, .725$\, and $.95$. The left panel is a $y$--magnification
of the right panel. Fitted functions such as those in Figure \
\ref{fig:gen-rcsex} will be linear combinations of these basis
functions as long as knots are at the same locations used
here.",scaption="Restricted cubic spline component variables for 5
knots">>=
. . .
@
<<rcsex,fig.height=6,fig.width=7,bot=2,mfrow=c(2\,
2),ps=13,caption="Some typical restricted cubic spline functions for
$k = 3\, 4\, 5\, 6$. The $y$--axis is $X\\beta$. Arrows indicate
knots. These curves were derived by randomly choosing values of $\\beta
$ subject to standard deviations of fitted functions being
normalized.",scaption="Some typical restricted cubic spline
functions">>=
. . .
@
You can also stuff captions into R variables in earlier chunks then
call out the caption with caption=variable name in a later chunk.
I'm very pleased with how this is working and plan to re-do my book
using this approach.
Thanks to Yihui and others who have helped so much. If any of this
becomes built-in to knitr I'll have even less work to do next time.
Frank