knit2pdf(): knit and latex passes - r

I'm using knit2pdf("book.Rnw", quiet=TRUE) to compile a book project under
RStudio. The knit step takes a long time (I'm not yet using caching), and when
there are new references, figures, cross-references, etc. it takes several
passes to resolve them, even if the .Rnw files haven't changed.
What I'd like is an equivalent or extension of knit2pdf which allows
either knit=FALSE to suppress the regeneration of the .tex file, or
an option latex.passes= to request additional runs of tools::texi2pdf.
I've looked at the code in knit2pdf, and it is a bit too opaque to allow
a simple patch for this functionality.

All knit2pdf does is generate a .tex file and then call tools:texi2pdf. If you're looking for a version of knit2pdf that doesn't generate a .tex file first, it is exactly tools::texi2pdf.
Using stringr::str_replace, I do something like this and have found it sufficient:
knit2pdf_mod <- function(rnw_file) {
knit2pdf(rnw_file, compiler = "xelatex")
texi2pdf(file = str_replace(rnw_file, pattern = "Rnw", replacement = "tex"))
}
You could throw in a for loop to repeat the texi2pdf step as many times as you want.
knit2pdf_mod <- function(rnw_file, latex.passes = 1) {
knit2pdf(rnw_file, compiler = "xelatex")
for (i in 1:latex.passes) {
texi2pdf(file = str_replace(rnw_file, pattern = "Rnw", replacement = "tex"))
}
}

Related

How to dump png to stdout? [duplicate]

Is it possible to get R to write a plot in bitmap format (e.g. PNG) to standard output? If so, how?
Specifically I would like to run Rscript myscript.R | other_prog_that_reads_a_png_from_stdin. I realise it's possible to create a temporary file and use that, but it's inconvenient as there will potentially be many copies of this pipeline running at the same time, necessitating schemes for choosing unique filenames and removing them afterwards.
I have so far tried setting outf <- file("stdout") and then running either bitmap(file=outf, ...) or png(filename=outf, ...), but both complain ('file' must be a non-empty character string and invalid 'filename' argument, respectively), which is in line with the official documentation for these functions.
Since I was able to persuade R's read.table() function to read from standard input, I'm hoping there's a way. I wasn't able to find anything relevant here on SO by searching for [r] stdout plot, or any of the variations with stdout replaced by "standard output" (with or without double quotes), and/or plot replaced by png.
Thanks!
Unfortunately the {grDevices} (and, by implication, {ggplot2}) seems to fundamentally not support this.
The obvious approach to work around this is: let a graphics device write to a temporary file, and then read that temporary file back into the R session and write it to stdout.
But this fails because, on the one hand, the data cannot be read into a string: character strings in R do not support embedded null characters (if you try you’ll get an error such as “nul character not allowed”). On the other hand, readBin and writeBin fail because writeBin categorically refuses to write to any device that’s hooked up to stdout, which is in text mode (ignoring the fact that, on POSIX system, the two are identical).
This can only be circumvented in incredibly hacky ways, e.g. by opening a binary pipe to a command such as cat:
dev_stdout = function (underlying_device = png, ...) {
filename = tempfile()
underlying_device(filename, ...)
filename
}
dev_stdout_off = function (filename) {
dev.off()
on.exit(unlink(filename))
fake_stdout = pipe('cat', 'wb')
on.exit(close(fake_stdout), add = TRUE)
writeBin(readBin(filename, 'raw', file.info(filename)$size), fake_stdout)
}
To use it:
tmp_dev = dev_stdout()
contour(volcano)
dev_stdout_off(tmp_dev)
On systems where /dev/stdout exists (which are most but not all POSIX systems), the dev_stdout_off function can be simplified slightly by removing the command redirection:
dev_stdout_off = function (filename) {
dev.off()
on.exit(unlink(filename))
fake_stdout = file('/dev/stdout', 'wb')
on.exit(close(fake_stdout), add = TRUE)
writeBin(readBin(filename, 'raw', file.info(filename)$size), fake_stdout)
}
This might not be a complete answer, but it's the best I've got: can you open a connection using the stdout() command? I know that png() will change the output device to a file connection, but that's not what you want, so it might work to simply substitute png by stdout. I don't know enough about standard outputs to test this theory, however.
The help page suggests that this connection might be text-only. In that case, a solution might be to generate a random string to use as a filename, and pass the name of the file through stdout so that the next step in your pipeline knows where to find your file.

Writing a plot in bitmap format (e.g. PNG) to standard output

Is it possible to get R to write a plot in bitmap format (e.g. PNG) to standard output? If so, how?
Specifically I would like to run Rscript myscript.R | other_prog_that_reads_a_png_from_stdin. I realise it's possible to create a temporary file and use that, but it's inconvenient as there will potentially be many copies of this pipeline running at the same time, necessitating schemes for choosing unique filenames and removing them afterwards.
I have so far tried setting outf <- file("stdout") and then running either bitmap(file=outf, ...) or png(filename=outf, ...), but both complain ('file' must be a non-empty character string and invalid 'filename' argument, respectively), which is in line with the official documentation for these functions.
Since I was able to persuade R's read.table() function to read from standard input, I'm hoping there's a way. I wasn't able to find anything relevant here on SO by searching for [r] stdout plot, or any of the variations with stdout replaced by "standard output" (with or without double quotes), and/or plot replaced by png.
Thanks!
Unfortunately the {grDevices} (and, by implication, {ggplot2}) seems to fundamentally not support this.
The obvious approach to work around this is: let a graphics device write to a temporary file, and then read that temporary file back into the R session and write it to stdout.
But this fails because, on the one hand, the data cannot be read into a string: character strings in R do not support embedded null characters (if you try you’ll get an error such as “nul character not allowed”). On the other hand, readBin and writeBin fail because writeBin categorically refuses to write to any device that’s hooked up to stdout, which is in text mode (ignoring the fact that, on POSIX system, the two are identical).
This can only be circumvented in incredibly hacky ways, e.g. by opening a binary pipe to a command such as cat:
dev_stdout = function (underlying_device = png, ...) {
filename = tempfile()
underlying_device(filename, ...)
filename
}
dev_stdout_off = function (filename) {
dev.off()
on.exit(unlink(filename))
fake_stdout = pipe('cat', 'wb')
on.exit(close(fake_stdout), add = TRUE)
writeBin(readBin(filename, 'raw', file.info(filename)$size), fake_stdout)
}
To use it:
tmp_dev = dev_stdout()
contour(volcano)
dev_stdout_off(tmp_dev)
On systems where /dev/stdout exists (which are most but not all POSIX systems), the dev_stdout_off function can be simplified slightly by removing the command redirection:
dev_stdout_off = function (filename) {
dev.off()
on.exit(unlink(filename))
fake_stdout = file('/dev/stdout', 'wb')
on.exit(close(fake_stdout), add = TRUE)
writeBin(readBin(filename, 'raw', file.info(filename)$size), fake_stdout)
}
This might not be a complete answer, but it's the best I've got: can you open a connection using the stdout() command? I know that png() will change the output device to a file connection, but that's not what you want, so it might work to simply substitute png by stdout. I don't know enough about standard outputs to test this theory, however.
The help page suggests that this connection might be text-only. In that case, a solution might be to generate a random string to use as a filename, and pass the name of the file through stdout so that the next step in your pipeline knows where to find your file.

Source code from Rmd file within another Rmd

I'm attempting to make my code more modular: data loading and cleaning in one script, analysis in another, etc. If I were using R scripts, this would be a simple matter of calling source on data_setup.R inside analysis.R, but I'd like to document the decisions I'm making in an Rmarkdown document for both data setup and analysis. So I'm trying to write some sort of source_rmd function that will allow me to source the code from data_setup.Rmd into analysis.Rmd.
What I've tried so far:
The answer to How to source R Markdown file like `source('myfile.r')`? doesn't work if there are any repeated chunk names (a problem since the chunk named setup has special behavior in Rstudio's notebook handling). How to combine two RMarkdown (.Rmd) files into a single output? wants to combine entire documents, not just the code from one, and also requires unique chunk names. I've tried using knit_expand as recommended in Generate Dynamic R Markdown Blocks, but I have to name chunks with variables in double curly-braces, and I'd really like a way to make this easy for my colaborators to use as well. And using knit_child as recommended in How to nest knit calls to fix duplicate chunk label errors? still gives me duplicate label errors.
After some further searching, I've found a solution. There is a package option in knitr that can be set to change the behavior for handling duplicate chunks, appending a number after their label rather than failing with an error. See https://github.com/yihui/knitr/issues/957.
To set this option, use options(knitr.duplicate.label = 'allow').
For the sake of completeness, the full code for the function I've written is
source_rmd <- function(file, local = FALSE, ...){
options(knitr.duplicate.label = 'allow')
tempR <- tempfile(tmpdir = ".", fileext = ".R")
on.exit(unlink(tempR))
knitr::purl(file, output=tempR, quiet = TRUE)
envir <- globalenv()
source(tempR, local = envir, ...)
}

