rmarkdown: recursive chunk evaluation? - r

I would like to have a string produced by an R chunk in an rmarkdown document to be re-evaluated - is that possible? For demonstration purposes consider the following document:
---
params:
B: 'test'
---
```{r simple, results='asis', echo=FALSE}
write(params[['B']], file = "")
```
```{r recursive-evaluation-questionmark, results='asis', echo=FALSE}
write(
"How to get \"params[['B']]\" evaluated here? This \"`r params[['B']]`\" is
expected to be \"test\" ...",
file = "")
```
Currently this produces the following when knitted:
test
How to get “params[[‘B’]]” evaluated here? This “r params[['B']]” is expected to be “test” …
but I want:
test
How to get “params[[‘B’]]” evaluated here? This “test” is expected to be “test” …

You can't get knitr to treat R code as text, but you can solve the problem using pure R code. I'd also recommend using cat() instead of write(). For your example,
write(
paste0("How to get \"params[['B']]\" evaluated here? This \"", params[['B']], "\" is
expected to be \"test\" ..."),
file = "")
or the simpler
cat("How to get \"params[['B']]\" evaluated here? This \"", params[['B']], "\" is
expected to be \"test\" ...", sep = "")
For more complicated macro-like substitutions, you may want to use the sub() or gsub() functions, e.g.
msg <- "How to get \"params[['B']]\" evaluated here? This \"%param%\" is
expected to be \"test\" ..."
cat(sub("%param%", params[['B']], msg))

Related

Is there a specific R function that gives the greatest control over the formatting of printed strings?

