How to reproducibly include R code in Word documents with Rmarkdown? - r

I have some R code in a package. I don't want to copy that code, but I want to display it in a pretty way in Word with syntax highlighting without any manual steps.
I looked at styler::style_text in combination of capture.output and that looks nice in the browser, but all the formatting is lost when knitting to Word. Is there some way to preserve it? I'm thinking the best thing would be to have Word native styling but the next best (acceptable) thing would be to somehow render the output to an image and include that. Has anyone done these things to document their code in a report?
show_code = function (fun) {
stopifnot(is.function(fun))
out = capture.output(fun)
n = length(out)
without_bytecode_and_env_lines = -1*c(n-1, n)
code = paste(out[without_bytecode_and_env_lines], collapse = "\n")
styler::style_text(code)
}

I believe you are trying to use syntax highlighting on the output of show_code and to do that, you simply need to use the options comment="" and class.output="r" and syntax highlighting will apply to the output.
---
title: "Source Code highlighting"
output:
word_document:
highlight: kate
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
## R Markdown
```{r echo=FALSE}
show_code = function (fun) {
stopifnot(is.function(fun))
out = capture.output(fun)
n = length(out)
without_bytecode_and_env_lines = c(n-1, n)
code = paste0(out[-without_bytecode_and_env_lines], collapse = "\n")
styler::style_text(code)
}
```
### The source code for `lm`
```{r comment='', echo=FALSE, class.output = "r"}
show_code(lm)
```

Related

Pagedown package error in producing the output

I have recently decided to use pagedown package in producing pdf and html outputs and therefore installed the library. I am trying to run the very simple Rmd file that comes as default when I choose to use pagedown file as my new file in the RStudio.
Here is the Rmd file content if you would like to see;
---
title: "A Multi-page HTML Document"
author: "Yihui Xie and Romain Lesur"
date: "`r Sys.Date()`"
output:
pagedown::html_paged:
number_sections: FALSE
# uncomment this line to produce HTML and PDF in RStudio:
knit: pagedown::chrome_print
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
# Introduction
This is an example of a multi-page HTML document with some options shown in YAML header. See https://pagedown.rbind.io for the full documentation. The rest of this document is random text.
# Random text
```{r, results='asis', echo = FALSE}
random_markdown = function(len = 100) {
uri = knitr::image_uri(file.path(R.home('doc'), 'html', 'logo.jpg'))
text = function(len) {
trimws(paste(sample(c(letters, rep(' ', 10)), len, TRUE), collapse = ''))
}
id = function() paste(sample(letters, 8, TRUE), collapse = '')
figure = function(i = id()) {
sprintf('![(#fig:%s)The R logo.](%s){width=%d%%}', i, uri, sample(20:60, 1))
}
tab = paste(knitr::kable(head(mtcars[, 1:5])), collapse = '\n')
table = function(i = id()) {
c(sprintf('Table: (#tab:%s)A table example.', i), tab)
}
unlist(lapply(seq_len(len), function(i) {
if (i %% 20 == 0) return(paste('#', text(sample(10:30, 1))))
if (i %% 10 == 0) return(paste('##', text(sample(10:30, 1))))
# insure some elements
if (i == 3) return(text(50))
if (i == 4) return(figure("md-fig"))
if (i == 5) return(text(50))
if (i == 6) return(table("md-tab"))
# then random
type = sample(1:3, 1, prob = c(.9, .03, .07))
switch(type, text(sample(50:300, 1)), figure(), table())
}))
}
cat(random_markdown(), sep = '\n\n')
# Knitr inserted Figures and tables
## Simple graphics
```
Until here, R markdown can run the document well. However, when I tried to add the following two code chunks, the document fails to run.
``` {r simple-graphic, fig.cap = 'A very simple plot'}
plot(1)
```
## Simple tables
```{r simple-table}
knitr::kable(head(mtcars, 3), caption = "A Simple table")
```
And here is the error I get;
Error in force(expr) : Failed to generate output in 30 seconds (timeout).
Calls: <Anonymous> -> with_temp_loop_maybe -> with_loop -> force
Closing websocket connection
Closing browser
Cleaning browser working directory
Closing local webserver
I do not understand why the document does not fail to run in the first phase, while it fails to run in the second step. I tried to find a solution through web. I hope I am clear regarding my problem and I look forward to your reply. Thank you for your understanding beforehand.
If you just remove knit: pagedown::chrome_print the document will render to HTML just fine, Then from here, you can "print to PDF" from the browser to get both an HTML and a PDF doc of the output
---
title: "A Multi-page HTML Document"
author: "Yihui Xie and Romain Lesur"
date: "`r Sys.Date()`"
output:
pagedown::html_paged:
number_sections: FALSE
---
I think the issue might be your are specifying 2 different output formats, and Rmarkdown is taking a long time to render these
There is a similar issue discussed here the advice was to increase the timeout value, as it's default is 30 seconds until the process is fails out. and another similar issue here... but to be honest, I was not able to correct apply the timeout argument in chrome_print

