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
Related
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.
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:
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.
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:
I would like to send to someone else a presentation that I have created using R and slidfy, but it contains sensitive information, so putting it up onto github pages using the gh-pages branch and then sending the URL isn't really an option, since all github pages are public, as suggested here.
Pushing it up to the glimmer shiny server as well seems a bit unsecure too...(I would ideally like to do this for free so setting up a server to host the one presentation seems a bit cumbersome and overkill for my purposes)
I don't think dropbox would work either since any URL link that is produced if someone else types it into an address bar, would probably be able to download it and view the sensitive information...
Is there a way of sending the presentation (by email or other methods) that contains all the necessary files for it to work, so that a person who doesn't use R can open it and view it easily. (i.e. without having to send them a zip file of all the files (i.e. the assets and libraries and figure folders etc), asking them to unzip it and then opening the index.html file)?
Edit
I forgot to mention that the presentations include nvd3 and morrisjs graphs too, making it difficult to bring over all the files in one go...
Edit2
Given all the libraries used are public libraries, is there a way for it to reference a URL instead of a local drive?
Here is how you would do it with Slidify. There are two tricks to use.
Specify mode: standalone in your YAML front matter. This makes sure that all slide related JS and CSS assets are served from an online CDN, and also that all static images are converted into data URLs.
Use n1$print('mychart', include_assets = TRUE, cdn = TRUE) when you print the chart in your knitr code chunk. This makes sure that all chart relate assets are included and served from an online CDN. Note that for each library, you should use include_assets only once, so that you don't duplicate.
This approach is not very robust since you are linking to multiple JS libraries in a single file, and as a result there could be conflicts. Case in point, MorrisJS does not play well with Google IO2012, since Google IO2012 uses requireJS and for some reason raises conflicts.
You can also use the same code chunks in RStudio Presentations and save them as standalone HTML. Here is the same presentation in RPres format.
---
title : Standalone Presentation with Slidify
author : Ramnath Vaidyanathan
mode : standalone
---
## Plain Text
This is a slide with plain text
> 1. Point 1
> 2. Point 2
> 3. Point 3
---
## R Plot
```{r message = F}
require(ggplot2)
qplot(wt, mpg, data = mtcars)
```
---
## NVD3 Plot
```{r results = 'asis', comment = NA, message = F, echo = F}
require(rCharts)
n1 <- nPlot(mpg ~ wt, data = mtcars, type = 'scatterChart', group = 'gear')
n1$print('chart2', include_assets = TRUE, cdn = TRUE)
```
<style>
.rChart {
height: 500px;
}
</style>
---
## Another NVD3 Plot
```{r results = 'asis', comment = NA, message = F, echo = F}
require(rCharts)
n2 <- nPlot(mpg ~ cyl, data = mtcars, type = 'scatterChart')
n2$print('chart3')
```
Another option is to
1) Open the HTML file in Chrome,
2) Choose the option to print
3) Save it as .pdf.
It's not perfect for all cases but definately a decent option to consider.
I think your last method is the safest.
Do you really want to use slidify? With the latest (preview) version of Rstudio you can create HTML5 presentations on the fly. And it is much easier than slidify. To distribute the slideshow you just have to mail the standalone output html file (if you aren't using fancy things like nvd3 or latex).
Here is some more information.
link1
link2
link3