Data Transformations as shiny Module
NEST CoreDev
Source:vignettes/data-transform-as-shiny-module.Rmd
data-transform-as-shiny-module.Rmd
Introduction
teal
version 0.16
introduced new argument
in teal::module
called transformers
. This
argument allows to pass a list
of
teal_data_module
objects that are created using
teal_transform_module()
function.
The main benefit of teal_transform_module()
is the
ability to transform data before passing it to the module. This feature
allows to extend the regular behavior of existing modules by specifying
custom data operations on data inside this module.
teal_transform_module()
is a Shiny module that takes
ui
and server
arguments. When provided,
teal
will execute data transformations for the specified
module when it is loaded and whenever the data changes.
server
extend the logic behind data manipulations, where
ui
extends filter panel with new UI elements that
orchestrate the transformer inputs.
This vignette presents the way on how to manage custom data
transformations in teal
apps.
Creating your first custom data transformation module
We initialize a simple teal
app where we pass
iris
and mtcars
as the input datasets.
data <- within(teal_data(), {
iris <- iris
mtcars <- mtcars
})
app <- init(
data = data,
modules = teal::example_module()
)
if (interactive()) {
shinyApp(app$ui, app$server)
}
Single Transformer
Let’s create a simple teal_transform_module
that returns
the first n
number of rows of iris
based on
the user input.
We do this by creating the ui
with the
numericInput
for the user to input the number of rows to be
displayed. In the server
function we take in the reactive
data
and perform this transformation and return the new
reactive data
.
data <- within(teal_data(), {
iris <- iris
mtcars <- mtcars
})
my_transformers <- list(
teal_transform_module(
label = "Custom transform for iris",
ui = function(id) {
ns <- NS(id)
tags$div(
numericInput(ns("n_rows"), "Number of rows to subset", value = 6, min = 1, max = 150, step = 1)
)
},
server = function(id, data) {
moduleServer(id, function(input, output, session) {
reactive({
within(data(),
{
iris <- head(iris, num_rows)
},
num_rows = input$n_rows
)
})
})
}
)
)
app <- init(
data = data,
modules = teal::example_module(transformers = my_transformers)
)
if (interactive()) {
shinyApp(app$ui, app$server)
}
Note: It is recommended to return reactive()
with teal_data()
in server
code of a
teal_transform_module
as this is more robust for
maintaining the reactivity of Shiny. If you are planning on using
eventReactive()
in the server, the event should include
data()
(example
eventReactive(list(input$a, data()), {...})
). More in
this
discussion.
Multiple Transformers
Note that we can add multiple teal
transformers by
including teal_transform_module
in a list.
Let’s add another transformation to the mtcars
dataset
that creates a column with rownames
of mtcars
.
Note that this module does not have interactive UI elements.
data <- within(teal_data(), {
iris <- iris
mtcars <- mtcars
})
my_transformers <- list(
teal_transform_module(
label = "Custom transform for iris",
ui = function(id) {
ns <- NS(id)
tags$div(
numericInput(ns("n_rows"), "Number of rows to subset", value = 6, min = 1, max = 150, step = 1)
)
},
server = function(id, data) {
moduleServer(id, function(input, output, session) {
reactive({
within(data(),
{
iris <- head(iris, num_rows)
},
num_rows = input$n_rows
)
})
})
}
),
teal_transform_module(
label = "Custom transform for mtcars",
ui = function(id) {
ns <- NS(id)
tags$div(
"Adding rownames column to mtcars"
)
},
server = function(id, data) {
moduleServer(id, function(input, output, session) {
reactive({
within(data(), {
mtcars$rownames <- rownames(mtcars)
rownames(mtcars) <- NULL
})
})
})
}
)
)
app <- init(
data = data,
modules = teal::example_module(transformers = my_transformers)
)
if (interactive()) {
shinyApp(app$ui, app$server)
}