Knit function that returns dot (graphviz) code as string

Using pure dot inside a knitr chunk with engine = "dot" is straightforward, but you have to write the dot code yourself.
<<r dot-ex, engine = "dot", echo=FALSE>>=
digraph test123 {
A -> B
}
#
I want a function to do it for me.
dotFun <- function() {
dotCode <- 'digraph test123 {
A -> B
}'
return(dotCode)
}
and then call this inside a knit chunk similar to a function that returns LaTeX code and knit with result = 'as.is'
<<r dot-ex, engine = "dot">>=
cat(dotFun())
#
but this results in :Error in (knit_engines$get(options$engine))(options):
setting chunk option results = 'asis' yields the same error message.
Is there a way to do this?
It is not possible with the current version of knitr (v1.5), but will be possible in the next version (v1.6), which has not been released yet. If you use the development version on Github, you can actually assign the source code to a code chunk via the code option, e.g.
<<dot-ex, engine = "dot", code = dotFun()>>=
#
More on this in the news for v1.6.
Any particular reason this has to be evaluated within a knitr chunk with that engine? Why not do it directly from R with some system calls? You can write the command to a file, and use system to call dot on that file, and read the results back into R.
This is, in fact, what knitr does. You can probably pretty easily take the knitr dot engine and wrap it into a function of your own -- see
https://github.com/yihui/knitr/blob/master/R/engine.R#L144.

