Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

basic chunks vignette #21

Merged
merged 17 commits into from
Apr 28, 2022
3 changes: 3 additions & 0 deletions inst/WORDLIST
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
funder
Reproducibility
reproducibility
RStudio
UI
240 changes: 240 additions & 0 deletions vignettes/basic_chunks.Rmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
---
title: "Basic chunks"
author: "NEST coreDev"
date: "2022-04-22"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Basic chunks}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---

# The chunks container

The main concept behind the code chunks is the chunks container. This container consists of two elements:

1. A stack of quoted R expressions, each called a "chunk"
2. An environment carrying variable values

<img src="images/container.png" alt="container" style="width: 100%;"/>

Each chunk can be evaluated inside the current environment. To evaluate all chunks inside the same environment, the `chunks` R6 object was created in `teal.code`. We refer to this object as "chunks container". It internally has a stack of chunks and the environment where they will get evaluated.
mhallal1 marked this conversation as resolved.
Show resolved Hide resolved

The next sections will explain what is a chunk and how it is evaluated.
mhallal1 marked this conversation as resolved.
Show resolved Hide resolved

# What is a chunk?

A quoted R expression is a **chunk**. Quoted R expressions can be created in three different ways:
nikolas-burkoff marked this conversation as resolved.
Show resolved Hide resolved
```{r}
a <- 3

# Creating a chunk by quote ------------------------------
expr_a <- quote(sum(a, a))
print(expr_a)
print(class(expr_a))


# Creating a chunk by bquote ------------------------------
expr_b <- bquote(b <- sum(a, a))
print(expr_b)
print(class(expr_b))

# Creating a chunk by call -------------------------------
expr_c <- call("sum", a, a)
print(expr_c)
print(class(expr_c))
```

As seen above, a chunk is of class `call` or an assignment given by class `<-`. To evaluate such expressions, R uses the `eval` function. This function evaluates each single `call` inside the current environment, in case no other environment is given. In the example code you can see what happens upon evaluating the chunks:
nikolas-burkoff marked this conversation as resolved.
Show resolved Hide resolved

```{r}
a <- 3
expr_a <- quote(sum(a, a))
expr_b <- bquote(b <- a + a)
expr_c <- call("sum", a, a)

eval(expr_a)
eval(expr_b)
print(b)
eval(expr_c)
```

The next sections will tell in a step by step guide which features are provided by the `chunks` object.

# Step by step to understand chunks

## General information

Normally as a module developer the chunks will be used within the `server` function of a shiny/teal module. This enables storing the chunks container inside the shiny session. For simplicity reasons this feature will be used in the tutorial. To store a container inside the shiny session simply use the call `init_chunks()` and the `chunks` R6 object will be stored in `session$userData$<MODULENAME>$chunks`. After using `init_chunks()`, the functions dealing with chunks can be used. Those are recommended. If for any reasons you want to use your own chunks container, it is possible. Please see the the table in the last section of this article to see the similarities and differences between these two approaches. So for now, we just call `teal.code::init_chunks()`.
nikolas-burkoff marked this conversation as resolved.
Show resolved Hide resolved

As a simulation of the teal environment the `init_session` function is provided:

```{r}
# pseudo code simulating a shiny session ----------------------------
init_session <- function() {
session <- new.env()
session$userData <- new.env() # nolint
session$ns <- function(x) paste0("x-", x)
mhallal1 marked this conversation as resolved.
Show resolved Hide resolved
return(session)
}

# initializing code chunks -------------------------------------------
session <<- init_session()
nikolas-burkoff marked this conversation as resolved.
Show resolved Hide resolved
teal.code::init_chunks(session = session)
```

## Feature 1: Reset (initialize the environment)

Normally reproducible code will be used inside a `renderPlot` or `renderTable` call of a shiny module. For simplicity reasons we just use the *pseudo* shiny session in this tutorial. As a first step the chunks container should be handed over an analysis dataset (`ANL`) and two variables `x = "abc"`, `y = 5`. Therefore you need to use the `teal.code::chunks_reset` function. It not only empties all current chunks inside the container, but also hands over all variables from the current environment to the container environment.
mhallal1 marked this conversation as resolved.
Show resolved Hide resolved

To check that it worked as expected the function `teal.code::chunks_get_var` will be used and check that the values inside the chunks container are equal to the values from the environment.

You can use this code snippet:

```{r}
# Adding variables to the chunks -------------------------------------

ANL <- data.frame(left = c(1, 2, 3), right = c(4, 5, 6)) # nolint
nikolas-burkoff marked this conversation as resolved.
Show resolved Hide resolved
x <- "abc"
y <- 5

teal.code::chunks_reset()

# Double check variables were handed over-----------------------------
all.equal(x, teal.code::chunks_get_var("x"))
```

