diff --git a/v0.15.1/404.html b/v0.15.1/404.html new file mode 100644 index 0000000000..fddd97ccd4 --- /dev/null +++ b/v0.15.1/404.html @@ -0,0 +1,131 @@ + + +
+ + + + +.github/CODE_OF_CONDUCT.md
+ We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
+We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
+Examples of behavior that contributes to a positive environment for our community include:
+Examples of unacceptable behavior include:
+Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
+Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
+This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [INSERT CONTACT METHOD]. All complaints will be reviewed and investigated promptly and fairly.
+All community leaders are obligated to respect the privacy and security of the reporter of any incident.
+Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
+Community Impact: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
+Consequence: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
+Community Impact: A violation through a single incident or series of actions.
+Consequence: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
+Community Impact: A serious violation of community standards, including sustained inappropriate behavior.
+Consequence: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
+Community Impact: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
+Consequence: A permanent ban from any sort of public interaction within the community.
+This Code of Conduct is adapted from the Contributor Covenant, version 2.1, available at https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
+Community Impact Guidelines were inspired by Mozilla’s code of conduct enforcement ladder.
+For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.
+.github/CONTRIBUTING.md
+ 🙏 Thank you for taking the time to contribute!
+Your input is deeply valued, whether an issue, a pull request, or even feedback, regardless of size, content or scope.
+ +Please refer the project documentation for a brief introduction. Please also see other articles within the project documentation for additional information.
+A Code of Conduct governs this project. Participants and contributors are expected to follow the rules outlined therein.
+All your contributions will be covered by this project’s license.
+We use GitHub to track issues, feature requests, and bugs. Before submitting a new issue, please check if the issue has already been reported. If the issue already exists, please upvote the existing issue 👍.
+For new feature requests, please elaborate on the context and the benefit the feature will have for users, developers, or other relevant personas.
+This repository uses the GitHub Flow model for collaboration. To submit a pull request:
+Create a branch
+Please see the branch naming convention below. If you don’t have write access to this repository, please fork it.
+Make changes
+Make sure your code
+Create a pull request (PR)
+In the pull request description, please link the relevant issue (if any), provide a detailed description of the change, and include any assumptions.
+Address review comments, if any
Post approval
+Merge your PR if you have write access. Otherwise, the reviewer will merge the PR on your behalf.
+Pat yourself on the back
+Congratulations! 🎉 You are now an official contributor to this project! We are grateful for your contribution.
+Suppose your changes are related to a current issue in the current project; please name your branch as follows: <issue_id>_<short_description>
. Please use underscore (_
) as a delimiter for word separation. For example, 420_fix_ui_bug
would be a suitable branch name if your change is resolving and UI-related bug reported in issue number 420
in the current project.
If your change affects multiple repositories, please name your branches as follows: <issue_id>_<issue_repo>_<short description>
. For example, 69_awesomeproject_fix_spelling_error
would reference issue 69
reported in project awesomeproject
and aims to resolve one or more spelling errors in multiple (likely related) repositories.
monorepo
and staged.dependencies
+Sometimes you might need to change upstream dependent package(s) to be able to submit a meaningful change. We are using staged.dependencies
functionality to simulate a monorepo
behavior. The dependency configuration is already specified in this project’s staged_dependencies.yaml
file. You need to name the feature branches appropriately. This is the only exception from the branch naming convention described above.
Please refer to the staged.dependencies package documentation for more details.
+This repository follows some unified processes and standards adopted by its maintainers to ensure software development is carried out consistently within teams and cohesively across other repositories.
+This repository follows the standard tidyverse
style guide and uses lintr
for lint checks. Customized lint configurations are available in this repository’s .lintr
file.
Lightweight is the right weight. This repository follows tinyverse recommedations of limiting dependencies to minimum.
+If the code is not compatible with all (!) historical versions of a given dependenct package, it is required to specify minimal version in the DESCRIPTION
file. In particular: if the development version requires (imports) the development version of another package - it is required to put abc (>= 1.2.3.9000)
.
We continuously test our packages against the newest R version along with the most recent dependencies from CRAN and BioConductor. We recommend that your working environment is also set up in the same way. You can find the details about the R version and packages used in the R CMD check
GitHub Action execution log - there is a step that prints out the R sessionInfo()
.
If you discover bugs on older R versions or with an older set of dependencies, please create the relevant bug reports.
+pre-commit
We highly recommend that you use the pre-commit
tool combined with R hooks for pre-commit
to execute some of the checks before committing and pushing your changes.
Pre-commit hooks are already available in this repository’s .pre-commit-config.yaml
file.
As mentioned previously, all contributions are deeply valued and appreciated. While all contribution data is available as part of the repository insights, to recognize a significant contribution and hence add the contributor to the package authors list, the following rules are enforced:
+git blame
query) OR*Excluding auto-generated code, including but not limited to roxygen
comments or renv.lock
files.
The package maintainer also reserves the right to adjust the criteria to recognize contributions.
+Copyright 2022 F. Hoffmann-La Roche AG + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. ++ +
SECURITY.md
+ If you believe you have found a security vulnerability in any of the repositories in this organization, please report it to us through coordinated disclosure.
+Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.
+Instead, please send an email to vulnerability.management[@]roche.com.
+Please include as much of the information listed below as you can to help us better understand and resolve the issue:
+This information will help us triage your report more quickly.
+vignettes/adding-support-for-reporting.Rmd
+ adding-support-for-reporting.Rmd
The teal
package offers an integrated reporting feature
+utilizing the teal.reporter
package. For a comprehensive
+explanation of the reporting functionality itself, please refer to the
+documentation therein.
This article is intended for module developers and aims to provide
+guidance on enhancing a custom teal
module with an
+automatic reporting feature. This enhancement enables users to
+incorporate snapshots of the module outputs into a report which can then
+be reviewed in another module automatically provided by
+teal
. Thus the app user can interact with the report.
The responsibilities of a module developer include:
+The entire life cycle of objects involved in creating the report and
+configuring the module to preview the report is handled by
+teal
.
Let us consider an example module, based on the example module from
+teal
:
+library(teal)
+example_module <- function(label = "example teal module") {
+ module(
+ label = label,
+ server = function(id, data) {
+ checkmate::assert_class(data, "reactive")
+ checkmate::assert_class(isolate(data()), "teal_data")
+
+ moduleServer(id, function(input, output, session) {
+ updateSelectInput(session, "dataname", choices = isolate(datanames(data())))
+ output$dataset <- renderPrint({
+ req(input$dataname)
+ data()[[input$dataname]]
+ })
+ })
+ },
+ ui = function(id) {
+ ns <- NS(id)
+ sidebarLayout(
+ sidebarPanel(selectInput(ns("dataname"), "Choose a dataset", choices = NULL)),
+ mainPanel(verbatimTextOutput(ns("dataset")))
+ )
+ }
+ )
+}
Using teal
, you can launch this example module with the
+following:
+app <- init(
+ data = teal_data(IRIS = iris, MTCARS = mtcars),
+ modules = example_module()
+)
+
+if (interactive()) shinyApp(app$ui, app$server)
The first step is to add an additional argument to the server
+function declaration - reporter
. This informs
+teal
that the module requires reporter
, and it
+will be included when the module is called. See below:
+example_module_with_reporting <- function(label = "example teal module") {
+ module(
+ label = label,
+ server = function(id, data, reporter) {
+ moduleServer(id, function(input, output, session) {
+ updateSelectInput(session, "dataname", choices = isolate(datanames(data())))
+ output$dataset <- renderPrint({
+ req(input$dataname)
+ data()[[input$dataname]]
+ })
+ })
+ },
+ ui = function(id) {
+ ns <- NS(id)
+ sidebarLayout(
+ sidebarPanel(selectInput(ns("dataname"), "Choose a dataset", choices = NULL)),
+ mainPanel(verbatimTextOutput(ns("dataset")))
+ )
+ }
+ )
+}
With these modifications, the module is now ready to be launched with
+teal
:
+app <- init(
+ data = teal_data(IRIS = iris, MTCARS = mtcars),
+ modules = example_module_with_reporting()
+)
+
+if (interactive()) shinyApp(app$ui, app$server)
teal
adds another tab to the application, titled
+Report previewer
. However, there is no visible change in
+how the module operates and appears and the user cannot add content to
+the report from this module. That requires inserting UI and server
+elements of the teal.reporter
module into the module
+body.
teal.reporter
module
+The UI and the server logic necessary for adding cards from
+example_module_with_reporting
to the report are provided by
+teal.reporter::simple_reporter_ui
and
+teal.reporter::simple_reporter_srv
.
+example_module_with_reporting <- function(label = "example teal module") {
+ module(
+ label = label,
+ server = function(id, data, reporter) {
+ moduleServer(id, function(input, output, session) {
+ teal.reporter::simple_reporter_srv(
+ id = "reporter",
+ reporter = reporter,
+ card_fun = function(card) card
+ )
+ updateSelectInput(session, "dataname", choices = isolate(datanames(data())))
+ output$dataset <- renderPrint({
+ req(input$dataname)
+ data()[[input$dataname]]
+ })
+ })
+ },
+ ui = function(id) {
+ ns <- NS(id)
+ sidebarLayout(
+ sidebarPanel(
+ teal.reporter::simple_reporter_ui(ns("reporter")),
+ selectInput(ns("dataname"), "Choose a dataset", choices = NULL)
+ ),
+ mainPanel(verbatimTextOutput(ns("dataset")))
+ )
+ }
+ )
+}
This updated module is now ready to be launched:
+
+app <- init(
+ data = teal_data(IRIS = iris, MTCARS = mtcars),
+ modules = example_module_with_reporting()
+)
+
+if (interactive()) shinyApp(app$ui, app$server)
A new piece of UI
has been added, and the buttons are
+clickable. The user can now add a card to the report and view it in the
+Report previewer
module but the preview is still empty
+since we have not instructed our module what to put on the card.
To add content to a card, we will utilize the public API exposed by
+the TealReportCard
class. The
+teal.reporter::simple_reporter_srv
module accepts the
+card_fun
argument that determines the appearance of the
+output from our custom module. ReportCard
and its
+derivatives allow the sequential addition of content according to the
+order of method calls. To explore the content, we can use the
+$get_content
method. For further details, refer to the
+documentation of TealReportCard
and
+teal.reporter::ReportCard
.
We will add simple text to the card by modifying the
+card_fun
argument passed to
+teal.reporter::simple_reporter_srv
. The function must
+return the card
object, otherwise errors may occur in
+teal
.
+custom_function <- function(card = teal.reporter::ReportCard$new()) {
+ card$append_text("This is content from a custom teal module!")
+ card
+}
+
+example_module_with_reporting <- function(label = "example teal module") {
+ module(
+ label = label,
+ server = function(id, data, reporter) {
+ moduleServer(id, function(input, output, session) {
+ teal.reporter::simple_reporter_srv(
+ id = "reporter",
+ reporter = reporter,
+ card_fun = custom_function
+ )
+ updateSelectInput(session, "dataname", choices = isolate(datanames(data())))
+ output$dataset <- renderPrint({
+ req(input$dataname)
+ data()[[input$dataname]]
+ })
+ })
+ },
+ ui = function(id) {
+ ns <- NS(id)
+ sidebarLayout(
+ sidebarPanel(
+ teal.reporter::simple_reporter_ui(ns("reporter")),
+ selectInput(ns("dataname"), "Choose a dataset", choices = NULL)
+ ),
+ mainPanel(verbatimTextOutput(ns("dataset")))
+ )
+ }
+ )
+}
+app <- init(
+ data = teal_data(IRIS = iris, MTCARS = mtcars),
+ modules = example_module_with_reporting()
+)
+
+if (interactive()) shinyApp(app$ui, app$server)
Now, an application user can see the text added by
+custom_function
in the Report previewer
+module.
teal.reporter
supports the addition of tables, charts,
+and more. For more information, explore the API of
+teal.reporter::ReportCard
to learn about the supported
+content types.
TealReportCard
+teal
exports the TealReportCard
class,
+which extends the teal.reporter::ReportCard
class and
+provides several convenient methods to facilitate working with
+teal
features like the filter panel or source code. For
+more details, refer to the documentation of
+TealReportCard
.
To support TealReportCard
, the function that is passed
+to teal.reporter::simple_reporter_srv
must define a default
+value for the card, as shown below:
+custom_function <- function(card = TealReportCard$new()) {
+ # ... some code ... #
+ card
+}
Without this definition, the API of TealReportCard
will
+not be available within the function.
In conclusion, we have demonstrated how to build a standard
+teal
app with code reproducibility and reporter
+functionalities. Note that the server function requires the
+filter_panel_api
argument so that the filter panel state
+can be added to the report.
In the final example, we have incorporated teal.code
+snippets. teal.code
is an R
library that
+offers utilities for storing code and associating it with an execution
+environment. This allows ReporterCard
to store the code
+necessary to generate the table along with the table itself. To learn
+more about teal.code
see the vignette
+qenv
in teal.code
.
##
+
+example_reporter_module <- function(label = "Example") {
+ module(
+ label = label,
+ server = function(id, data, reporter, filter_panel_api) {
+ with_filter <- !missing(filter_panel_api) && inherits(filter_panel_api, "FilterPanelApi")
+ moduleServer(id, function(input, output, session) {
+ updateSelectInput(session, "dataname", choices = isolate(datanames(data())))
+ dat <- reactive(data()[[input$dataname]])
+ observe({
+ req(dat())
+ updateSliderInput(session, "nrow", max = nrow(dat()), value = floor(nrow(dat()) / 5))
+ })
+
+ table_q <- reactive({
+ req(input$dataname)
+ req(input$nrow)
+ within(
+ data(),
+ result <- head(dataset, nrows),
+ dataset = as.name(input$dataname),
+ nrows = input$nrow
+ )
+ })
+
+ output$table <- renderTable(table_q()[["result"]])
+
+ ### REPORTER
+ card_fun <- function(card = teal.reporter::ReportCard$new(), comment) {
+ card$set_name("Table Module")
+ card$append_text(paste("Selected dataset", input$dataname), "header2")
+ card$append_text("Selected Filters", "header3")
+ if (with_filter) {
+ card$append_text(filter_panel_api$get_filter_state(), "verbatim")
+ }
+ card$append_text("Encoding", "header3")
+ card$append_text(
+ yaml::as.yaml(
+ stats::setNames(
+ lapply(c("dataname", "nrow"), function(x) input[[x]]), c("dataname", "nrow")
+ )
+ ),
+ "verbatim"
+ )
+ card$append_text("Module Table", "header3")
+ card$append_table(table_q()[["result"]])
+ card$append_text("Show R Code", "header3")
+ card$append_text(teal.code::get_code(table_q()), "verbatim")
+ if (!comment == "") {
+ card$append_text("Comment", "header3")
+ card$append_text(comment)
+ }
+ card
+ }
+ teal.reporter::add_card_button_srv(
+ "addReportCard",
+ reporter = reporter,
+ card_fun = card_fun
+ )
+ teal.reporter::download_report_button_srv("downloadButton", reporter = reporter)
+ teal.reporter::reset_report_button_srv("resetButton", reporter)
+ ###
+ })
+ },
+ ui = function(id) {
+ ns <- NS(id)
+
+ sidebarLayout(
+ sidebarPanel(selectInput(ns("dataname"), "Choose a dataset", choices = NULL)),
+ mainPanel(
+ teal.reporter::simple_reporter_ui(ns("reporter")),
+ verbatimTextOutput(ns("dataset"))
+ )
+ )
+
+ sidebarLayout(
+ sidebarPanel(
+ div(
+ teal.reporter::add_card_button_ui(ns("addReportCard")),
+ teal.reporter::download_report_button_ui(ns("downloadButton")),
+ teal.reporter::reset_report_button_ui(ns("resetButton"))
+ ),
+ selectInput(ns("dataname"), "Choose a dataset", choices = NULL),
+ sliderInput(ns("nrow"), "Number of rows", min = 1, max = 1, value = 1, step = 1)
+ ),
+ mainPanel(tableOutput(ns("table")))
+ )
+ }
+ )
+}
+
+app <- init(
+ data = teal_data(AIR = airquality, IRIS = iris),
+ modules = list(
+ example_reporter_module(label = "with Reporter"),
+ example_module(label = "without Reporter")
+ ),
+ filter = teal_slices(teal_slice(dataname = "AIR", varname = "Temp", selected = c(72, 85))),
+ header = "Example teal app with reporter"
+)
+
+if (interactive()) shinyApp(app$ui, app$server)
There are two main actors in the teal
development:
teal
App Developerteal
Module Developerteal
app developer
++%% This is a mermaid diagram, if you see this the plot failed to render. Sorry. +graph LR + A[teal App Developer]--utilizes--> B[teal modules] + B--to create--> C[teal app] + D[app user]--uses--> C +style A fill:lightblue +style C fill:gold +style D fill:pink ++
The primary responsibility of a teal
app developer is to
+leverage the available building blocks of the teal
+framework to create a functional teal
app that analyses the
+data.
To expedite the app creation process, developers can take advantage
+of pre-existing teal
modules found in R
+packages like teal.modules.general
and
+teal.modules.clinical
.
These modules are designed with a focus on standardization and +versatility, making them suitable for a wide range of use cases.
+When developing a teal
app, the developer will select
+the most appropriate teal
modules and integrate them into
+the app’s interface to ensure seamless usability for end-users.
To learn more about the existing modules, visit teal.gallery
,
+which contains several demo applications and their source code.
teal
module developer
++%% This is a mermaid diagram, if you see this the plot failed to render. Sorry. +graph LR + A[Teal App Developer]--utilizes--> B[teal modules] + E[Teal Module Developer]--develops--> B + B--to create--> C[teal app] + D[app user]--uses--> C +style A fill:lightblue +style E fill:lightgreen +style D fill:pink +style C fill:gold ++
The main duty of a teal
module developer is to construct
+a compatible teal
module that can be utilized within the
+teal
framework.
Several factors influence the scope and requirements for building a
+teal
module.
When creating a reusable teal
module, it’s advisable to
+focus on making it as general and adaptable as feasible to maximize the
+possibilities of being re-used in the future. However, developers have
+the freedom to create a teal
module that is customized to
+suit the specific demands of a project.
Ultimately, one or more teal
modules are employed to
+construct a teal
app.
To learn more about creating custom modules follow the Tutorial on Creating a Custom +Module.
++%% This is a mermaid diagram, if you see this the plot failed to render. Sorry. +%%| fig-width: 7.5 +graph LR + subgraph Study B + A2[Study Teal App Developer]--utilizes--> B2[teal modules] + E2[Study Teal Module Developer]-.develops.-> B2 + B2--to create--> C2[study teal app] + D2[study app user]--uses--> C2 + end + E3[Teal Module Developer]--develops--> B + E3--develops-->B2 + subgraph Study A + A[Study Teal App Developer]--utilizes--> B[teal modules] + E[Study Teal Module Developer]-.develops.-> B + B--to create--> C[study teal app] + D[study app user]--uses--> C + end +style A fill:lightblue +style A2 fill:lightblue +style E fill:limegreen +style E2 fill:limegreen +style E3 fill:lightgreen +style D fill:pink +style D2 fill:pink +style C fill:gold +style C2 fill:gold ++
In a clinical trial study setting, a unique study
+teal
app developer is assigned to each study team
+and is accountable for developing a tailored teal
app for
+their respective study.
The study teal
app developer will
+initially leverage existing teal
modules from
+R
packages created by teal
module
+developers.
In cases where there is a need to create new modules tailored to the
+study, a study teal
module developer will
+need to be involved.
Upon completion, each study team will have their own designated
+teal
app tailored to their specific study.
+%% This is a mermaid diagram, if you see this the plot failed to render. Sorry. +sequenceDiagram; + autonumber + participant data + participant filters + participant filtered data + participant teal module + data->filters: gets + filters->>filtered data: becomes + filtered data->>teal module: sent to ++
The sequence diagram above illustrates the different stages that data
+goes through within the teal
framework, supported by the
+teal.slice
package:
teal
app;
+teal_data
before being
+passed to the app;teal_data
class
+facilitates reproducibility;teal
modules for analysis;
+teal_data
object so analysis
+code applied to the data is tracked (and can be used to reproduce
+the whole analysis);Whenever filters are added or removed, the data coming into modules
+is re-computed, providing the teal
module with new filtered
+data to conduct the required analysis.
vignettes/blueprint/filter_panel.Rmd
+ filter_panel.Rmd
The teal.slice
package provides teal
+applications with the filter panel, a powerful tool for
+exploring and analyzing data, and a key component of the
+teal
framework.
One of the great things about the filter panel is that it comes
+built-in with teal
, requiring no programming knowledge to
+use.
The filter panel provides a convenient way for users to subset their +data, simplifying the process of exploration and comprehension. +Moreover, users can activate or deactivate filter states interactively, +either individually or globally, using the filter panel.
++%% This is a mermaid diagram, if you see this the plot failed to render. Sorry. +sequenceDiagram + autonumber + data->teal.slice: processed by + teal.slice->>shiny inputs: creates + Note over teal.slice,shiny inputs: based on data type + shiny inputs->>reactive dataset: updates + reactive dataset->>teal modules: processed by ++
The filter panel creates subsets of data. Subsetting is achieved by
+creating filter states, each of which holds a logical predicate (filter
+condition) that is applied to single variables. These filter conditions
+are composed into a single expression, a call to a particular function
+(e.g. dplyr::filter
), and that expression is evaluated to
+create a filtered data subset.
The process is entirely interactive. Filter states can be created, +removed, and changed at will, however, the app developer may choose to +constrain or even restrict them.
+When a filter state is created, the filter panel generates a
+filter card with shiny
inputs appropriate to the
+type of the variable, e.g. range selectors for numeric columns and sets
+of checkboxes or drop-down menus for categorical ones.
As users interact with the filter cards, the subsetting complete +expression is updated and filtered data is recomputed.
+The filtered data is passed to teal
modules for
+downstream analysis. The subsetting expression is returned along with
+the data, ensuring an unbroken track record of the entire analysis.
+Signature of the data are also stored to ensure reproducible
+results.
Typically the data that is passed into a teal
+application is available before the app is run. However, this is not
+always true and in some cases the data will be built only after the app
+has started. A good example is pulling the data from an external
+repository, like a database, or uploading a file. Additional
+authentication may be required.
teal_data_module
+Preprocessing actions can be performed in-app using the
+teal_data_module
. Rather than passing a
+teal_data
object to the app, one may pass a
+shiny
module that returns a
+teal_data
object (wrapped in a reactive expression). This
+allows the app developer to include user actions data creation,
+fetching, and even pre-filtering modification.
A complete explanation of using the teal_data_module
can
+be found in this
+teal
vignette
vignettes/blueprint/index.Rmd
+ index.Rmd
Welcome to teal
Technical Blueprint documentation!
The purpose of this material is to aid new developer’s comprehension
+of the fundamental principles of the teal
framework. We
+will explore crucial teal
concepts such as data flow,
+actors, and filter panel, among others.
While this material will be presented at a high-level, we will direct +you to our vignettes for a more in-depth understanding.
+Our hope is that this resource will provide new developers with a
+strong grasp of teal
products, enabling them to contribute
+to code with confidence.
Reproducibility is paramount in the pharmaceutical industry. Accurate +and consistent results are essential to ensure high-quality research and +the safety of patients. By prioritizing reproducibility, researchers can +validate their methods, confirm their findings, and contribute to the +advancement of the field.
+The teal.code
package provides the qenv
+class that facilitates code reproducibility. Code is passed to a
+qenv
object, where is evaluated in a specific environment.
+qenv
also stores the code so that it can be retrieved on
+request.
The teal_data
class, which serves as the primary data
+interface for teal
applications, inherits this code
+tracking behavior from qenv
.
teal
application
+All teal
applications run on data provided in a
+teal_data
object. Data objects are stored and modified
+within the environment of the teal_data
object and all
+R
code used is tracked, which allows for the code to be
+evaluated and executed in the teal
application, and
+reproduced outside the teal
application. This includes data
+loading, preprocessing, filtering, transformations, and plotting,
+etc.
The teal_data
object makes it easy for users to
+reproduce and validate the results of their analyses.
Learn more about the use of teal_data
in the teal.data
+package vignettes.
Show R Code
and Reporter
+In both the teal.modules.clinical
and
+teal.modules.general
packages, you’ll find that most
+modules include a convenient Show R Code
button. When this
+button is clicked, a modal window appears, revealing the R
+code responsible for generating the module’s output. This functionality
+is achieved by inspecting the teal_data
object and
+retrieving code from it. With the Show R Code
button, users
+can easily copy and independently run the code to reproduce the analysis
+presented in the teal module.
The Reporter feature also leverages the teal_data
object
+in its operation. Much like the Show R Code
mechanism, the
+code displayed in a Reporter Card is extracted from the
+teal_data
object.
To learn more about the Reporter
feature, please visit
+the teal.reporter
+documentation.
Overall, qenv
from teal.code
and its child
+class, teal_data
, are powerful tools for ensuring code
+reproducibility and promoting high-quality research in the
+R
programming language.
teal
is an interactive data exploration framework based
+on Shiny, designed primarily to analyze CDISC clinical trial data.
A Shiny Application created with teal
offers users the
+ability to:
Moreover, the teal
framework provides application
+developers with a wide range of customizable standard analysis modules
+to integrate into their applications, along with a logging framework
+that facilitates debugging. Additionally, advanced users of the
+framework can develop new analysis modules and easily integrate them
+into any teal
application.
The teal
framework’s functionality draws heavily from
+the following packages:
R package | +Description | +
---|---|
teal |
+
+shiny -based interactive exploration
+framework for analyzing data |
+
teal.widgets |
+
+shiny UI components used within
+teal
+ |
+
teal.data |
+provides the data structure used in all
+teal applications |
+
teal.slice |
+provides the filter panel to allow dynamic filtering of +data | +
teal.code |
+provides a mechanism for tracking code to reproduce an +analysis | +
teal.logger |
+standardizes logging within teal
+framework |
+
teal.reporter |
+allows teal applications to generate
+reports |
+
Although these packages are mentioned in the material, we strongly +recommend visiting their vignettes to learn more about them.
+Learn on how to make your first teal
application here!
vignettes/blueprint/module_encapsulation.Rmd
+ module_encapsulation.Rmd
The teal
framework leverages the shiny
+module concept to enable encapsulation of analytical actions in
+teal
modules, while maintaining seamless communication
+between the modules and the application.
By implementing the modular app technique from the shiny module into
+the creation of the teal
module, several benefits are
+realized:
Streamlined maintenance
+The development of the teal
module becomes more manageable,
+as it can function independently from the teal
framework.
+This separation allows developers to maintain the module with ease. This
+approach has been successfully applied in R
packages
+dedicated to teal
module development, such as
+teal.modules.general
and
+teal.modules.clinical
.
Enhanced focus on outputteal
module developers can concentrate solely on refining
+parameters or encoding, and output aspects (such as data summarization
+and visualization) without the need to concern themselves with the
+intricacies of the teal
framework. When developed
+correctly, the module seamlessly integrates with
+teal
.
Facilitated collaborationteal
module development becomes an accessible entry point
+for developers interested in collaborating. This approach encourages
+user collaboration for the improvement of teal
modules, as
+developers gain a deeper understanding of the mechanics of the
+teal
framework.
+%% This is a mermaid diagram, if you see this the plot failed to render. Sorry. +flowchart RL +teal +subgraph features + direction LR + teal.data + teal.slice + teal.code + teal.logger + teal.widgets +end +subgraph modules + direction RL + teal.modules.general + teal.modules.clinical + teal.osprey + teal.goshawk + teal.modules.hermes +end +teal--has-->features +features--builds-->modules +modules--creates-->teal +subgraph modules + teal.modules.general + teal.modules.clinical + teal.osprey + teal.goshawk + teal.modules.hermes +end +subgraph calculations + direction RL + tern + osprey + goshawk + hermes +end +tern--supports-->teal.modules.clinical +osprey--supports-->teal.osprey +goshawk--supports-->teal.goshawk +hermes--supports-->teal.modules.hermes +style teal fill:lightblue +style features fill:lightgreen +style modules fill:pink ++
teal
is a modular framework that relies on a suite of
+related packages, as illustrated in the above diagram, to provide a wide
+range of functionalities.
teal
’s primary function is to create web app for
+analyzing clinical trial data but it has a multitude of
+features distributed across various packages.
Developers can selectively leverage these packages, such as
+teal.widgets
, teal.code
, and
+teal.logger
, to build teal
+modules for a teal
app. This approach gives the developers
+the tools that speed up their work and avoid re-implementing existing
+logic and UI elements.
The teal
modules utilize various packages such as
+tern
, osprey
, and goshawk
to
+perform calculations and analyses. These packages provide
+support to the teal
modules by performing
+all computations while the modules only have to focus on how to wrap the
+input options and the output.
Once developed, new and existing modules can be integrated into
+teal
to create a functional
+teal
app.
vignettes/bootstrap-themes-in-teal.Rmd
+ bootstrap-themes-in-teal.Rmd
We offer an easy application of a custom Bootstrap theme in a
+teal
app. teal
uses the bslib
+R
package which provides tools for customizing Bootstrap
+themes, including those of shiny
apps.
teal
app developers can specify custom Bootstrap themes
+by setting the teal.bs_theme
R
option, which
+has to be set to bslib::bs_theme
object. The
+bslib::bs_theme(...)
function creates a Bootstrap theme
+object, where one specifies the (major) Bootstrap version (default or
+one of 3, 4, or 5). Optionally one can choose a
+bootswatch
theme and customize the
+app CSS with functions like bslib::bs_add_rules
.
+Please read more about custom themes in the
+bslib
getting started vignette. The
+teal.bs_theme
R
option has to be specified at
+the top of the code script.
Please install bslib
package before you run the code
+below.
teal.bs_theme
R
option
+options("teal.bs_theme" = bslib::bs_theme("Custom Options"))
+
+#######################
+# teal::init() app code
+#######################
+The best and recommended ways to explore the
+Bootstrap themes are to use
+bslib::run_with_themer(shinyApp(app$ui, app$server))
or
+bslib::bs_theme_preview()
, both of which offer an
+interactive explore mode (not supported for Bootstrap 3). The
+bslib::bs_theme_preview()
is recommended when the end user
+does not have any shiny
app yet. When you already have a
+shiny
app and you want to test different Bootstrap themes
+(and CSS
styling) then
+bslib::run_with_themer(shinyApp(app$ui, app$server))
is
+recommended.
Available Bootstrap versions could be checked with
+bslib::versions()
and Bootstrap themes
+(bootswatch
) with
+bslib::bootswatch_themes(version = "5")
.
# bslib::versions()
+# bslib::bootswatch_themes(version = "5")
+options("teal.bs_theme" = bslib::bs_theme(version = "5", bootswatch = "lux")
+# or
+options("teal.bs_theme" = bslib::bs_theme_update(bslib::bs_theme(version = "5"), bootswatch = "lux"))
+When using the default bslib
theme for any version (3, 4
+or 5), its styling might not be as expected. Please run the interactive
+themer (recommended) or apply a custom theme to explore all the theme
+options. In many scenarios updating only the theme might not be
+enough and e.g. font color and other specifications should be updated
+too.
# instead of
+options("teal.bs_theme" = bslib::bs_theme(version = "5"))
+# try non-default themes
+options("teal.bs_theme" = bslib::bs_theme(version = "5", bootswatch = "THEME NAME". ...))
+# or run the app inside bslib::run_with_themer
+Please use the options("teal.bs_theme" = NULL)
call to
+return to the default shiny
Bootstrap for teal
+apps.
One reason the theme is not updated could be that the web browser
+caches the previous one, especially when different themes are run one
+after another. Please, use the Cmd+Shift+R
(Mac) or
+Ctrl+F5
(Windows) to hard refresh the webpage.
teal
CSS
+The most important HTML tags in teal
have a specific id
+or class, so they can be directly styled. The
+bslib::bs_add_rules
function could be used around the
+bslib::bs_theme
object to apply custom CSS
+rules.
library(magrittr)
+options("teal.bs_theme" = bslib::bs_theme(version = "5") %>% bslib::bs_add_rules("Anything understood by sass::as_sass()"))
+Other bslib::bs_add_*
family functions could be used to
+specify low-level Bootstrap elements.
It is important to note that the statements
+options("teal.bs_theme" = NULL)
and
+options("teal.bs_theme" = bslib::bs_theme(version = "3")
+are not equivalent as the bslib
approximation of the
+default shiny
theme for Bootstrap version 3 can introduce
+some discrepancies. One important difference is that when using
+bslib::bs_theme(version = "3", bootswatch = "THEME NAME")
+one can apply the custom Bootstrap theme. Another one is that the usage
+of bslib::bs_theme(version = "3")
requires the installation
+of the latest shinyWidgets
package from the main branch,
+see below.
# Downloading the newest shinyWidgets
+# needed only when bslib::bs_theme(version = "3", ...) is used
+remotes::install_github("https://github.com/dreamRs/shinyWidgets@main")
+shiny::fluidPage
+If you want to update the theme in a regular
+shiny::fluidPage
-like app, you do not need the
+teal.bs_theme
option. Simply provide the
+bslib::bs_theme
directly:
+shiny::fluidPage(theme = bslib::bs_theme(...), ...)
.
In this section we provide a step-by-step guide to customizing a
+teal
application theme interactively with
+bslib::run_with_themer()
. We recommend starting with a
+simple case and once you are satisfied, verifying with your full
+application. To that end we will use the teal
application
+below. For this example we assume that we want to use Bootstrap 5. To
+start, we launch the app with
+bslib::run_with_themer(app$ui, app$server)
instead of
+shiny::shinyApp
.
+options("teal.bs_theme" = bslib::bs_theme(version = "5"))
+
+library(teal)
+
+app <- init(
+ data = teal_data(IRIS = iris), # nolint
+ filter = teal_slices(teal_slice("IRIS", "Sepal.Length", selected = c(5, 7))),
+ modules = modules(example_module(), example_module()),
+ header = "My first teal application"
+)
+
+bslib::run_with_themer(shinyApp(app$ui, app$server))
This gives us the following.
+ +Note the Theme Customizer
section on the right hand
+side. This was added by bslib
and is how we customize our
+theme.
Instead of starting from scratch, we want to start with a Bootswatch
theme. Let us
+select the Minty theme in the “Overall theme” drop-down.
bslib
has updated our CSS
styles to use our
+new theme, including the customizer
theme. Additionally, if
+we look at our R
console, we will see
+#### Update your bs_theme() R code with: #####
+bs_theme_update(theme, bootswatch = "minty")
This is a helpful guide that provides code to update our theme. For
+teal
applications we don’t actually use
+bs_theme_update
and opt for bs_theme
instead.
+However, the printed code will still be helpful.
bootswatch
theme
+Our base theme (Minty) is close to what we want but let’s make a few
+modifications. To start, we will increase the base font size. To do
+this, we choose the “Fonts” section of the customizer
theme
+and then set a value in the “Base font size” input. We use 1.25 here,
+which means all our fonts will be increased by a factor of 1.25. If we
+check the R
console, we will see bslib
has
+printed
+bs_theme_update(theme, font_scale = 1.25, bootswatch = "minty")
,
+which now includes our font size adjustment.
Finally, suppose we do not want borders to be rounded. In our
+customizer
theme, we can go to “Options” and then uncheck
+the “Rounded corners” box.
As expected, our corners are no longer rounded. If we look at our
+R
console, we will now see
+bs_theme_update(theme, font_scale = 1.25,
enable-rounded= FALSE, bootswatch = "minty")
.
Once our customization is complete, we will apply the changes to our
+application. To do this, we use the option teal.bs_theme
+like before but this time we will expand on our
+bslib::bs_theme
call to include our changes. Luckily, the
+arguments that were printed to the R
console when running
+our app in the themer can be plugged right in.
+options(
+ "teal.bs_theme" = bslib::bs_theme(
+ version = "5",
+ font_scale = 1.25,
+ `enable-rounded` = FALSE,
+ bootswatch = "minty"
+ )
+)
+
+library(teal)
+
+app <- init(
+ data = teal_data(IRIS = iris),
+ filter = teal_slices(teal_slice("IRIS", "Sepal.Length", selected = c(5, 7))),
+ modules = modules(example_module(), example_module())
+)
+
+shinyApp(app$ui, app$server)
Now the application has our custom theme applied.
+ +Please note the interactive themer only contains the most commonly
+applied options. For more customization options, review the
+bslib
documentation.
vignettes/creating-custom-modules.Rmd
+ creating-custom-modules.Rmd
The teal
framework provides a large catalog of
+plug-in-ready analysis modules to be incorporated into teal
+applications. However, it is also possible to create your own modules
+using the module
function.
This function contains the UI required for the module. It should be a
+function with at least the argument id
. See the server
+section below for more details.
This function contains the shiny
server logic for the
+module and should be of the form:
+function(
+ id,
+ data, # optional; use if module needs access to application data
+ filter_panel_api, # optional; use if module needs access to filter panel; see teal.slice
+ reporter, # optional; use if module supports reporting; see reporting vignette
+ ...) {
+ moduleServer(id, function(input, output, session) {
+ # module code here
+ })
+}
The data that arrives in the module is a teal_data
+object, the data container used throughout the teal
+application. teal_data
is passed to the init
+function when building the application and, after filtering by the
+filter panel, it is passed to modules, wrapped in a reactive expression.
+The teal_data
class allows modules to track the
+R
code that they execute so that module outputs can be
+reproduced. See the teal.data
package for a detailed
+explanation.
Here is a minimal module that allows the user to select and view one
+dataset at a time. By default, filtering is enabled for all datasets.
+Note that dataset choices are specified by the datanames
+property of the teal_data
container.
+library(teal)
+
+example_module <- function(label = "example teal module") {
+ checkmate::assert_string(label)
+
+ module(
+ label = label,
+ server = function(id, data) {
+ checkmate::assert_class(data, "reactive")
+ checkmate::assert_class(isolate(data()), "teal_data")
+
+ moduleServer(id, function(input, output, session) {
+ updateSelectInput(session, "dataname", choices = isolate(datanames(data())))
+ output$dataset <- renderPrint({
+ req(input$dataname)
+ data()[[input$dataname]]
+ })
+ })
+ },
+ ui = function(id) {
+ ns <- NS(id)
+ sidebarLayout(
+ sidebarPanel(selectInput(ns("dataname"), "Choose a dataset", choices = NULL)),
+ mainPanel(verbatimTextOutput(ns("dataset")))
+ )
+ }
+ )
+}
The example below allows the user to interact with the data to create +a simple visualization. In addition, it prints the code that can be used +to reproduce that visualization.
+
+library(teal)
+
+# ui function for the module
+# allows for selecting dataset and one of its numeric variables
+ui_histogram_example <- function(id) {
+ ns <- NS(id)
+ sidebarLayout(
+ sidebarPanel(
+ selectInput(ns("datasets"), "select dataset", choices = NULL),
+ selectInput(ns("numerics"), "select numeric variable", choices = NULL)
+ ),
+ mainPanel(
+ plotOutput(ns("plot")),
+ verbatimTextOutput(ns("code"))
+ ),
+ )
+}
+
+# server function for the module
+# presents datasets and numeric variables for selection
+# displays a histogram of the selected variable
+# displays code to reproduce the histogram
+srv_histogram_example <- function(id, data) {
+ checkmate::assert_class(data, "reactive")
+ checkmate::assert_class(isolate(data()), "teal_data")
+
+ moduleServer(id, function(input, output, session) {
+ # update dataset and variable choices
+ # each selection stored in separate reactive expression
+ updateSelectInput(inputId = "datasets", choices = isolate(datanames(data())))
+ observe({
+ req(dataset())
+ nums <- vapply(data()[[dataset()]], is.numeric, logical(1L))
+ updateSelectInput(inputId = "numerics", choices = names(nums[nums]))
+ })
+ dataset <- reactive(input$datasets)
+ selected <- reactive(input$numerics)
+
+ # add plot code
+ plot_code_q <- reactive({
+ validate(need(length(dataset()) == 1L, "Please select a dataset"))
+ validate(need(length(selected()) == 1L, "Please select a variable"))
+ req(selected() %in% names(data()[[dataset()]]))
+
+ # evaluate plotting expression within data
+ # inject input values into plotting expression
+ within(
+ data(),
+ p <- hist(dataset[, selected], las = 1),
+ dataset = as.name(dataset()), selected = selected()
+ )
+ })
+
+ # view plot
+ output$plot <- renderPlot({
+ plot_code_q()[["p"]]
+ })
+
+ # view code
+ output$code <- renderPrint({
+ plot_code_q() %>%
+ get_code() %>%
+ cat()
+ })
+ })
+}
+
+# function that creates module instance to use in `teal` app
+tm_histogram_example <- function(label) {
+ module(
+ label = label,
+ server = srv_histogram_example,
+ ui = ui_histogram_example,
+ datanames = "all"
+ )
+}
This module is ready to be used in a teal
app.
+app <- init(
+ data = teal_data(IRIS = iris, NPK = npk),
+ modules = tm_histogram_example(label = "Histogram Module"),
+ header = "Simple app with custom histogram module"
+)
+
+if (interactive()) {
+ shinyApp(app$ui, app$server)
+}
Refer to this
+vignette to read about adding support for reporting in your
+teal
module.
The teal.widgets
+package provides various widgets which can be leveraged to quickly
+create standard elements in your custom module.
vignettes/data-as-shiny-module.Rmd
+ data-as-shiny-module.Rmd
Proper functioning of any teal
application requires the
+presence of a teal_data
object. Typically, a
+teal_data
object created in the global environment will be
+passed to the data
argument in init
. This
+teal_data
object should contain all elements necessary for
+successful execution of the application’s modules.
In some scenarios, however, application developers may opt to
+postpone some data operations until the application runtime. This can be
+done by passing a special shiny
module to the
+data
argument. The teal_data_module
function
+is used to build such a module from the following components:
id
; defines
+user interface elements for the data moduleid
;
+defines server logic for the data module, including data creation; must
+return a reactive expression containing a teal_data
+objectteal
will run this module when the application starts
+and the resulting teal_data
object that will be used
+throughout all teal
(analytic) modules.
One case for postponing data operations is datasets that are dynamic,
+frequently updated. Such data cannot be created once and kept in the
+global environment. Using teal_data_module
enables creating
+a dataset from scratch every time the user starts the application.
+data_module <- teal_data_module(
+ ui = function(id) div(),
+ server = function(id) {
+ moduleServer(id, function(input, output, session) {
+ reactive({
+ data <- within(
+ teal_data(),
+ {
+ dataset1 <- iris
+ dataset2 <- mtcars
+ }
+ )
+ datanames(data) <- c("dataset1", "dataset2") # optional
+ data
+ })
+ })
+ }
+)
+
+
+app <- init(
+ data = data_module,
+ modules = example_module()
+)
+
+if (interactive()) {
+ shinyApp(app$ui, app$server)
+}
See ?qenv
for a detailed explanation of how to use
+the within
method.
Another reason to postpone data operations is to involve the +application user in the preprocessing stage. An initial, constant form +of the data can be created in the global environment and then modified +once the app starts.
+The following example illustrates how teal_data_module
+can be utilized to subset data based on the user inputs:
+data <- within(teal_data(), {
+ dataset1 <- iris
+ dataset2 <- mtcars
+})
+datanames(data) <- c("dataset1", "dataset2")
+
+data_module <- teal_data_module(
+ ui = function(id) {
+ ns <- NS(id)
+ div(
+ selectInput(ns("species"), "Select species to keep",
+ choices = unique(iris$Species), multiple = TRUE
+ ),
+ actionButton(ns("submit"), "Submit")
+ )
+ },
+ server = function(id) {
+ moduleServer(id, function(input, output, session) {
+ eventReactive(input$submit, {
+ data_modified <- within(
+ data,
+ dataset1 <- subset(dataset1, Species %in% selected),
+ selected = input$species
+ )
+ data_modified
+ })
+ })
+ }
+)
+
+app <- init(
+ data = data_module,
+ modules = example_module()
+)
+
+if (interactive()) {
+ shinyApp(app$ui, app$server)
+}
Note that running preprocessing code in a module as opposed to the +global environment will increase app loading times. It is recommended to +keep the constant code in the global environment and to move only the +dynamic parts to a data module.
+When using teal_data_module
to modify a pre-existing
+teal_data
object, it is crucial that the server function
+and the data object are defined in the same environment, otherwise the
+server function will not be able to access the data object. This means
+server functions defined in packages cannot be used.
teal_data_modules
+The server logic of a teal_data_module
can be modified
+before it is used in an app, using the within
function.
+This allows the teal_data
object that is created in the
+teal_data_module
to be processed further.
In the previous example, data_module
takes a predefined
+teal_data
object and allows the app user to select a
+subset. The following example modifies data_module
so that
+new columns are added once the data is retrieved.
+data_module_2 <- within(
+ data_module,
+ {
+ # Create new column with Ratio of Sepal.Width and Petal.Width
+ dataset1$Ratio.Sepal.Petal.Width <- round(dataset1$Sepal.Width / dataset1$Petal.Width, digits = 2L)
+ # Create new column that converts Miles per Galon to Liter per 100 Km
+ dataset2$lp100km <- round(dataset2$mpg * 0.42514371, digits = 2L)
+ }
+)
+
+app <- init(
+ data = data_module_2,
+ modules = example_module()
+)
+
+if (interactive()) {
+ shinyApp(app$ui, app$server)
+}
teal
apps with the filter panel
+The filter panel is an integral part of all teal
+applications and is included on the right side. Based on the selections
+made in the filter panel, filter expressions are executed before passing
+data to teal
modules. The technical details of the filter
+panel are extensively described in teal.slice
+documentation.
By default, init
initializes the filter panel without
+any active filters but allows the user to add filters on any column. To
+start a teal
application with predefined filters, one must
+specify the filter
argument. In the following example four
+filters are specified using the teal_slice
function and
+wrapped together with teal_slices
.
+library(teal)
+
+app <- init(
+ data = teal_data(IRIS = iris, CARS = mtcars),
+ modules = example_module(),
+ filter = teal_slices(
+ teal_slice(dataname = "IRIS", varname = "Sepal.Length"),
+ teal_slice(dataname = "IRIS", varname = "Species", selected = "setosa"),
+ teal_slice(dataname = "CARS", varname = "mpg", selected = c(20, Inf)),
+ teal_slice(dataname = "CARS", expr = "qsec < 20", title = "1/4 mile under 20 sec", id = "qsec_20")
+ )
+)
+
+if (interactive()) {
+ shinyApp(app$ui, app$server)
+}
teal.slice
+teal_module
+Each teal_module
(see ?module
) object
+contains the datanames
attribute that determines which data
+sets are to be sent to that module. The filter panel will display only
+those data sets and hide the rest when this module is active.
+library(teal)
+
+app <- init(
+ data = teal_data(IRIS = iris, CARS = mtcars),
+ modules = modules(
+ example_module(label = "all datasets"),
+ example_module(label = "IRIS only", datanames = "IRIS"),
+ example_module(label = "CARS only", datanames = "CARS"),
+ example_module(label = "no filter panel", datanames = NULL)
+ )
+)
+if (interactive()) {
+ shinyApp(app$ui, app$server)
+}
teal
contains the teal_slices
function that
+extends the original teal_slices
found in
+teal.slice
by adding two arguments:
+module_specific
and mapping
. By default
+init
initializes the app with a “global” filter panel,
+where all modules use the same filters. Setting
+module_specific = TRUE
switches to a “module-specific”
+filter panel, where each module can have a different set of filters
+active at any time. It is still possible to set global filters that will
+be shared among modules.
One possible scenario is depicted in the figure below:
+filter 1
is shared by all modulesfilter 2
is shared by module 1
and
+module 3
filter 3
is used only by module 2
+filter 4
is used only by module 1
+filter 5
and filter 6
are not active in
+any of the modulesTo achieve the described setup, one must set the
+module_specific
argument to TRUE
and use the
+mapping
argument to match filters to modules.
+mapping
takes a named list where element names correspond
+to module labels, and elements are vectors of teal_slice
+id
s applied to that module at startup.
+teal_slice
s listed the element called
+"global_filters"
will be applied to all modules.
For a detailed explanation about the filter states, see this
+teal.slice
vignette.
+library(teal)
+
+app <- init(
+ data = teal_data(mtcars = mtcars),
+ modules = modules(
+ example_module(label = "module 1"),
+ example_module(label = "module 2"),
+ example_module(label = "module 3"),
+ example_module(label = "module 4")
+ ),
+ filter = teal_slices(
+ # filters created with id
+ teal_slice(dataname = "mtcars", varname = "mpg", id = "filter 1"),
+ teal_slice(dataname = "mtcars", varname = "cyl", id = "filter 2"),
+ teal_slice(dataname = "mtcars", varname = "disp", id = "filter 3"),
+ teal_slice(dataname = "mtcars", varname = "hp", id = "filter 4"),
+ teal_slice(dataname = "mtcars", varname = "drat", id = "filter 5"),
+ teal_slice(dataname = "mtcars", varname = "wt", id = "filter 6"),
+ # module-specific filtering enabled
+ module_specific = TRUE,
+ # filters mapped to modules
+ mapping = list(
+ "module 1" = c("filter 2", "filter 4"),
+ "module 2" = "filter 3",
+ "module 3" = "filter 2",
+ global_filters = "filter 1"
+ )
+ )
+)
+
+if (interactive()) {
+ shinyApp(app$ui, app$server)
+}
vignettes/getting-started-with-teal.Rmd
+ getting-started-with-teal.Rmd
teal
is a shiny-based interactive exploration framework
+for analyzing data, with particular emphasis on CDISC clinical trial
+data. teal
applications allow their users to:
In addition, the teal
framework provides application
+developers with:
More advanced users of the framework can also create new analysis
+modules which can be added into any teal
applications.
teal
application:
+This simple teal
application takes the iris
+and mtcars
datasets and displays their contents:
+library(teal)
+
+app <- init(
+ data = teal_data(IRIS = iris, MTCARS = mtcars),
+ modules = modules(
+ example_module("Module 1"),
+ example_module("Module 2")
+ ),
+ filter = teal_slices(
+ teal_slice(dataname = "IRIS", varname = "Species", selected = "setosa")
+ ),
+ title = build_app_title(title = "My first teal app"),
+ header = h3("My first teal application"),
+ footer = div(a("Powered by teal", href = "https://insightsengineering.github.io/teal/latest-tag/"))
+)
+
+if (interactive()) {
+ shinyApp(app$ui, app$server)
+}
Hovering the image shows the teal
application
+generated by this code.
+
+Every teal
application is composed of the following
+elements, all of which can be controlled by the app developer by passing
+arguments to the init
function:
teal
modules.
+Species
variable in the iris
+dataset.The key function to use to create your teal
application
+is init
, which requires two mandatory arguments:
+data
and modules
. There are other optional
+arguments for init
, which can be used to customize the
+application. Please refer to the documentation for init
for
+further details.
The data
argument in the init
function
+specifies the data used in your application. All datasets which are
+about to be used in teal
application must be passed through
+teal_data
object. It is also possible to specify
+relationships between the datasets using the join_keys
+argument but in this case the datasets are not related. See this vignette for
+details. If data is not available and has to be pulled from a remote
+source, init
must receive a teal_data_module
+that specifies how to obtain the desired datasets and put them into a
+teal_data
object. See this vignette for details.
In order to use CDISC clinical trial data in a teal
+application the cdisc_data
function is used instead. Custom
+SDTM
standards can be handled with teal_data
+and join_keys
.
For further details, we recommend exploring the teal.data
+package documentation.
The modules
argument to init
consists of a
+list of teal
modules (which can be wrapped together using
+the function modules
). Core teal
developers
+have created several universal teal
modules that can be
+useful in any teal
application. To learn how to create your
+own modules, please explore Creating Custom Modules
+vignette. To use our predefined modules, see the references below
+for links to these modules.
The optional filter
argument in init
allows
+you to initialize the application with predefined filters. For further
+details see Filter Panel vignette .
If any of the modules
in your teal
+application support reporting (see teal.reporter
+for more details), users of your application can add the outputs of the
+modules to a report. This report can then be downloaded and a special
+Report Previewer module will be added to your application as an
+additional tab, where users can view and configure their reports before
+downloading them. See more details in this vignette.
teal
hands over data with reproducible code to every
+module included in the application. Note that teal
does not
+display the code, that is the modules’ responsibility. For example, the
+example_module
function used above shows the code in the
+main panel together with other outputs. For more details see this vignette.
To learn more about the teal
framework we recommend
+first exploring some of the available analysis modules.
For example see:
+MultiAssayExperiment
objectsFor a demo of teal
apps see:
The teal
framework relies on a set of supporting
+packages whose documentation provides more in-depth information. The
+packages which are of most interest when defining
+teal
applications are:
teal.data
:
+defining data for teal
application.teal.slice
:
+defining data filtering before passing into teal
+modules.vignettes/including-data-in-teal-applications.Rmd
+ including-data-in-teal-applications.Rmd
teal
Applications
+The teal
framework readily accepts general,
+non-relational data. Modules defined in the
+teal.modules.general
package are designed to work well with
+that kind of data. Relational data is handled just as well and the
+mechanism of passing data to applications is virtually the same. This
+includes clinical data that conforms to the ADaM
standard.
+We are working on making the framework extendable so that support for
+other data structures can be added with relative ease. Currently some
+support is offered for the MultiAssayExperiment
class.
All applications use the teal_data
class as a data
+container. teal_data
objects are passed to
+init
to build the application, where they are modified by
+the filter panel (if applicable) and passed on to modules. Thus, the
+first step of building a teal
app is creating a
+teal_data
object.
A teal_data
object is created by calling the
+teal_data
function and passing data objects as
+name:value
pairs.
Note that iris
and cars
have been added to
+the datanames
property of data
(see datanames
property).
This is sufficient to run a teal
app.
+# build app
+app <- init(
+ data = data,
+ modules = example_module()
+)
+
+# run app
+shinyApp(app$ui, app$server)
A teal_data
object stores data in a separate
+environment. Therefore, modifying the stored datasets requires that
+processing code be evaluated in that environment. Following that logic,
+one can create an empty teal_data
object and populate it by
+evaluating code. This can be done using the eval_code
+function or, more conveniently, using the within
+function.
+# create empty object
+data_empty <- teal_data()
+
+# run code in the object
+data_populated_1 <- eval_code(data_empty, code = "iris <- iris
+ cars <- mtcars")
+# alternative
+data_populated_2 <- within(data_empty, {
+ iris <- iris
+ cars <- mtcars
+})
The key difference between eval_code
and
+within
is that the former accepts code as character vector
+or language objects (calls and expressions), while within
+accepts only inline code. See ?qenv
for more
+details.
Note that in the first example data
was created by
+passing data objects, so the code that was used to create the data
+objects is unknown and therefore the process cannot be reproduced.
+Inspecting code the in the app created above reveals a note that the
+preprocessing code is absent.
The necessary code can be supplied to the code
argument
+of the teal_data
function.
+data_with_code <- teal_data(
+ iris = iris, cars = mtcars,
+ code = "iris <- iris
+ cars <- mtcars"
+)
Keep in mind this code is not executed in the
+teal_data
’s environment, so it may not reproduce the
+environment. Such an object is considered unverified (see verified
property).
If reproducibility is required, we recommend creating an empty
+teal_data
object and then evaluating code.
The one departure from passing a teal_data
object to
+init
is when the data does not exist in the environment
+where the app is run, e.g. when it has to be pulled from a
+remote source. In those cases a teal_data_module
must be
+used. See this vignette for a
+detailed description.
Currently teal
supports two specialized data
+formats.
ADaM
data
+The ADaM
data model, defined in CDISC standards,
+specifies relationships between the subject-level parent dataset and
+observation-level child datasets. The cdisc_data
function
+takes advantage of that fact to automatically set default joining keys
+(see join_keys
property). In the
+example below, two standard ADaM
datasets
+(ADSL
and ADTTE
) are passed to
+cdisc_data
.
+# create cdisc_data
+data_cdisc <- cdisc_data(ADSL = teal.data::rADSL, ADTTE = teal.data::rADSL)
+
+datanames(data_cdisc)
+#> [1] "ADSL" "ADTTE"
+join_keys(data_cdisc)
+#> A join_keys object containing foreign keys between 2 datasets:
+#> ADSL: [STUDYID, USUBJID]
+#> <-- ADTTE: [STUDYID, USUBJID]
+#> ADTTE: [STUDYID, USUBJID, PARAMCD]
+#> --> ADSL: [STUDYID, USUBJID]
+app <- init(
+ data = data_cdisc,
+ modules = example_module()
+)
+shinyApp(app$ui, app$server)
MultiAssayExperiment
data
+The MultiAssayExperiment
package offers a data structure
+for representing and analyzing multi-omics experiments that involve
+multi-modal, high-dimensionality data, such as DNA mutations, protein or
+RNA abundance, chromatin occupancy, etc., in the same biological
+specimens.
The MultiAssayExperiment
class is described in detail here.
MultiAssayExperiment
objects (MAEs) are placed in
+teal_data
just like normal objects.
+library(MultiAssayExperiment)
+utils::data(miniACC)
+
+data_mae <- teal_data(MAE = miniACC)
+
+app <- init(
+ data = data_mae,
+ modules = example_module()
+)
+shinyApp(app$ui, app$server)
Due to the unique structure of a MAE, teal
requires
+special considerations when building teal
modules.
+Therefore, we cannot guarantee that all modules will work properly with
+MAEs. The package teal.modules.hermes
+has been developed specifically with MAE in mind and will be more
+reliable.
The filter panel supports MAEs out of the box.
+teal_data
properties
+datanames
+The datanames
property lists the objects stored in the
+teal_data
environment that constitute datasets of interest.
+Objects passed to teal_data
become automatically listed in
+the datanames
property of the resulting object. Objects
+created in teal_data
by evaluating code need not be data
+objects of interest and as such they are not automatically added to
+datanames
. For convenience, an empty datanames
+property is considered to mean “all objects in the container”.
+datanames
can be read or modified with the
+datanames
function.
+data_with_objects <- teal_data(iris = iris, cars = mtcars)
+data_with_code <- teal_data() %>%
+ within({
+ iris <- iris
+ cars <- mtcars
+ not_a_dataset <- "data source credits"
+ })
+datanames(data_with_objects)
+#> [1] "iris" "cars"
+datanames(data_with_code)
+#> character(0)
+datanames(data_with_code) <- c("iris", "cars")
+datanames(data_with_code)
+#> [1] "iris" "cars"
The datanames
property serves as a communication bridge
+between the data container and modules in a teal
+application. In teal
all modules are called with a
+datanames
argument that determines which of the variables
+in the teal_data
object they are to access. Only variables
+enumerated in the datanames
property are eligible for use
+in modules.
Note that specifying datanames
in teal_data
+is optional; if the property is empty, all objects are considered
+eligible. Likewise, the datanames
argument in the module
+call defaults to "all"
, which means that module will
+attempt to access all eligible variables in the teal_data
+object.
For a detailed explanation of datanames
, see this
+teal.data
vignette.
join_keys
+Using relational data requires specifying joining keys for each pair
+of datasets. Primary keys are unique row identifiers in individual
+datasets and thus should be specified for each dataset. Foreign keys
+describe mapping of variables between datasets. Joining keys are stored
+in the join_keys
property, which can be set when creating a
+teal_data
object, using the join_keys
+argument, or using the join_keys
function.
+ds1 <- data.frame(
+ id = seq(1, 10),
+ group = rep(c("A", "B"), each = 5)
+)
+ds2 <- data.frame(
+ group = c("A", "B"),
+ condition = c("condition1", "condition2")
+)
+keys <- join_keys(
+ join_key("DS1", keys = "id"),
+ join_key("DS2", keys = "group"),
+ join_key("DS1", "DS2", keys = c("group" = "group"))
+)
+data_relational1 <- teal_data(DS1 = ds1, DS2 = ds2, join_keys = keys)
+data_relational2 <- teal_data(DS1 = ds1, DS2 = ds2)
+join_keys(data_relational2) <- keys
For a detailed explanation of join keys, see this
+teal.data
vignette.
verified
+teal_data
allows for tracking code from data creation
+through data filtering through data analysis so that the whole process
+can be reproduced. The verified
property designates whether
+or not reproducibility has been confirmed. teal_data
+objects that are created empty and only modified by evaluating code
+within them are considered verified by default. Those created with data
+objects alone or with data objects and code are not verified by default,
+but can become verified by running the verify
function.
+data_with_code
+#> ✅︎ verified teal_data object
+#> <environment: 0x557df7753d38> [L]
+#> Parent: <environment: package:teal>
+#> Bindings:
+#> • not_a_dataset: <chr> [L]
+#> • cars: <df[,11]> [L]
+#> • iris: <df[,5]> [L]
+
+data_with_objects_and_code <- teal_data(iris = iris, cars = mtcars, code = expression(iris <- iris, cars <- mtcars))
+data_with_objects_and_code
+#> ✖ unverified teal_data object
+#> <environment: 0x557df7bfa450> [L]
+#> Parent: <environment: package:teal>
+#> Bindings:
+#> • cars: <df[,11]> [L]
+#> • iris: <df[,5]> [L]
+
+data_with_objects_and_code_ver <- verify(data_with_objects_and_code)
+data_with_objects_and_code_ver
+#> ✅︎ verified teal_data object
+#> <environment: 0x557df7bfa450> [L]
+#> Parent: <environment: package:teal>
+#> Bindings:
+#> • cars: <df[,11]> [L]
+#> • iris: <df[,5]> [L]
For a detailed explanation of verification, see this
+teal.data
vignette.
For a complete guide to the teal_data
class, please
+refer to the teal.data
+package.
teal
teal
appsteal
The purpose of the blueprint is to aid new developer’s comprehension of the fundamental principles of the teal
framework. We will explore crucial teal
concepts such as data flow, actors, and filter panel, among others.
Features.
+ +vignettes/teal-options.Rmd
+ teal-options.Rmd
Some R
packages use options
to modify their
+runtime behavior. They usually specify sensible default values for
+internal function arguments or determine responses to users actions. For
+example, testthat
uses an option
+testthat.progress.max_fails
to define a default number of
+failed expectations before the testing functions terminate execution.
+While some of these adjustable values can be exposed as function
+parameters, some are confined to an option. This vignette details the
+options available in the package teal
and it’s supporting
+packages teal.logger
, teal.widgets
, and
+teal.slice
.
At any time during an interactive session, you can change an option +using:
+
+options(option_to_set = "value")
A way to change options for only the execution of a specific block of
+code is with the withr
package like so:
+withr::with_options(list(digits = 3), print(pi))
## [1] 3.14
+After the line above is run the option, digits
, will go
+back to its value before the line was run.
The function getOption
allows to inspect the value of an
+option:
+getOption("option_to_set")
## [1] "value"
+Once set, the value of an option persists during a session, but it
+returns to the default value in a new session. Make sure to change the
+options after all the teal
-related packages are loaded
+because some of them initialize the options themselves and will
+overwrite your custom values.
teal
application
+teal.bs_theme
(bslib::bs_theme
+object)
+This option controls the bootstrap theme and version used in
+teal
apps. Achieve better UX with the customized UI of an
+app. Please see the vignette on
+Bootstrap themes to read more about the functionality.
Default: NULL
teal.load_nest_code
(character
)
+The value of this option is appended to the top of the code rendered
+when using the Show R Code
modal button.
Default:
+"# Add any code to install/load your NEST environment here"
.
teal.threshold_slider_vs_checkboxgroup
+(numeric
)
+This is the threshold that determines if a variable is treated as a
+factor in the filter panel. If the number of unique values of a variable
+is less than this threshold the variable will be treated as a factor
+instead of its original class. As an example, imagine
+teal.threshold_slider_vs_checkboxgroup
equals to 2. Then a
+numeric variable c(1, 1, 1)
, which has only one unique
+value, is treated as a factor in the filter panel (and in the filter
+panel only!). The filter panel creates a checkbox widget to filter
+values from this variable, as it would for a factor variable, instead of
+the usual numeric range selector.
Default: 5.
+teal.basic_table_args
(basic_table_args
+object)
+This specifies the list of arguments passed to every call to
+rtables::basic_table
made in a teal
+application. This can be used to format rtables
without
+making any changes to the application code. See the documentation of
+teal.widgets::basic_table_args
for more information.
Default: teal.widgets::basic_table_args()
.
teal.ggplot2_args
(ggplot2_args
+object)
+This option allows modifying labels and themes of all
+ggplot2
plots in a teal
application. See the
+documentation of teal.widgets::ggplot2_args
for more
+information.
Default: teal.widgets::ggplot2_args()
.
teal.plot_dpi
(integer value 24 or larger)
+This option controls the dots per inch of the graphs rendered and
+downloaded when using the module plot_with_settings
from
+the teal.widgets
package.
Default: 72
+teal.log_layout
(character
)
+This defines the layout of a log message used in a teal
+application. teal
uses this layout to format the emitted
+log messages. Read the documentation of
+teal.logger::register_logger
for more information.
Default:
+"[{level}] {format(time, \"%Y-%m-%d %H:%M:%OS4\")} pid:{pid} token:[{token}] {ans} {msg}"
.
Note that this layout is formatted by the glue
+package.
teal.log_level
(character
)
+This is the logging level threshold used in a teal
+application. A teal
application will not emit logs below
+this level. Read the documentation of
+teal.logger::register_logger
for more information. Possible
+values: "TRACE"
, "INFO"
,
+"WARNING"
, "ERROR"
. See the documentation of
+logger::TRACE
for all possible values of logging threshold
+and more information on what it does.
Default: "INFO"
.
Note that there are two levels considered less severe than
+"INFO"
: "DEBUG"
and "TRACE"
. In
+order to see the log messages for these two levels as well, change the
+log level from the default to "TRACE"
, the least severe log
+level.