How to let bookdown render LaTeX in kableExtra table headers properly?

I use kableExtra package in rmarkdown (bookdown) to generate nice looking tables in pdf outputs. All works well except for the rendering of LaTeX code in headers. A header named like $\\alpha$ isn't rendered as the Greek alpha. The result is just a $\alpha$ shown in the pdf document.
Additional information: I use format = "latex" and escape = TRUE. If I use escape = FALSE, I get an error when rendering the document:
I was unable to find any missing LaTeX packages from the error log _main.log.
! Misplaced \noalign.
\cmidrule ->\noalign
{\ifnum 0=`}\fi \#ifnextchar [{\#cmidrule }{\#cmidrule ...
l.1293 \cmidrule
{3-7}
I am sorry for not giving a reproducible example. I somehow hope it is a setting I missed somewhere in the kableExtra. If it is needed I will make an example though.
Many thanks in advance!
You could try this:
---
title: "Use slashes to escape"
author: "bttomio"
date: "3/24/2021"
output: pdf_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
```{r echo=F}
library(kableExtra)
x <- matrix(1:4, ncol=2)
kbl(x, col.names=c('$\\alpha$', 'B'), align = 'c', 'latex', booktabs = T, escape = F) %>%
add_header_above(c("$\\\\alpha$" = 2), escape = F)
```
-output

Set number of decimal places to show in output

I am wanting to get more into using R markdown to perform analyses and generate output. Maybe I'm missing something simple, but I just want to be able to set the number of decimal places to show either 2 or 3 digits, depending on the output (e.g. t-statistic vs p-value).
I have previously used r options(digits=2), which works until the last digit you want to include is 0. I have gotten around this with the sprintf function, but having to specify for each number.
Is there a way to set a 'global' sprintf option so that for all numbers following, the same number of decimal places are shown?
Thank you,
Paul
Defining a format for inline code output is feasible with a knitr inline hook (hooks are the hidden gems of knitr).
Example #1
With this Rmd file, the number of decimals is controlled without using sprintf() in all inline codes:
---
title: "Use an inline hook"
---
```{r setup, include=FALSE}
# Register an inline hook:
knitr::knit_hooks$set(inline = function(x) {
x <- sprintf("%1.2f", x)
paste(x, collapse = ", ")
})
```
Now, get 3.14 with just writing `r pi`.
Example #2
Want to change the inline output format in some part of the report?
This Rmd file does the job:
---
title: "Use a closure and an inline hook"
---
```{r setup, include=FALSE}
# Register an inline hook
knitr::knit_hooks$set(inline = function(x) {
paste(custom_print(x), collapse = ", ")
})
# Define a function factory (from #eipi10 answer)
op <- function(d = 2) {
function(x) sprintf(paste0("%1.", d, "f"), x)
}
# Use a closure
custom_print <- op()
```
Now, get 3.14 with `r pi`...
```{r three-decimals, include=FALSE}
custom_print <- op(d = 3)
```
...and now 3.142 with `r pi`.
```{r more-decimals, include=FALSE}
custom_print <- op(d = 10)
```
Finally, get 3.1415926536 with `r pi`.
Example #3
Want to display different formats for t-statistic and p-value?
One can use S3 objects and an inline hook as in this Rmd file:
---
title: "Use S3 methods and an inline hook"
---
```{r setup, include=FALSE}
# Register an inline hook
knitr::knit_hooks$set(inline = function(x) {
paste(custom_print(x), collapse = ", ")
})
# Define a generic
custom_print <- function(x, ...) {
UseMethod("custom_print", x)
}
# Define a method for p-values
custom_print.p.value <- function(x, ...) paste(sprintf("%1.2f", x), collapse = ", ")
# Define a method for t-statistics
custom_print.t.stat <- function(x, ...) paste(sprintf("%1.1f", x), collapse = ", ")
```
Estimate models...
```{r fake-results, include=FALSE}
t <- c(2.581, -1.897)
class(t) <- "t.stat"
p <- c(0.025, 0.745)
class(p) <- "p.value"
```
Want to show T-stats: `r t` (get 2.6, -1.9).
And p-values: `r p` (get 0.03, 0.74).
Who said knitr is a wonderful package?
I don't know of a way to set a global option (though there may be one). But you can write a convenience output function to reduce the amount of typing. For example, put this function at the beginning of your document:
op = function(x, d=2) sprintf(paste0("%1.",d,"f"), x)
Then, later in your document, when you want to output numbers, you can, for example, do:
op(mtcars$mpg)
Or if you want 3 digits instead of the default 2, you can do:
op(mtcars$mpg, 3)
As found in the tutorial here by Yihui, this is how I've successfully implemented it in my Rmd file.
{r setup, include=FALSE, cache=FALSE}
options(scipen = 1, digits = 2) #set to two decimal

Chopped up output using message() in an R Markdown / knitr report

I recently submitted a package to rOpenSci, and they prefer the use of message() rather than cat() for user-side console output. When I made the switch for my package, I noticed a disconcerting change in the formatting of the rendered vignettes. I have reproduced the problem in the following R Markdown report.
---
title: "MWE"
author: "Will Landau"
date: "11/20/2017"
output: html_document
---
```{r testcat}
for(x in LETTERS[1:3]){
cat(x, "\n")
}
```
```{r testmessage}
for(x in LETTERS[1:3]){
message(x)
}
```
```{r testmessage2}
for(x in LETTERS[1:3]){
message(x, "\n", appendLF = FALSE)
}
```
For the first code chunk, I get the desired output: all three lines string together in a single gray box.
## A
## B
## C
But for the second and third chunks, each line is given its own separate gray box.
## A
.
## B
.
## C
How do I keep using message() without chopping up the knitr output like this?
I think I solved it: knitr has a collapse chunk option. All I needed was to put this chunk before any of the other chunks.
```{r setup}
knitr::opts_chunk$set(collapse = TRUE)
```
The output is more condensed than I expected, but after some touching up, the formatting actually looks much better now.
You can try and build the string and put the message function outside the loop, like this:
```{r testmessage}
single_message <- c()
for(x in LETTERS[1:3]){
single_message <- paste(single_message , x, sep = "\n")
}
message(single_message )
```
Note that this example does add a newline at the start, you can prevent this with an extra if, or use the first element outside the loop to initialize single_message.

Rmarkdown Chunk Name from Variable

How can I use a variable as the chunk name? I have a child document which gets called a number of times, and I need to advance the chunk labels in such a manner than I can also cross reference them.
Something like this:
child.Rmd
```{r }
if(!exists('existing')) existing <- 0
existing = existing + 1
myChunk <- sprintf("myChunk-%s",existing)
```
## Analysis Routine `r existing`
```{r myChunk,echo = FALSE}
#DO SOMETHING, LIKE PLOT
```
master.Rmd
# Analysis Routines
Analysis for this can be seen in figures \ref{myChunk-1}, \ref{myChunk-2} and \ref{myChunk-3}
```{r child = 'child.Rmd'}
```
```{r child = 'child.Rmd'}
```
```{r child = 'child.Rmd'}
```
EDIT POTENTIAL SOLUTION
Here is one potential workaround, inspired by SQL injection of all things...
child.Rmd
```{r }
if(!exists('existing')) existing <- 0
existing = existing + 1
myChunk <- sprintf("myChunk-%s",existing)
```
## Analysis Routine `r existing`
```{r myChunk,echo = FALSE,fig.cap=sprintf("The Caption}\\label{%s",myChunk)}
#DO SOMETHING, LIKE PLOT
```
A suggestion to preknit the Rmd file into another Rmd file before knitting&rendering as follows
master.Rmd:
# Analysis Routines
Analysis for this can be seen in figures `r paste(paste0("\\ref{", CHUNK_NAME, 1:NUM_CHUNKS, "}"), collapse=", ")`
###
rmdTxt <- unlist(lapply(1:NUM_CHUNKS, function(n) {
c(paste0("## Analysis Routine ", n),
paste0("```{r ",CHUNK_NAME, n, ", child = 'child.Rmd'}"),
"```")
}))
writeLines(rmdTxt)
###
child.Rmd:
```{r,echo = FALSE}
plot(rnorm(100))
```
To knit & render the Rmd:
devtools::install_github("chinsoon12/PreKnitPostHTMLRender")
library(PreKnitPostHTMLRender) #requires version >= 0.1.1
NUM_CHUNKS <- 5
CHUNK_NAME <- "myChunk-"
preknit_knit_render_postrender("master.Rmd", "test__test.html")
Hope it helps. Cheers!
If you're getting to this level of complexity, I suggest you look at the brew package.
That provides a templating engine where you can dynamically create the Rmd for knitting.
You get to reference R variables in the outer brew environment, and build you dynamic Rmd from there.
Dynamic chunk names are possible with knitr::knit_expand(). Arguments are referenced in the child document, including in the chunk headers, using {{arg_name}}.
So my parent doc contains:
```{r child_include, results = "asis"}
###
# Generate a section for each dataset
###
species <- c("a", "b")
out <- lapply(species, function(sp) knitr::knit_expand("child.Rmd"))
res = knitr::knit_child(text = unlist(out), quiet = TRUE)
cat(res, sep = "\n")
```
And my child doc, which has no YAML header, contains:
# EDA for species {{sp}}
```{r getname-{{sp}}}
paste("The species is", "{{sp}}")
```
See here in the RMarkdown cookbook.

Resources