library(shiny)
library(dplyr)
library(stringr)
library(tibble)
library(teal)
library(teal.data)
library(teal.widgets)
library(ComplexHeatmap)
library(InteractiveComplexHeatmap)
library(SummarizedExperiment) #
BiocManager::install("SummarizedExperiment")
library(hermes) # devtools::install_github("insightsengineering/hermes")InteractiveComplexHeatmap teal Module with hermes Toy Data
This vignette reuses the teal module pattern from the proof-of-concept vignette with a toy SummarizedExperiment from hermes.
Prerequisites
teal Module
tm_interactive_heatmap_ui <- function(id) {
ns <- shiny::NS(id)
teal.widgets::standard_layout(
encoding = shiny::tagList(
shiny::selectInput(ns("dataset"), "Dataset", choices = NULL),
shiny::selectInput(ns("scale_by"), "Scale", choices = c("none", "row", "column")),
shiny::selectInput(ns("palette"), "Palette", choices = c("blue_white_red", "viridis")),
shiny::checkboxInput(ns("cluster_rows"), "Cluster rows", value = TRUE),
shiny::checkboxInput(ns("cluster_columns"), "Cluster columns", value = TRUE)
),
output = InteractiveComplexHeatmap::InteractiveComplexHeatmapOutput(ns("heatmap"))
)
}
tm_interactive_heatmap_server <- function(id, data) {
shiny::moduleServer(id, function(input, output, session) {
shiny::observe({
shiny::updateSelectInput(
session = session,
inputId = "dataset",
choices = names(data()),
selected = "ADAE"
)
})
selected_data <- shiny::reactive({
shiny::req(input$dataset)
data()[[input$dataset]]
})
heatmap <- shiny::reactive({
make_heatmap(
selected_data(),
scale_by = input$scale_by,
palette = input$palette,
cluster_rows = input$cluster_rows,
cluster_columns = input$cluster_columns
)
})
shiny::observe({
InteractiveComplexHeatmap::makeInteractiveComplexHeatmap(
input = input,
output = output,
session = session,
ht_list = heatmap(),
heatmap_id = session$ns("heatmap")
)
})
})
}
tm_interactive_heatmap <- function(label = "Interactive Heatmap", datanames = "all") {
teal::module(
label = label,
ui = tm_interactive_heatmap_ui,
server = tm_interactive_heatmap_server,
datanames = datanames
)
}toy Data 1
For the proof of concept, each dataset has an ID column and numeric assay-like columns. The module also accepts matrix objects directly.
set.seed(42)
make_demo_matrix <- function(n_rows = 20, n_cols = 8, prefix = "Gene") {
matrix(
rnorm(n_rows * n_cols),
nrow = n_rows,
dimnames = list(
paste0(prefix, seq_len(n_rows)),
paste0("Sample", seq_len(n_cols))
)
)
}
expression_matrix <- make_demo_matrix()
expression_df <- expression_matrix |>
as.data.frame(check.names = FALSE) |>
tibble::rownames_to_column("ID")
wide_clinical_df <- matrix(rnorm(12 * 6), nrow = 12) |>
as.data.frame() |>
dplyr::rename_with(~ stringr::str_replace(.x, "V", "Marker")) |>
tibble::rownames_to_column("ID") |>
dplyr::mutate(ID = paste0("Subject", ID))teal App Assembly 1
The plain Shiny module handles matrix input directly, while the teal app keeps to two data-frame datasets with an ID column.
teal_data_obj <- teal.data::teal_data(
expression_df = expression_df,
wide_clinical_df = wide_clinical_df
)
teal_app <- teal::init(
data = teal_data_obj,
modules = teal::modules(
tm_interactive_heatmap(
label = "Interactive heatmap",
datanames = c("expression_df", "wide_clinical_df")
)
)
)
# shiny::shinyApp(teal_app$ui, teal_app$server)Toy Data 2
The hermes::summarized_experiment object is split into two teal datasets: subject-level column data and a wide expression table. The expression feature names are normalized so they are syntactic column names, and id_seq is retained as a row-level key for teal joins.
# df_SE <- hermes::summarized_experiment
nrows <- 20; ncols <- 6
counts <- matrix(runif(nrows * ncols, 1, 1e4), nrows)
rowRanges <- GRanges(rep(c("chr1", "chr2"), c(5, 15)),
IRanges(sample(1000L, 20), width=100),
strand=Rle(c("+", "-"), c(12, 8)),
seqlengths=c(chr1=1800, chr2=1300))
colData <- DataFrame(Treatment=rep(c("ChIP", "Input"), 3),
row.names=LETTERS[1:6])
rse <- SummarizedExperiment(assays=SimpleList(counts=counts),
rowRanges=rowRanges, colData=colData)
df_SE <- rse
df_se_ADSL <- df_SE |>
SummarizedExperiment::colData() |>
as.data.frame() |>
tibble::rownames_to_column("id") |>
tibble::as_tibble()
df_se_exprs_w <- df_SE |>
SummarizedExperiment::assay() |>
t() |>
as.data.frame(check.names = FALSE) |>
tibble::rownames_to_column("id") |>
tibble::as_tibble() |>
dplyr::rename_with(~ stringr::str_replace_all(.x, ":", "_"), -id) |>
dplyr::group_by(id) |>
dplyr::mutate(id_seq = dplyr::row_number(), .before = 2) |>
dplyr::ungroup()
teal_data_obj_se <- teal.data::teal_data(
ADSL = df_se_ADSL,
ADAE = df_se_exprs_w
)
teal.data::join_keys(teal_data_obj_se) <- teal.data::join_keys(
teal.data::join_key("ADSL", "ADSL", keys = "id"),
teal.data::join_key("ADAE", "ADAE", keys = c("id", "id_seq")),
teal.data::join_key("ADSL", "ADAE", keys = "id")
)teal App Assembly 2
teal_app <- teal::init(
data = teal_data_obj_se,
modules = teal::modules(
tm_interactive_heatmap(
label = "Hermes expression heatmap",
datanames = c("ADSL", "ADAE")
)
)
)
# shiny::shinyApp(teal_app$ui, teal_app$server)