knitr_child throws error after upgrade to R 3.0

A script that has been running seamlessly for over a month stopped adding my child Latex code into my main document following an upgrade to R 3.0.1. The following snippet used to include the text from the compiled test.rnw file in my main document (so that it could be compiled as one document). Now it just includes the filenames of the compiled rnw files.
<<run-all, include=FALSE>>=
out = NULL
for (i in 1:10) {
out = c(out, knit_child('test.rnw', sprintf('test-template-%d.tex', i)))
}
#
\Sexpr{paste(out, collapse = '\n')}
When I try to run the knit_child command interactively, I get this error:
> knit_child('test.rnw', sprintf('test-template-%d.tex', i))
Error in setwd(opts_knit$get("output.dir")) : character argument expected
Running knit() alone will compile the Latex code, if I then run knin_child() there is not error but the "out" object just contains the filename of the child file instead of the contents.
Any ideas how to fix this?
You are not supposed to use knit_child() interactively. It was designed to be called inside knit().
As you have noted, knit_child() in the latest version of knitr returns the content of the child document if you do not provide the second argument. By explicitly providing the second argument sprintf('test-template-%d.tex', i), you mean "please write the output to this file and return the filename".
To fix the problem, you need to remove the second argument:
<<run-all, include=FALSE>>=
out = NULL
for (i in 1:10) {
out = c(out, knit_child('test.rnw'))
}
#
\Sexpr{paste(out, collapse = '\n')}

Resources