rmarkdown and terminal colors

The R output in rmarkdown strips all terminal control sequences – colors and formats (i.e., bold or italics). However, it is relatively easy to restore it. For this, one needs to install the fansi package and include the following chunk in the rmarkdown document, hooking a custom function to the output:

    ```{r echo=FALSE}
    options(crayon.enabled = TRUE)
    knitr::knit_hooks$set(output = function(x, options){
      paste0(
        '<pre class="r-output"><code>',
        fansi::sgr_to_html(x = htmltools::htmlEscape(x), warn = FALSE),
        '</code></pre>'
      )
    })
    ```

If you are using the crayon package, however, you might run into the following problem: in some situations crayon “thinks” that the terminal has a limited capability of displaying colors, and will use only the 16 base colors. Even if more colors are available. One such situation is when one includes colored output in a vignette processed automatically – there is no way to convince the num_colors function from crayon that it should report 256 colors.

Therefore, we need to substitute the num_colors function by a dumber version:

num_colors <- function(forget=TRUE) 256
library(crayon)
assignInNamespace("num_colors", num_colors, pos="package:crayon") 

R, rmarkdown, cache and objects

If your rmarkdown takes hours to generate, and you want to be able to generate different document output types on the fly, using the output_format option from rmarkdown::render is extremely annoying: every time you change the output format, the cache is reset, so you need to wait hours to get the other format.

I found no clean solution to this problem, but here is an ugly hack. We create a copy of the document and render it. First time it will take hours, but then the cache will be separate from your original document:

file.copy("test.rmd", "test_html.rmd", overwrite=TRUE)
rmarkdown::render("test_html.rmd")

Of course, this is annoying, and we can wrap this two commands with a function. But beware! Markdown by default evaluates in its parent environment, so to make sure it is evaluated in the global environment, you need to set an option. Here is a wrapper function which also opens by default the resulting document in google-chrome:

myrender <- function(fn, open=TRUE) {

  fb <- gsub("\\.rmd$", "", fn, ignore.case=TRUE)
  fn2 <- paste0(fb, "_html.rmd")
  file.copy(fn, fn2, overwrite=T)
  res <- render(fn2, output_format="html_document", envir=globalenv())
  system(sprintf("google-chrome %s", res))
  res
} 

Invert a list / map

Often we use lists to map keywords onto values, for example

foo <- list(a=c("quark", "fark"), 
            b=c("quark", "foo", "bark"), 
            c=c("fark", "bark"))

To invert this list (such that “fark”, “bark” etc. become keywords, and “a”, “b” and “c” the values), do

foo.rev <- split(rep(names(foo), lengths(foo)), unlist(foo))

split splits a vector or data frame along a factor. In this case, we expand the names of foo using rep such that we get two vectors, as can be seen with the following command:

cbind(rep(names(foo), lengths(foo)), unlist(foo))

with the result

   [,1] [,2]   
a1 "a"  "quark"
a2 "a"  "fark" 
b1 "b"  "quark"
b2 "b"  "foo"  
b3 "b"  "bar"  
c1 "c"  "fark" 
c2 "c"  "bar"

When we apply split() to the first vector with the second to guide the split, we will get

$bar
[1] "b" "c"

$fark
[1] "a" "c"

$foo
[1] "b"

$quark
[1] "a" "b"

R, shiny and source()

This one cost me more time to figure out than it should have. The reason being, it turns out that I never properly understood what the source() function does.

So here is the story: I was setting up a shiny server for a student based on her code. She was running the shiny app from within RDesktop, and so before starting the app with runApp() she would load all necessary object and source() a file called helpers.R with some common calculations.

In order to put the app on a server, I have moved these pre-runApp() initializations into ui.R and server.R. Suddenly, weird errors appeared. The functions in the helpers.R no longer seemed to be able to find anything in the parent environment — object X not found! Even though I called source() immediately after loading the necessary objects into the environment:

# file server.R
load("myobjects.rda")
source("helpers.R")

The solution was, as usual, to read documentation. Specifically, documentation on source():

local   TRUE, FALSE or an environment, determining where the 
        parsed expressions are evaluated. FALSE (the default) 
        corresponds to the user's workspace (the global 
        environment) and TRUE to the environment from which 
        source is called.

The objects which I have load()-ed before were not in the global environment, but instead in another environment created by shiny. However, the expressions from helpers.R were evaluated in the global environment. Thus, a new function defined in helpers.R could be seen from inside server.R, but an object loaded from server.R could not be seen by helpers.R.

It is the first time that I have noticed this. Normally, I would use a file such as helpers.R only to define helper functions, and actually call them from server.R or ui.R. However, I was thinking that source() is something like #include in C, simply calling the commands in the given file as if they were inserted at this position into the code — or called from the environment from which source() was called.

This is not so.