<img src="images/reset.png" alt="reset" style="width: 100%;"/>


## Feature 2: Push - adding code snippets

To populate chunks, a chunk can be added using the `teal.code::chunks_push` function. Here two code snippets will be added:

```{r}
# Code needs modification before it can be run:
# - chunks need to exist inside the environment
nikolas-burkoff marked this conversation as resolved.
Show resolved Hide resolved
teal.code::chunks_push(bquote(y <- y + 1))
teal.code::chunks_push(bquote(x <- paste0(x, y)))
```

<img src="images/push.png" alt="push" style="width: 100%;"/>

## Feature 3: Get R code - showing the chunks code

To reproduce what was done inside the chunks, it is necessary to render the R code inside them. Therefore the chunks container can display all its code by calling `teal.code::chunks_get_rcode`. You can run this example to see the code:

```{r}
# Code needs modification before it can be run:
# - chunks need to exist inside the environment
# - chunks needed to be pushed to the container
nikolas-burkoff marked this conversation as resolved.
Show resolved Hide resolved
teal.code::chunks_get_rcode()
```
<img src="images/get_rcode.png" alt="get_rcode" style="width: 100%;"/>

## Feature 4: `eval` - evaluating the code

The `eval` function is responsible to run the code inside the chunks container. The `eval` function of a chunks container is called `teal.code::chunks_safe_eval`. It evaluates all chunks inside the container in the order they were pushed. It is not possible to change the order or run just pieces of the code. The chunks will always be evaluated without any error, even if an error appears. You will learn more about errors in following parts.
mhallal1 marked this conversation as resolved.
Show resolved Hide resolved

The `teal.code::chunks_safe_eval` will always return the value of the last evaluation. By `teal.code::chunks_get_var` it is possible to retrieve specific variables after evaluation.

```{r}
# Code needs modification before it can be run:
# - chunks need to exist inside the environment
# - chunks needed to be pushed to the container
teal.code::chunks_safe_eval()
teal.code::chunks_get_var("x")
teal.code::chunks_get_var("y")
```

<img src="images/eval.png" alt="eval" style="width: 100%;"/>

## Feature 5: Is ok - check for errors and warnings

As already said the chunks will never throw an error upon execution. Errors are caught and stored in error messages. The most important function to check if everything went fine is `teal.code::chunks_is_ok`. It will return `TRUE` in case everything was fine.
mhallal1 marked this conversation as resolved.
Show resolved Hide resolved

`teal.code::chunks_validate_is_ok` returns a useful `validate(need(...))` message inside the shiny app in case something went wrong.

Additionally, the function `teal.code::chunks_safe_eval` can be used inside shiny, which will evaluate the chunks container and check if everything went right.
mhallal1 marked this conversation as resolved.
Show resolved Hide resolved

```{r, error=TRUE}
# Code needs modification before it can be run:
# - chunks need to exist inside the environment
# - chunks needed to be pushed to the container
nikolas-burkoff marked this conversation as resolved.
Show resolved Hide resolved
teal.code::chunks_is_ok()
teal.code::chunks_validate_is_ok()

# Trying an error inside a chunk ------------------------
teal.code::chunks_push(quote(stop("ERROR")))
teal.code::chunks_safe_eval()

teal.code::chunks_is_ok()

teal.code::chunks_validate_is_ok()
```

<img src="images/is_ok.png" alt="is_ok" style="width: 100%;"/>

## Tutorial Summary

In summary:

1. chunks host code snippets
nikolas-burkoff marked this conversation as resolved.
Show resolved Hide resolved
2. chunks host their own environment
3. chunks are initialized inside shiny/teal using `teal.code::init_chunks`
4. chunks can be accessed to retrieve variables from the environment using `teal.code::chunks_get_var`
5. chunks can be added to the chunks container by `teal.code::chunks_push`
6. All chunks inside a container can be executed by `teal.code::chunks_eval` or
`teal.code::chunks_safe_eval`
mhallal1 marked this conversation as resolved.
Show resolved Hide resolved
7. `teal.code::chunks_validate_is_ok` and `teal.code::chunks_is_ok` allow checking for execution errors

The whole implementation of this tutorial is given in the *gif* below:

<img src="images/chunks_animation.gif" alt="chunks_animation" style="width: 100%;"/>


For more information about the implementation of chunks inside of shiny/teal module, please visit the Advanced chunks article.

Please find below the implicit vs. explicit usage of code chunks.

---

## Implementation of code chunks

There are two ways to initialize the code chunks inside shiny modules:

-Using R6 implementation: `session_chunks <- chunks$new()`.

-Using `teal.code` wrappers: `teal.code::init_chunks()`.
nikolas-burkoff marked this conversation as resolved.
Show resolved Hide resolved

