how to store embedded file (includes) in custom Rmarkdown - r

I created my own format in Rmarkdown based on this blogpost. I implemented it in my personal package and it works great. I also added custom files in includes argument of html_document.
My question is whether it's possible to store my custom files (included in includes argument) after I click Knit button. Similarly to self_contained = F option which allows storing all Rmarkdown dependencies.
Update
I should give you some context first. Let’s say I used my html format to create a report two months ago. Two weeks later I decided to implement major changes in my html format and updated my package.
After next two weeks, my boss came to me asking for adding minor changes in old report. Then, by clicking Knit button, the report was not able to create, because there was a new version of my html format, which was significantly different.
I see three possibilities how to deal with this request. Either I can install old version of my package (suboptimal), create a new html format every time I implement major changes or I can store my dependencies (header, footer, css files) in a separate subdirectory (like a packrat). Then each report would be independent and immune to changes in my custom format.
Let me know if there is any better solution.

Basic example
Assume that you have a myreport.Rmd file with the following header:
---
title: "Untitled"
author: "Romain Lesur"
date: "27 janvier 2018"
output:
html_document:
includes:
in_header: inheader.html
---
Using a hacky rmarkdown preprocessor, you can copy inheader.html file.
The following code is intended to be run in R console:
pre_processor <- function(metadata,
input_file,
runtime,
knit_meta,
files_dir,
output_dir) {
in_header <- metadata$output$html_document$includes$in_header
if (!is.null(in_header)) file.copy(in_header, output_dir)
invisible(NULL)
}
custom_output_format <- function() {
rmarkdown::output_format(
knitr = NULL,
pandoc = NULL,
pre_processor = pre_processor,
base_format = rmarkdown::html_document()
)
}
rmarkdown::render('myreport.Rmd',
output_format = custom_output_format(),
output_dir = 'output')
You get a output directory with the rendered report and inheader.html file inside.
In order to run a similar preprocessor on clicking Knit button, you have to include it in your personal package's output_format (see below).
Turning in a package
As the question mentions this blogpost, here is an adaptation of the quarterly_report function:
quarterly_report <- function(toc = TRUE) {
# get the locations of resource files located within the package
css <- system.file("reports/styles.css", package = "mypackage")
header <- system.file("reports/quarterly/header.html", package = "mypackage")
# call the base html_document function
base_format <-
rmarkdown::html_document(toc = toc,
fig_width = 6.5,
fig_height = 4,
theme = NULL,
css = css,
includes = includes(before_body = header))
pre_processor <- function(metadata,
input_file,
runtime,
knit_meta,
files_dir,
output_dir) {
purrr::walk(c(css, header), file.copy, output_dir)
invisible(NULL)
}
rmarkdown::output_format(
knitr = NULL,
pandoc = NULL,
pre_processor = pre_processor,
base_format = base_format
)
}
This solution is not 100% satisfying because I don't think that rmarkdown pre_processor was created for having side-effects. But it works.

Related

Package build/check removes htmlwidget-derived files