Consider the string "Hello \n World!". It appears that the relevant methods for formatting and printing it are print.default, cat, and if need be, format then print or cat. However, each of these seem to be able to do some things that the others cannot. Is there any one ultimate printing function that gives the maximum amount of control over the formatting/printing of my strings?
For example, I can't see anywhere in print, format, or print.default's documentations that would make them respect my \n and put a line break in "Hello \n World!", as cat does, but I also can't see anyway to make cat keep the quotation marks in "Hello \n World!", as print("Hello \n World!", quote=FALSE) would.
If we need the quotes as well, wrap it with dQuote within cat
cat( dQuote("Hello \n World!", FALSE))
"Hello
World!"
According to ?cat
Character strings are output ‘as is’ (unlike print.default which escapes non-printable characters and backslash — use encodeString if you want to output encoded strings using cat). Other types of R object should be converted (e.g., by as.character or format) before being passed to cat. That includes factors, which are output as integer vectors.
Or we can use message. The advantage is that it can be used as well with RMD files where those messages will be printed on the console rather than on the document
message('"Hello \n World!"')
#"Hello
# World!"
i.e. as a trial, create a simple RMD file
---
output:
html_document:
df_print: paged
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, cache = TRUE)
library(ggplot2)
```
```{r trial 1, echo = FALSE, message = FALSE}
message("data(mtcars) is created with message")
print("data(mtcars) is created with print")
glue::glue("data(mtcars is created with glue")
```
-output
NOTE: We already specified message = FALSE. So, it would not show up in the document, whereas for debugging, it still prints on the Rmarkdown console output
...
data(mtcars) is created with message
output file: test1.knit.md
...
Just modify the cat
foo = function(...) {
s = paste0("\"", ..., "\"")
cat(s)
}
foo("Hello \n World!")
#"Hello
# World!"
This is possible using glue from Tidyverse. glue will respect the \n. And you can print the double quotes by wrapping the string in a single quotes ' or escaping them with \".
library(glue)
# wrap in single quote
glue('"Hello \n World!"')
# escape the double quotes
glue("\"Hello \n World!\"")

How to insert R text variables into Latex expressions keeping normal formatting

I want to create Section headers with a for loop and have managed to do this however the text is stuck in math mode formatting. I've tried using Latex syntax to undo this however it did not work as expected:
Code:
---
title: "Troubleshooting"
author: "Me"
output: pdf_document
header-includes:
- \usepackage{amsmath}
---
```{r}
words <- c("plz_work","better_yet","what_is_this")
```
```{r, results='asis', echo=FALSE}
for (i in 1:3) {
cat('\\subsection{$',words[i],'$}')
}
```
Output:
If I use the latex notation to convert it to text formatting nothing changes:
```{r, results='asis', echo=FALSE}
for (i in 1:3) {
cat('\\subsection{$\text{',words[i],'}$}')
}
```
If I escape the "$\text{..}$" again i.e. "$\\text{..}$" then I get an error:
! Missing $ inserted.
<inserted text>
$
l.283 \subsection{$\text{ plz_work }$}
Error: Failed to compile test_delete_is_ok.tex. See test_delete_is_ok.log for more info.
Execution halted
Adding more escapes doesnt help. If I try to insert this variable without math mode I also get the same error that I just listed:
```{r, results='asis', echo=FALSE}
for (i in 1:3) {
cat('\\subsection{',words[i],'}')
}
```
! Missing $ inserted.
<inserted text>
$
l.283 \subsection{ plz_work }
Error: Failed to compile test_delete_is_ok.tex. See test_delete_is_ok.log for more info.
Execution halted
As mentioned by Samcarter, when using tex you need to also be aware of any tex-related special meanings.
In this instance - the "_" symbol is used in tex math mode for signalling a subscript is about to follow. When passing my R strings that contained "_" into the tex expression it would throw an error as the "_" is reserved for math mode (unless escaped) - this is why I got no error when I enclosed it with $...$
The solution for me was to add in escapes before every tex special character:
#original
words <- c("plz_work","better_yet","what_is_this")
#modified version. Replace collapse with other tex functions if needed
words2 <- lapply(strsplit(words,"_"), paste, collapse = '\\_')
for (i in 1:3) {
cat('\\subsection{',words2[[i]],'}')
}
The underscores here are a little long for my liking but you can easily replace the 'collapse' argument to something else which you prefer eg. ... collapse ='\\textunderscore ') ... also works

Having multiple pander()s in a function

How do I create multiple outputs via pander() in a knitted document "asis" ?
When I have multiple calls of pander in a function, only the most recent one is shown in the HTML output. Here's an example:
tmp = function() {
pander('A')
pander('B')
pander('C')
}
tmp()
In the knitted document this gives: C
I could set panderOptions('knitr.auto.asis', FALSE) or I could use cat() so that the pander() output is written to the standard output. But then it's formatted as code, not as part of the document. As I need pander() to format a few tables for me, this does not help.
The tmp function will return only the last object -- that's why only C is printed. If you want to write each object to the stdout right away without the auto-asis convenience option, then you have to both disable the option like you did and use the relate knitr chunk option, eg:
```{r results='asis'}
library(pander)
panderOptions('knitr.auto.asis', FALSE)
tmp = function() {
pander('A')
pander('B')
pander('C')
}
tmp()
```
See more examples in the related "Using pander with knitr" vignette.

How to syntax highlight inline R code in R Markdown?

This question is similar to consistent code html inline and in chunks with knitr. Instead of .Rhtml documents, I want to highlight inline R code in R Markdown documents, e.g., after `r "plot(cars, main = 'A scatterplot.')"` is compiled through rmarkdown, the tokens like plot should be highlighted. By default, R code chunks are syntax highlighted, but there is no way to highlight inline R code.
Here is one solution using the development version of the highr package (devtools::install_github('yihui/highr')). Basically you just define your custom LaTeX commands to highlight the tokens. highr:::cmd_pandoc_latex is a data frame of LaTeX commands that Pandoc uses to do syntax highlighting.
head(highr:::cmd_pandoc_latex)
## cmd1 cmd2
## COMMENT \\CommentTok{ }
## FUNCTION \\NormalTok{ }
## IF \\NormalTok{ }
## ELSE \\NormalTok{ }
## WHILE \\NormalTok{ }
## FOR \\NormalTok{ }
Then you can redefine the inline hook of knitr:
---
output:
pdf_document:
keep_tex: yes
---
```{r include=FALSE}
local({
hi_pandoc = function(code) {
if (knitr:::pandoc_to() != 'latex') return(code)
if (packageVersion('highr') < '0.6.1') stop('highr >= 0.6.1 is required')
res = highr::hi_latex(code, markup = highr:::cmd_pandoc_latex)
sprintf('\\texttt{%s}', res)
}
hook_inline = knitr::knit_hooks$get('inline')
knitr::knit_hooks$set(inline = function(x) {
if (is.character(x) && inherits(x, 'AsIs')) hi_pandoc(x) else hook_inline(x)
})
})
```
Test inline R code: `r I("plot(cars, main = 'A scatterplot.')")`.
Normal inline code `r pi`.
A code block:
```r
plot(cars, main = 'A scatterplot.')
1 + 2 # a comment
```
I used I() as a convenient marker to tell the character strings to be syntax highlighted from normal character strings. It is just an arbitrary choice. PDF output:
This is not a perfect solution, though. You will need to tweak it in some cases. For example, most special LaTeX characters are not escaped, such as ~. You may need to process the LaTeX code returned by hi_pandoc() by gsub().
Personally I find multiple colors in inline output distracting, so I would not syntax highlighting it, but this is entirely personal taste.
Now-a-days:
Here is some `plot(cars, main = 'A scatterplot.')`{.R} inline R code
Well, I don't know specifically about R and the way you're using it, but for most languages (pandoc uses the skylighting pkg to do this), you can do inline code blocks with the above syntax.

Sweave can't see a vector if run from a function?

I have a function that sets a vector to a string, copies a Sweave document with a new name and then runs that Sweave. Inside the Sweave document I want to use the vector I set in the function, but it doesn't seem to see it.
(Edit: I changed this function to use tempdir(() as suggested by Dirk)
I created a sweave file test_sweave.rnw;
%
\documentclass[a4paper]{article}
\usepackage[OT1]{fontenc}
\usepackage{Sweave}
\begin{document}
\title{Test Sweave Document}
\author{gb02413}
\maketitle
<<>>=
ls()
Sys.time()
print(paste("The chosen study was ",chstud,sep=""))
#
\end{document}
and I have this function;
onOK <- function(){
chstud<-"test"
message(paste("Chosen Study is ",chstud,sep=""))
newfile<-paste(chstud,"_report",sep="")
mypath<-paste(tempdir(),"\\",sep="")
setwd(mypath)
message(paste("Copying test_sweave.Rnw to ",paste(mypath,newfile,".Rnw",sep=""),sep=""))
file.copy("c:\\local\\test_sweave.Rnw",
paste(mypath,newfile,".Rnw",sep=""), overwrite=TRUE)
Sweave(paste(mypath,newfile,".Rnw",sep=""))
require(tools)
texi2dvi(file = paste(mypath,newfile,".tex",sep=""), pdf = TRUE)
}
If I run the code from the function directly, the resulting file has this output for ls();
> ls()
[1] "chstud" "mypath" "newfile" "onOK"
However If I call onOK() I get this output;
> ls()
[1] "onOK"
and the print(...chstud...)) function generates an error.
I suspect this is an environment problem, but I assumed because the call to Sweave occurs within the onOK function, it would be in the same enviroment, and would see all the objects created within the function. How can I get the Sweave process to see the chstud vector ?
Thanks
Paul.
I have similar problem. Eventually I found a work-around that "works for me", although it may not be the most elegant way to solve this issue.
Within my function, before 'Sweave' is executed, I put a statement to globally store the local environment:
temp <<- environment()
Using your code example it would look something like:
testFoo<-function(){
foo<-"My Test String"
temp <<- environment()
Sweave("test_sweave.Rnw")
require(tools)
texi2dvi(file = "test_sweave.tex", pdf = TRUE)
}
rm(foo) testFoo()
Then in the LaTeX file to be 'Sweaved' in the beginning of the first chunk I restore the necessary variables, but you can also use 'temp$foo' to access the 'foo' variable that was created within the function. This way I avoid globally storing multiple variables.
%
\documentclass[a4paper]{article}
\usepackage[OT1]{fontenc}
\usepackage{Sweave}
\begin{document}
\title{Test Sweave Document}
\author{Paul Hurley}
\maketitle
<<>>=
if(exists("foo", envir=temp)) { print(temp$foo) }
ls()
Sys.time()
#
\end{document}
Like I wrote above - this works, but is not very elegant.
OK, I realise that my initial ideas of a 'simple, self contained example' wasn't particularly simple or usefull. So I redid my example and got a sort of answer for myself, although I'd love someone to exaplain why and even suggest a better solution,
Here's my example test_sweave.Rnw file
%
\documentclass[a4paper]{article}
\usepackage[OT1]{fontenc}
\usepackage{Sweave}
\begin{document}
\title{Test Sweave Document}
\author{Paul Hurley}
\maketitle
<<>>=
if(exists("foo")){print(foo)}
ls()
Sys.time()
#
\end{document}
If I run this code;
testFoo<-function(){
foo<-"My Test String"
Sweave("test_sweave.Rnw")
require(tools)
texi2dvi(file = "test_sweave.tex", pdf = TRUE)
}
rm(foo) testFoo()
my resulting file does NOT contain the contents of the string foo.
> if (exists("foo")) {
+ print(foo)
+ }
> ls()
[1] "testFoo"
If I run this code (i.e, the same thing, just run directly)
rm(foo)
foo<-"My Test String"
Sweave("test_sweave.Rnw")
require(tools)
texi2dvi(file = "test_sweave.tex", pdf = TRUE)
my resulting file does contain the foo string
> if (exists("foo")) {
+ print(foo)
+ }
[1] "My Test String"
> ls()
[1] "foo" "testFoo"
and if I run this code
testBar<-function(){
foo<<-"My Test String"
Sweave("test_sweave.Rnw")
require(tools)
texi2dvi(file = "test_sweave.tex", pdf = TRUE)
}
rm(foo)
testBar()
My resulting file also contains the foo string
> if (exists("foo")) {
+ print(foo)
+ }
[1] "My Test String"
> ls()
[1] "foo" "testBar" "testFoo"
So, it seems that sweave runs in the global environment, not in the environment it was called from. This means the only way to pass variables to sweave when sweave is run from a function is to use the <<- operator to put the variable in the global environment. (I think).
Anyone else want to comment who knows more about environments ?

Resources