Search code examples
rplotlayoutr-markdownrnotebook

How to get console output and plot side by side in a R Notebook?


In a R Notebook there is a function that makes many plots and print summary statistics in the console. I would like to get the plot and the console output (i.e. summary statistics) side by side on the HTML output.

Here is a very simple example:

---
title: "Example"
output: html_notebook
---


```{r}
mapply(FUN = function(.x) {
  plot(.x)
  summary(.x)
}, split(iris, iris$Species), SIMPLIFY = FALSE)
```

The output of the above code looks like:

enter image description here

As you can see, plots are one after the other and console outputs are somewhere in the middle-end.

The expected output looks like:

enter image description here

I went through these SO topics without success:

Please note that I am really trying to get the console output, if possible I would like to avoid using many tranformation to get a grob or a picture as, sometimes, relevant methods for my actual outputs do not exist. It is ok no to use R Notebook but RMarkdown instead.


Solution

  • It's not perfect. For example, I didn't add in handlers for screen size. However, I can. You would have to let me know what you're looking for, though.

    This uses Javascript in R Markdown. The engine for this is built-in. However, I added the code for you to check it (should you so choose): names(knitr::knit_engines$get()).

    Also, in the setup chunk, I added options(width = 75). This will affect all chunk outputs. You can change this to make it a chunk-specific option. However, the default is 80, so you probably won't notice a difference. I did this because for two of the three groups, Species wrapped to the next row. However, for versicolor, it was different. This is part of the enforcement for uniformity.

    ```{r setup, include=FALSE}
    knitr::opts_chunk$set(echo = FALSE)
    
    # confirm engine for 'js' (it was #37 for me)
    names(knitr::knit_engines$get())
    
    # set the output width for chunks' render
    # this is to keep the summaries even (versicolor was doing its own thing)
    options(width = 75)
    library(tidyverse)
    ```
    

    The styles are not in a chunk. This goes between the setup and the next chunk.

    <style>
    .setupCols {
      display:flex;
      flex-direction:row;
      width: 100%;
    }
    .setupCols p{
      display:flex;
      flex-direction: column;
      width: 45%;
    }
    
    .setupCols pre {
      display:flex;
      flex-direction: column;
      width: 55%
    }
    .setupCols pre code {
      font-size: 85%;
    }
    </style>
    

    Next is some code before your mapply call and code after.

    <div class="setupCols">
    
    ```{r graphOne}
    mapply(FUN = function(.x) {
      plot(.x)
      summary(.x)
    }, split(iris, iris$Species), SIMPLIFY = FALSE)
    ```
    
    </div>
    

    At any point in time after this chunk, you will add a styling chunk. If you want it to apply more than once, move this chunk to the end. If you only want it to apply to the chunk (I named) graphOne, then make it the next chunk.

    ```{r styler,results='asis',engine='js'}
    
    // search for class and tags
    elem = document.querySelector('div.setupCols > pre > code');
    // remove hashtags
    elem.innerHTML = elem.innerHTML.replace(/#{2}/g, '');
    // add newlines between summaries
    elem.innerHTML = elem.innerHTML.replace(/\s{9}\n/g, '<br /><br />')
    
    ```
    

    If you run this chunk inline, you will not get any output. However, you will see this output when you knit.

    I also added some text here, but this is what it looks like:

    enter image description here

    If you wanted to see what the Javascript is doing, you could add eval = F and knit. This is what it will look like:

    enter image description here

    Let me know if I missed something or if you have any questions.