Source code from Rmd file within another Rmd - r

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, ...)
}

Related

Using R/Markdown fails inside learnr question

Motivation: I want to write an interface that uses questions from the R package exams in learnr questions/quizzes. In R/exams each question is either an R/Markdown (Rmd) or R/LaTeX (Rnw) file with a certain structure specifying question, solution, and further meta-information. The questions can contain R code to make them dynamic, e.g., sampling numbers or certain text building blocks etc. Hence, the workflow is that first the questions are run through knitr::knit or utils::Sweave and then embedded in a suitable output format.
Problem: When I rmarkdown::run("learnr+rexams.Rmd") a learnr tutorial that dynamically produces a question or quiz from an Rmd exercise I get the error:
Error in if (grepl(not_valid_char_regex, label)) { :
argument is of length zero
The code for a simple reproducible example learnr+rexams.Rmd is included below.
The reason for the error appears to be that learnr runs a function verify_tutorial_chunk_label() that tries to assure the the learnr R chunk labels are well formatted. However, confusion is caused by the chunks that are run by the R/exams package, unnecessarily leading to the error above.
Workarounds: I can disable the verify_tutorial_chunk_label() in the learnr namespace and then everything works well. Or I can use Rnw instead of Rmd exercises and then learnr does not conflict with Sweave(). Also, when I run my code outside of a learnr tutorial it works fine.
Question: Can I do anything less invasive to make exams cooperate with learnr? For example, setting some appropriate knitr options or something like that?
Example: This is the source for the minimal learnr tutorial learnr+rexams.Rmd that replicates the problem. Note that everything is very much simplified and only works for certain R/exams exercises, here using the function exercise template that ships with R/exams.
---
title: "learnr & R/exams"
output: learnr::tutorial
runtime: shiny_prerendered
---
```{r exams2learnr, include = FALSE}
exams2learnr <- function(file) {
x <- exams::xexams(file)[[1]][[1]]
x <- list(text = x$question, type = "learnr_text",
learnr::answer(x$metainfo$solution, correct = TRUE))
do.call(learnr::question, x)
}
## assignInNamespace("verify_tutorial_chunk_label", function() return(), ns = "learnr")
```
```{r rfunctions, echo = FALSE, message = FALSE}
exams2learnr("function.Rmd")
```
Running this tutorial (as noted above) replicates the error. To avoid it I can either uncomment the assignInNamespace() call or alternatively replace "function.Rmd" by "function.Rnw".
The problem is that by the time learnr::question() is called, knitr is no longer able to find the chunk label for the chunk where exams2learnr() was called. You can get around this by setting the current chunk label before calling do.call(learnr_question, x):
exams2learnr <- function(file, label = knitr::opts_current$get("label")) {
force(label)
x <- exams::xexams(file)[[1]][[1]]
x <- list(
text = x$question,
type = "learnr_text",
learnr::answer(x$metainfo$solution, correct = TRUE)
)
knitr::opts_current$set(label = label)
do.call(learnr::question, x)
}
This also lets you set the label dynamically if you want, which becomes the ID of the question in learnr.

kable produces malformed reference links within lapply function in blogdown

I am using blogdown to to create a blogpost that has a series of tables. Creating a single table using the kable function works fine. If you do
blogdown::new_site()
blogdown::new_post("test", ext = ".rmd")
A new rmd file will be created within the content/post directory of the project. If you open that file and create a single table by doing
```{r test1}
library(knitr)
library(magrittr)
library(shiny)
data.frame(a= c(1,2,3)) %>% kable(caption = 'test',format = 'html')
```
A correctly formatted table will be generated. The caption will read "
Table 1: test" If you look at the code of the generated site, the caption will look like this.
<caption>
<span id="tab:test1">Table 1: </span>test
</caption>
Ideally I don't have any desire to label the table as Table 1 in the first place but that is another question. If formatting of captions by kable can be disabled entirely, I'd also be happy.
However if I use lapply to generate 2 tables instead
```{r test2}
lapply(1:2,function(x){
data.frame(a= c(1,2,3)) %>% kable(caption = 'test2',format = 'html') %>% HTML()
}) -> tables
tables[[1]]
tables[[2]]
```
The captions will have the prefix \#tab:test2. If you look at the caption of these tables, you'll see
<caption>(\#tab:test2)test2</caption>
The question is, why kable behaves differently when its called from a lapply compared to its behaviour outside? Note that both of these behaviours are different that its behaviour when simply knitting the file as an html_document.
I did some digging into the kable's code and found that the caption link is created by the knitr:::create_label function. Looking into this function, I saw the part that is responsible for the wrong behaviour seen with the multiple tables.
if (isTRUE(opts_knit$get("bookdown.internal.label"))) {
lab1 = "(\\#"
lab2 = ")"
}
I could not find the code, responsible for the "correct" behaviour with the single table but it seems like knitr internal options are responsible.
Ultimately the behaviour that I want is simply
<caption>test</caption>
which is the behaviour when simply knitting an html document. But I am yet to find a way to set the relevant knitr options and why are they different within the same document.
Edit: Further examination suggests that the issue isn't lapply specific. It can be replicated using a for loop or even { by itself. A complete post with all the problematic examples can be acquired from this issue on knitr's github page. This github repo includes the basic blogdown site that replicates the issue
Turns out the responsible party is not the lapply call but the HTML call at the end. It seems like the regular process by knitr in blogdown and bookdown is to have a temporary marker for the table references in the form of (\#tab:label) and replace it with the appropriate syntax later in the processing.
I was using the HTML call to be able to use the tags object in shiny/htmltools to bind the tables together. This approach seems to make the process of replacing the temporary marker impossible for reasons outside my understanding. For my own purposes I was able to remove the temporary marker all together to get rid of both malformed captions and the working-as-intended table numbers by doing
remove_table_numbers = function(table){
old_attributes = attributes(table)
table %<>% as.character() %>% gsub("\\(\\\\#tab:.*?\\)","",.)
attributes(table) = old_attributes
return(table)
}
data.frame(a= c(1,2,3)) %>% kable(caption = 'test',format = 'html') %>% remove_table_numbers
This question still would benefit from a proper explanation of the reference link placement process and if its possible to apply it to tables in HTML calls as well. But for know this solves my issue. I'll gladly switch the accepted answer if a more complete explanation appears

knit2pdf(): knit and latex passes

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"))
}
}

R - Handling cut intervals of form [x,y] in tables when converting to LaTeX

I'm working on a document in R, with knitr to pdflatex and am using the extended version of toLatex from memisc.
When I'm producing a table with cut intervals however, the square brackets are not sanitised and the pdflatex job errors because of the existence of [.
I tried putting sanitize=TRUE in the knitr chunk code, but this only works for tikz.
Previously, I have used gsub and replaced the string in the R object itself which is rather inelegant. I'm hoping someone could point me in the direction of a nuance of memisc or knitr that I'm missing or another function/method that would easily handle latex special characters.
Example
library("memisc")
library("Hmisc")
example<-data.frame(cbind(x=1:100,y=1:100))
example$x<-cut2(example$x,m=20)
toLatex(example)
UPDATE
Searching SO I found a post about applying latexTranslate with apply function, but this requires characters so I would have to unclass from factor to character.
I found another SO post that identifies the knitr:::escape_latex function however, the chunk then outputs the stuff as markup instead of translating it (using results='asis') or produces an R style table inside a code block (using results='markup'). I tried configuring it as a hook function in my parent document and it had the effect of outputting all the document contents as markup. This is a brand new area for me so I probably implemented it incorrectly.
<<setup,include=FALSE>>=
hook_inline = knit_hooks$get('inline')
knit_hooks$set(inline = function(x) {
if (is.character(x)) x = knitr:::escape_latex(x)
hook_inline(x)
})
#
...
<<tab-example,echo=FALSE,cache=TRUE,results='asis',sanitize=TRUE,inline=TRUE>>=
library("Hmisc")
library("memisc")
example<-data.frame(cbind(x=1:100,y=1:100))
example$x<-cut2(example$x,m=20)
toLatex(example)
#
According to #yihui this is the wrong way to go
UPDATE 2
I have created a gsub wrapper which will escape percentages etc, however the [ symbol still pushes latex into maths mode and errors.
Courtesy of folks on the tex SE, a [ directly after a line break(\\) is considered an entry into math-mode. It is very simple to prevent this behaviour by adding {} into the output just before a [. My function looks like:
escapedLatex<-function (df = NULL)
{
require("memisc")
gsub(gsub(x = toLatex(df, show.xvar = TRUE), pattern = "%",
replacement = "\\%", fixed = TRUE), pattern = "[", replacement = "{}[",
fixed = TRUE)
}
I'd be very happy to see any alternative, more elegant solutions around and will leave it open for a few days.

Is it possible to to export from reporttools?

I am using tableNominal{reporttools} to produce frequency tables. The way I understand it, tableNominal() produces latex code which has to be copied and pasted onto a text file and then saved as .tex. But is it possible to simple export the table produced as can be done in print(xtable(table), file="path/outfile.tex"))?
You may be able to use either latex or latexTranslate from the "Hmisc" package for this purpose. If you have the necessary program infrastructure the output gets sent to your TeX engine. (You may be able to improve the level of our answers by adding specific examples.)
Looks like that function does not return a character vector, so you need to use a strategy to capture the output from cat(). Using the example in the help page:
capture.output( TN <- tableNominal(vars = vars, weights = weights, group = group,
cap = "Table of nominal variables.", lab = "tab: nominal") ,
file="outfile.tex")

Resources