I'm having trouble building a package that uses plotly figures as htmlwidgets. It appears that the supporting files containing the figures and related resources are either never created or are deleted at some point in the build/check process.
I have asked a related question here which used buildVignette to create just the vignette. While that question was resolved, using the solution there does not work when doing a full build and check.
I have created a minimal package for testing on Github. If one builds/checks/installs and then looks at the vignette, one sees:
and indeed none of that directory structure or files are present in the built package.
The odd thing is this worked fine around the beginning of February in the real context (i.e. not a test package, the documentation shows the working widgets here) suggesting something has changed, but I cannot figure out what changed.
I have looked carefully at combinations of recent versions of the dependent packages and that does not seem to be the problem. I have also looked at recent changes in r-source files build.R and check.R. The only relevant change is in this commit:
commit eed23bae244b17b9366bf7bf6863f6f51f17064d
Author: smeyer <smeyer#00db46b3-68df-0310-9c12-caf00c1e9a41>
Date: Sat Feb 19 01:10:34 2022 +0000
'R CMD Sweave --clean' no longer removes existing files (PR#18242)
but I don't think this is the problem. The build/check code is complex and I cannot figure out what is happening to these files.
To embed interactive graphics in a vignette we could build it using output rmarkdown::html_vignette, a lightweight version of html_document.
---
title: "Test Vignette"
output:
rmarkdown::html_vignette:
toc: yes
vignette: >
%\VignetteIndexEntry{Vignette}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{r SetUp, echo = FALSE, eval = TRUE, results = "hide"}
set.seed(13)
suppressPackageStartupMessages(library("knitr"))
suppressPackageStartupMessages(library("plotly"))
opts_chunk$set(eval = TRUE, echo = FALSE)
```
**Test Vignette**
Problem solved?
```{r testFig, results = "show"}
if (!is_latex_output()) {
DF <- data.frame(
x <- rnorm(50),
y <- rnorm(50),
z <- rnorm(50))
plot_ly(
name = "data", DF, x = ~x, y = ~y, z = ~z,
marker = list(size = 2.0)) %>%
add_markers()
}
```
html_vignette presets are slightly different from html_document. Because of this the graphic can appear quite small, some manual adjustment might be needed. Other stylistic changes can be made using CSS. The default file can be found using
system.file("rmarkdown", "templates", "html_vignette", "resources", "vignette.css", package = "rmarkdown")
While it passes R CMD checks, it may hurt some feelings if submitted to CRAN:
neither data nor documentation should exceed 5MB

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.

How can I make the output of my function (several ggplot2 graphs) an html file (displaying those graphs)?

I'm writing a personal use package which trains/tests models, and finally runs a myriad of LIME and DALEX explanations on them. I save these as their own ggplot2 objects (say lime_plot_1), and at the end of the function these are all returned to the global environment.
However, what I would like to have happen is that, at the end of the function, not only would I have these graphs in the environment but a small html report would also be rendered - containing all the graphs that were made.
I would like to point out that while I do know I could do this by simply using the function within an Rmarkdown or Rnotebook, I would like to avoid that as I plan on using it as an .R script to streamline the whole process (since I'll be running this with a certain frequency), and from my experience running big chunks in .Rmd tends to crash R.
Ideally, I'd have something like this:
s_plot <- function(...){
1. constructs LIME explanations
2. constructs DALEX explanations
3. saves explanations as ggplot2 objects, and list them under graphs_list
4. render graphs_list as an html file
}
1, 2, and 3 all work but I haven't found a way to tackle 4. that doesn't include doing the whole process in a .Rmd file.
EDIT: Thanks to #Richard Telford's and #Axeman's comments, I figured it out. Below is the function:
s_render <- function(graphs_list = graphs_list, meta = NULL, cacheable = NA){
currentDate <- Sys.Date()
rmd_file <- paste("/path/to/folder",currentDate,"/report.Rmd", sep="")
file.create(rmd_file)
graphs_list <- c(roc_plot, prc_plot, mp_boxplot, vi_plot, corr_plot)
c(Yaml file headers here, just like in a regular .Rmd) %>% write_lines(rmd_file)
rmarkdown::render(rmd_file,
params = list(
output_file = html_document(),
output_dir = rmd_file))}
First, create a simple Rmarkdown file, that takes a parameter. The only objective of this file is to create the report. You can for instance pass a file name:
---
title: "test"
author: "Axeman"
date: "24/06/2019"
output: html_document
params:
file: 'test.RDS'
---
```{r}
plot_list <- readRDS(params$file)
lapply(plot_list, print)
```
I saved this as test.Rmd.
Then in your main script, write the plot list to a temporary file on disk, and pass the file name to your markdown report:
library(ggplot2)
plot_list <- list(
qplot(1:10, 1:10),
qplot(1:10)
)
file <- tempfile()
saveRDS(plot_list, file)
rmarkdown::render('test.Rmd', params = list(file = file))
An .html file with the plots is now on your disk:

Alternative to R View() in server environment with no X11

I work with R in a linux server and would like to have a functionality similar to View() in RStudio where you can look at your dataset in a tabular format.
The problem is I will not have x11 enabled, this is not an option.
Is there any good alternative way?
You can use the package tableHTML which produces an HTML table that can be seen in the viewer and/or browser.
It is fairly easy to use, all you need to to is:
library(tableHTML)
tableHTML(mtcars, rownames = FALSE, theme = 'scientific')
This returns:
This is similar to clemens' answer but produces searchable and sortable output:
Use a parameterized report and knit it to HTML using rmarkdown::render. The resulting HTML file is opened in the default browser.
Create view_template.Rmd in your working directory with the following contents:
---
params:
myinput: ""
---
```{r, echo = FALSE}
DT::datatable(params$myinput, options = list(pageLength = 20))
```
To view a data set, run browseURL(rmarkdown::render(input = "view_template.Rmd", params = list(myinput = iris))), replacing iris by whichever data set is to be shown.
Of course, this could be wrapped in a nice helper function to get code that is better readable and easier to (re-) use. You need to install the packages DT and rmarkdown before running the code.
Tested on Windows 10; hopefully passing a file path to browseURL works on Linux as well.
Output:

knitr/Rmd: Adding title page and text when converting to MS Word

I'm trying to write a report using the rmarkdown package and as it is, unfortunately, customary in my field reports are often submitted as MS Word documents. So I can't always rely on the power of LaTeX and have to be able convert my .Rmd to MS Word. Now, because I want to be able to create PDF and MS Word files from the same source file, I'm trying to find a general way to do this. I've got PDF working using the apa6 LaTeX-document class. The .Rmd will look something like this when creating a Word file:
---
title: My title
abstract: This is the abstract.
author: John Doe
affiliation: Unknown
note: Nothing to say.
output:
word_document:
reference_docx: myreference.docx
---
Lorem ipsum.
I can create a Word document from this but for obvious reasons my custom yaml-variables (e.g. abstract) will not be rendered in the document.
So basically, my problem is the following:
When creating a word document, how can I add a title page (including author names, affiliations, author notes, etc.) and another page with just the abstract before the document body ("Lorem ipsum")? The focus here is not to create pagebreaks (there are other open questions on this), but rather **is there a way to make pandoc use the custom yaml variables place them at the beginning of the document and assign styles to them?
The rmarkdown package provides an include() function but it only works with HTML and PDF documents.
I have found that it's possible to customize the content of the Markdown file (e.g. to add and modify a title page) generated by rmarkdown before submitting it to pandoc for the conversion to DOCX by using a preprocessor. Let's assume we are trying to add some information specified in a YAML parameter note just before the abstract (support for abstracts has in the meantime been added to pandoc).
To do so, we first need a preprocessor function that reads the input file and parses the YAML front matter, and customizes the input file:
my_pre_processor <- function(metadata, input_file, runtime, knit_meta, files_dir, output_dir, from) {
# Identify YAML front matter delimiters
input_text <- readLines(input_file, encoding = "UTF-8")
yaml_delimiters <- grep("^(---|\\.\\.\\.)\\s*$", input_text)
if(length(yaml_delimiters) >= 2 &&
(yaml_delimiters[2] - yaml_delimiters[1] > 1) &&
grepl("^---\\s*$", input_text[yaml_delimiters[1]])) {
yaml_params <- yaml::yaml.load(paste(input_text[(yaml_delimiters[1] + 1):(yaml_delimiters[2] - 1)], collapse = "\n"))
} else yaml_params <- NULL
# Modify title page
custom_lines <- c(
"NOTE:"
, metadata$note
, "\n\n"
, "# Abstract"
, "\n"
, metadata$abstract
, "\n"
)
## Add modified title page components after YAML front matter
augmented_input_text <- c(custom_lines, input_text[(yaml_delimiters[2] + 1):length(input_text)])
# Remove redundant default abstract
yaml_params$abstract <- NULL
# Add modifications to input file
augmented_input_text <- c("---", yaml::as.yaml(yaml_params), "---", augmented_input_text)
input_file_connection <- file(input_file, encoding = "UTF-8")
writeLines(augmented_input_text, input_file_connection)
close(input_file_connection)
NULL
}
Now we need to define a custom format that utilizes our preprocessor:
my_word_document <- function(...) {
config <- rmarkdown::word_document(...)
# Preprocessor functions are adaptations from the RMarkdown package
# (https://github.com/rstudio/rmarkdown/blob/master/R/pdf_document.R)
pre_processor <- function(metadata, input_file, runtime, knit_meta, files_dir, output_dir, from = .from) {
# save files dir (for generating intermediates)
saved_files_dir <<- files_dir
args <- my_pre_processor(metadata, input_file, runtime, knit_meta, files_dir, output_dir, from)
args
}
config$pre_processor <- pre_processor
config
}
Now, you can use the custom format when rendering R Markdown documents as follows:
rmarkdown::render("./foo/bar.Rmd", output_format = my_word_document())

Resources