For any manipulation of the downstream chunks, the `teal.code` wrappers below can be used:
mhallal1 marked this conversation as resolved.
Show resolved Hide resolved


```{r}
session <<- init_session()
teal.code::init_chunks()
a <- 1
b <- 2

# set a & b in env
teal.code::chunks_reset()
nikolas-burkoff marked this conversation as resolved.
Show resolved Hide resolved
# push to chunks
teal.code::chunks_push(expression = bquote(a <- a + 1))
# eval gives return value
teal.code::chunks_eval()
nikolas-burkoff marked this conversation as resolved.
Show resolved Hide resolved
stopifnot(teal.code::chunks_get_var("a") == 2)
nikolas-burkoff marked this conversation as resolved.
Show resolved Hide resolved

teal.code::chunks_push(expression = bquote(c <- a + b))
teal.code::chunks_eval()
stopifnot(teal.code::chunks_get_var("c") == 4)
teal.code::chunks_push(expression = quote(a + b + c))
stopifnot(teal.code::chunks_eval() == 8)
# merging two chunks objects
session_chunks2 <- teal.code::chunks$new()
teal.code::chunks_push(bquote(d <- 1), chunks = session_chunks2)
teal.code::chunks_push_chunks(session_chunks2)
teal.code::chunks_eval()
teal.code::chunks_get_var("d")
mhallal1 marked this conversation as resolved.
Show resolved Hide resolved
```

In these wrappers, the R6 stack is coupled in a reactive way to the shiny session to provide module developers with a reactive chunk stacks. For more information about the explicit R6 methods, please refer to the implementation of the wrappers above.
mhallal1 marked this conversation as resolved.
Show resolved Hide resolved
Binary file added vignettes/images/chunks_animation.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added vignettes/images/container.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added vignettes/images/eval.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added vignettes/images/get_rcode.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added vignettes/images/is_ok.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added vignettes/images/push.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added vignettes/images/reset.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added vignettes/images/show_r_code.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 60 additions & 0 deletions vignettes/teal-code.Rmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
title: "Reproducibility"
author: "NEST coreDev"
date: "2022-04-22"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Reproducibility}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---


Reproducibility is an important feature when it comes to data analysis for the following reasons:

- Ability of users to reproduce the outputs at any moment in a simple R console outside of a reactive shiny app.
- Provides transparency where it helps users and others understand what happened during the analysis.
mhallal1 marked this conversation as resolved.
Show resolved Hide resolved

This is where the `chunks` concept of the`teal.code` package comes into play. It provides a mechanism to develop reproducible shiny/teal modules. The `teal` package complements the chunks concept by providing a server and an interface to retrieve the reproducible code via `Show R code` button.
mhallal1 marked this conversation as resolved.
Show resolved Hide resolved

Note that there is a public [`shinymeta`](https://github.com/rstudio/shinymeta) R package from `RStudio` that offers similar functionality. However, currently, `shinymeta` can not be easily integrated into `teal` modules and hence we recommend using `chunks` for `teal` based apps.

# Reproducibility in teal

Teal applications can be designed and set up such that for every output displayed, the associated R code can be requested via the **Show R code button**. In order to develop a teal application with reproducible outputs the app developer needs to take this into account when implementing the app.

The reproducible code displayed for outputs in `teal` is always made from three parts:

1. Header which includes information such as:
- directory path
- server name
- R version
- date
- `libPaths()`
- packages' versions.

2. Preprocessing Code: refers to the code which precedes the teal app initialization, this includes:
- Data imports
- Data transformation
- Checking data reproducibility

3. Teal Module Analysis Code which includes:
- [Data merging](https://insightsengineering.github.io/teal.transform/articles/transforming-teal-data) (optional)
- Filtering and encodings
- Data analysis/visualization

<img src="images/show_r_code.gif" alt="SRC" style="width: 100%;"/>

The header is created by `teal`, the preprocessing code must be supplied by the app developer and the analysis code is provided by the teal modules.

# Chunks

The code chunks were introduced to allow the development of reproducible `shiny`/`teal`modules. A reproducible module includes a "Show R Code" button which can display the code needed to reproduce the outputs of the module. Inside the modal, which pops up after clicking the button, you should see:
mhallal1 marked this conversation as resolved.
Show resolved Hide resolved

1. The code loading the necessary libraries - by `teal::get_code`
2. The code used for loading and filtering data sets - by `teal::FilteredData`
3. The code used for merging multiple data sets - by `teal.transform::data_merge_srv`
4. The code generating the outputs - by **code chunks**


The code chunks were especially designed for the code leading from an input data set to the outputs. To get more information about the concept of chunks, please refer to article [`basic chunks`](https://insightsengineering.github.io/teal.code/articles/basic_chunks.html).