#' The input function of the contrasts module
#'
#' This module provides the form elements to control contrasts used in e.g.
#' differential expression panels. In particular it provides the ability for
#' users to add filters to progressively refine a query.
#'
#' @param id Submodule namespace
#' @param allow_filtering Provide the filtering fields? Can be disabled to
#' produce unfiltered contrasts tables.
#' @param summarise Provide summarisation controls? Allow user to control how
#' how values are summarised per group. Disabling this disables
#' summarisation, which may be the desired result for modules that just need
#' to use the contrasts drop-down.
#' @param dynamic_filters Logical indicating whether the user should be able
#' to add progressive filters.
#' @param select_summary_type Allow user to select summary type (e.g. mean)?
#' Passed to \code{\link{summarisematrixInput}}.
#'
#' @return output An HTML tag object that can be rendered as HTML using
#' as.character()
#'
#' @keywords shiny
#'
#' @examples
#' contrastsInput("test")
#'
contrastsInput <- function(id, allow_filtering = TRUE, summarise = TRUE, dynamic_filters = FALSE, select_summary_type = FALSE) {
ns <- NS(id)
inputs <- list()
if (allow_filtering) {
inputs <- pushToList(inputs, checkboxInput(ns("filterRows"), "Filter rows", TRUE))
} else {
inputs <- pushToList(inputs, shinyjs::hidden(checkboxInput(ns("filterRows"), "Filter rows", FALSE)))
}
# Contrasts filters added by the observeEvent() in the server function. If no dynamic filters are to be provided, then all we need to do is provide a
# placeholder and a single set of filters will be provided when the page loads. If dynamic filters ARE to be provided, then some other buttons etc are
# required.
contrast_filters <- list(tags$div(id = ns("contrasts-placeholder")))
if (dynamic_filters) {
contrast_filters <- c(
list((ns("dynamic"), 1)), list(helpText("Build up a complex query by adding filters below"), hr()), contrast_filters,
list(hr(), uiOutput(ns("combine_operator")), actionButton(ns("insertBtn"), "Add"), HTML(" "), actionButton(ns("removeBtn"), "Remove"))
)
} else {
contrast_filters <- pushToList(contrast_filters, uiOutput(ns("combine_operator")))
}
# inputs <- pushToList(inputs, conditionalPanel(condition = paste0('input['', ns('filterRows'), ''] == true '), contrast_filters))
inputs <- pushToList(inputs, contrast_filters)
if (summarise) {
inputs <- pushToList(inputs, summarisematrixInput(ns("contrasts"), allow_none = FALSE, select_summary_type = select_summary_type))
}
inputs
}
#' The output function of the contrasts module
#'
#' This module provides the form elements to control contrasts used in e.g.
#' differential expression panels. In particular it provides the ability for
#' users to add filters to progressively refine a query.
#'
#' This function provides a summary. Actual output should be rendered
#' by calling modules using the provided reactives.
#'
#' @param id Submodule namespace
#'
#' @examples
#' contrastsOutput("myid")
#'
contrastsOutput <- function(id) {
ns <- NS(id)
uiOutput(ns("summary"))
}
#' The server function of the contrasts module
#'
#' This module provides the form elements to control contrasts used in e.g.
#' differential expression panels. In particular it provides the ability for
#' users to add filters to progressively refine a query.
#'
#' This function is not called directly, but rather via callModule() (see
#' example).
#'
#' @param input Input object
#' @param output Output object
#' @param session Session object
#' @param eselist ExploratorySummarizedExperimentList object containing
#' ExploratorySummarizedExperiment objects
#' @param selectmatrix_reactives The list of reactive expressions returned by
#' a call to the \code{\link{selectmatrix}} module. This will be unpacked to
#' gain access to the data provided by those reaactive.
#' @param multiple Allow selection of multiple contrasts?
#' @param select_all_contrasts Select all contrasts by default?
#' @param show_controls Show the controls for contrast selection?
#' @param default_foldchange default value for the fold change filter
#' @param default_pval Default value for the p value field
#' @param default_qval Default value for the q value field
#'
#' @keywords shiny
#'
#' @import data.table
#'
#' @examples
#' callModule(contrasts, "differential", getExperiment = getExperiment, selectMatrix = selectMatrix, getAssay = getAssay, multiple = TRUE)
#'
contrasts <- function(input, output, session, eselist, selectmatrix_reactives = list(), multiple = FALSE, select_all_contrasts = FALSE, show_controls = TRUE,
default_foldchange = 2, default_pval = 0.05, default_qval = 0.1) {
ns <- session$ns
unpack.list(selectmatrix_reactives)
getSummaryType <- callModule(summarisematrix, "contrasts")
########################################################################### Rendering the contrast control filter sets
inserted <- c() # Stores the list of inserted filter sets
filterset_values <- list() # Stores the list of values in the filter set
makeReactiveBinding("filterset_values") # Make the stored values reactive
filter_observers <- list()
# This observer adds a set of filters on the first page load and when the assocaited button is clicked.
observeEvent(
{
selectMatrix()
input$insertBtn
},
{
ese <- getExperiment()
contrasts <- getAllContrasts()
contrast_numbers <- getAllContrastsNumbers()
assay <- getAssay()
coldata <- selectColData()
# Restrict contrasts to those valid for the input matrix
valid_contrasts <- unlist(lapply(contrasts, function(cont) {
all(c(cont['Group.1'], cont['Group.2']) %in% coldata[[cont['Variable']]])
}))
contrasts <- contrasts[valid_contrasts]
contrast_numbers <- contrast_numbers[valid_contrasts]
# btn keeps track of how many filter sets have been added
btn <- length(inserted)
# Call makeContrastFilterSet() to generate a set of filters, and add to the UI with insertUI()
insertUI(selector = paste0("#", ns("contrasts-placeholder")), where = "beforeEnd", ui = makeContrastFilterSet(ns, ese, assay, contrasts, contrast_numbers,
multiple = multiple, show_controls = show_controls, default_foldchange = default_foldchange, default_pval = default_pval, default_qval = default_qval,
filter_rows = getFilterRows(), index = btn, select_all_contrasts = select_all_contrasts
))
# Record the ID of the added filter set
inserted <<- c(inserted, paste0("contrast", btn))
# Now add observers for each new element generated by makeControlFilterSet(). When they fire, these observers will add the filter values to the reactive
# filterset_values. When this is referenced (by e.g. getFoldChange()), the dependency chain is established such that outputs are refreshed when the
# dynamically added fields are altered.
filterId <- paste0("filter", btn)
filter_observers[[filterId]] <<- lapply(c("contrasts", "fold_change", "q_value", "p_value", "fold_change_card", "q_value_card", "p_value_card"), function(field) {
filter_field_id <- paste0(field, btn)
observeEvent(input[[filter_field_id]],
{
req(input[[filter_field_id]])
if (is.null(filterset_values[[filterId]])) {
filterset_values[[filterId]] <<- list()
}
if (length(input[[filter_field_id]]) == 1 && input[[filter_field_id]] == "NULL") {
filterset_values[[filterId]][[field]] <<- NULL
} else {
filterset_values[[filterId]][[field]] <<- input[[filter_field_id]]
}
# filterset_values[[filterId]][[paste0(field, 'card')]] <<- input[[paste0(filter_field_id, 'card')]]
},
ignoreNULL = FALSE
)
})
},
ignoreNULL = FALSE,
priority = 1
)
# This observer removes a filter set when the 'remove' button is clicked. This removes both the UI element and its stored values in filterset_values
observeEvent(input$removeBtn, {
if (length(inserted) > 1) {
removeUI(selector = paste0("#", inserted[length(inserted)]))
inserted <<- inserted[-length(inserted)]
filterset_values[[length(filterset_values)]] <<- NULL
}
})
# When a new assay is selected, or when the input matrix is otherwise changed, we need to rebuild the inputs
observeEvent(selectMatrix(),
{
if (length(inserted) > 0) {
lapply(names(filterset_values), function(filterId) {
lapply(names(filterset_values[[filterId]]), function(field) {
filterset_values[[filterId]][[field]] <<- NULL
})
filterset_values[[filterId]] <<- NULL
})
removeUI(selector = ".shinyngs-contrast", multiple = TRUE, immediate = TRUE)
inserted <<- c()
}
},
priority = 2
)
# The combine_operator field is only necessary with more than one filter set
output$combine_operator <- renderUI({
scn <- getSelectedContrastNumbers()
if (length(scn) > 1) {
inlineField(selectInput(ns("combine_operator"), NULL, c(and = "intersect", or = "union")), label = "Combine using", 6)
} else {
(ns("combine_operator"), "intersect")
}
})
########################################################################### Accessors for form values
# Get current value of field which determines if the table should be filtered at all.
getFilterRows <- reactive({
req(! is.null(input$filterRows))
as.logical(input$filterRows)
})
# Get the indices of the currently selected contrasts for each filter set by querying filterset_values.
getSelectedContrastNumbers <- reactive({
req(length(filterset_values) > 0)
lapply(filterset_values, function(x) x$contrasts)
})
# Fetch the values from all the fold change filters
getFoldChange <- reactive({
req(length(filterset_values) > 0)
unlist(lapply(filterset_values, function(x) x$fold_change))
})
# Fetch the values from all the fold change cardinality filters
getFoldChangeCard <- reactive({
req(length(filterset_values) > 0)
card <- unlist(lapply(filterset_values, function(x) x$fold_change_card))
if (fcsAvailable()) {
req(length(card) > 0)
}
card
})
# Get current value of the q value filter
getQval <- reactive({
req(length(filterset_values) > 0)
unlist(lapply(filterset_values, function(x) x$q_value))
})
# Get current value of the q value cardinality filter
getQvalCard <- reactive({
req(length(filterset_values) > 0)
card <- unlist(lapply(filterset_values, function(x) x$q_value_card))
if (qvalsAvailable()) {
req(length(card) > 0)
}
card
})
# Get current value of the p value filter
getPval <- reactive({
req(length(filterset_values) > 0)
unlist(lapply(filterset_values, function(x) x$p_value))
})
# Get current value of the p value cardinality filter
getPvalCard <- reactive({
req(length(filterset_values) > 0)
card <- unlist(lapply(filterset_values, function(x) x$p_value_card))
if (pvalsAvailable()) {
req(length(card) > 0)
}
card
})
# Get method for combining filters
getFilterSetCombinationOperator <- reactive({
req(input$combine_operator)
input$combine_operator
})
# Use presence of combine operator as a flag for use of dynamic contrasts, i.e. the possibility of multiple filter sets
isDynamic <- reactive({
!is.null(input$dynamic)
})
########################################################################### Generate summaries and contrast stats for all contrasts and all rows. Then the data is handy for subsetting by other functions. NOTE: this uses ALL
########################################################################### rows in the input ExploratorySummarizedExperiment. The rows in the matrix returned by selectMatrix() will but a subset, but any modifications to the
########################################################################### rows used will not necessitate a re-calculation of these basic stats.
# Generate the summary statistic (probably mean) for column groups as defined by the possible contrasts. Other functions can then pick from this output and
# calculate fold changes etc.
getSummaries <- reactive({
if (!is.null(getSummaryType())) {
ese <- getExperiment()
contrasts <- getAllContrasts()
matrix <- getAssayMatrix()
coldata <- data.frame(colData(ese), check.names = FALSE)
validate(need(nrow(matrix) > 0, "Waiting for input matrix"))
contrast_variables <- unique(unlist(lapply(contrasts, function(x) x['Variable'])))
names(contrast_variables) <- contrast_variables
withProgress(message = paste("Calculating summaries by", getSummaryType()), value = 0, {
summaries <- lapply(contrast_variables, function(cv) summarizeMatrix(matrix, coldata[[cv]], getSummaryType()))
})
summaries
}
})
# Get all the contrasts the user specified in their StructuredExperiment- if any
getAllContrasts <- reactive({
if (length(eselist@contrasts) > 0) {
contrasts <- eselist@contrasts
contrasts <- lapply(contrasts, function(cont){
if (is.null(names(cont))){
names(cont) <- c('Variable', 'Group.1', 'Group.2')
}
cont
})
names(contrasts) <- as.character(1:length(contrasts))
contrasts
} else {
NULL
}
})
# Get a named vector of integers for contrasts, to be used in field etc
getAllContrastsNumbers <- reactive({
contrasts <- getAllContrasts()
contrast_names <- makeContrastNames()
if (!is.null(contrasts)) {
structure(names(contrasts), names = contrast_names)
} else {
NULL
}
})
# Get list describing, for each contrast, the samples on each side
getContrastSamples <- reactive({
ese <- getExperiment()
coldata <- selectColData()
contrasts <- getAllContrasts()
lapply(contrasts, function(c) {
list(colnames(ese)[coldata[c['Variable']] == c['Group.1']], colnames(ese)[coldata[c['Variable']] == c['Group.2']])
})
})
# Main function for returning the table of contrast information. Means, fold changes calculated on the fly, p/q values must be supplied in a 'contrast_stats' slot
# of the ExploratorySummarizedExperiment. Make a summary table for every contrast. This data can then be re-used when processing filter sets.
contrastsTables <- reactive({
matrix <- selectMatrix()
ese <- getExperiment()
summaries <- getSummaries()
contrasts <- getAllContrasts()
assay <- getAssay()
# There can be a mismatch between the conrasts and summaries as we adjust the input matrix. Wait for updates to finish before making the table.
# validate(need(all(unlist(lapply(selected_contrasts, function(x) all(x[-1] %in% colnames(summaries[[x[1]]]))))), 'Matching summaries and contrasts'))
withProgress(message = "Calculating contrast tables", value = 0, {
contrast_tables <- lapply(names(contrasts), function(c) {
cont <- contrasts[[c]]
smry1 <- summaries[[cont['Variable']]][, cont['Group.1']]
smry2 <- summaries[[cont['Variable']]][, cont['Group.2']]
ct <- data.frame(cont['Variable'], cont['Group.1'], cont['Group.2'], round(smry1, 2), round(smry2, 2), row.names = names(smry1))
names(ct) <- c("Variable", "Condition 1", "Condition 2", "Average 1", "Average 2")
# Use pre-computed fold changes where provided.
if (fcsAvailable()) {
fcs <- ese@contrast_stats[[assay]]$fold_changes
ct[["Fold change"]] <- round(fcs[match(rownames(ct), rownames(fcs)), as.numeric(c)], 2)
} else {
ct[["Fold change"]] <- round(foldChange(smry1, smry2), 2)
}
if (pvalsAvailable()) {
pvals <- ese@contrast_stats[[assay]]$pvals
ct[["p value"]] <- signif(pvals[match(rownames(ct), rownames(pvals)), as.numeric(c)], 5)
}
if (qvalsAvailable()) {
qvals <- ese@contrast_stats[[assay]]$qvals
ct[["q value"]] <- signif(qvals[match(rownames(ct), rownames(qvals)), as.numeric(c)], 5)
}
ct
})
})
names(contrast_tables) <- getAllContrastsNumbers()
contrast_tables
})
# Test for the presence of pre-computed fold changes (e.g. from modelling)
fcsAvailable <- reactive({
assay <- getAssay()
ese <- getExperiment()
length(ese@contrast_stats) > 0 && assay %in% names(ese@contrast_stats) && "fold_changes" %in% names(ese@contrast_stats[[assay]]) && !is.null(ese@contrast_stats[[assay]]$fold_changes)
})
# Test for the presence of p values in the input object
pvalsAvailable <- reactive({
assay <- getAssay()
ese <- getExperiment()
length(ese@contrast_stats) > 0 && assay %in% names(ese@contrast_stats) && "pvals" %in% names(ese@contrast_stats[[assay]]) && !is.null(ese@contrast_stats[[assay]]$pvals)
})
# Test for the presence of q values in the input object
qvalsAvailable <- reactive({
assay <- getAssay()
ese <- getExperiment()
length(ese@contrast_stats) > 0 && assay %in% names(ese@contrast_stats) && "qvals" %in% names(ese@contrast_stats[[assay]]) && !is.null(ese@contrast_stats[[assay]]$qvals)
})
########################################################################### Subsetting using the rows in the input matrix. This does NOT involve the filters from this module, but simply subsets the base data to the rows pertinent
########################################################################### to the input matrix.
# Get contrasts tables with rows reflecting the input matrix
contrastsTablesToMatchMatrix <- reactive({
contrast_tables <- contrastsTables()
matrix <- selectMatrix()
lapply(contrast_tables, function(ct) {
ct[rownames(matrix), ]
})
})
########################################################################### Contrast naming
# Make names for the contrasts
makeContrastNames <- reactive({
contrasts <- getAllContrasts()
lapply(contrasts, function(x){
x <- x[! names(x) %in% 'id']
contrast_name <- paste(prettifyVariablename(x['Variable']), paste(x['Group.2'], x['Group.1'], sep = " vs "), sep = ": ")
extras <- setdiff(names(x), c('Variable', 'Group.1', 'Group.2'))
if (length(extras) > 0 ){
suffix <- paste0('(', paste(paste(extras, x[extras], sep = ':'), collapse = ','), ')')
contrast_name <- paste(contrast_name, suffix)
}
contrast_name
})
})
# Make safe set of names to be used where spaces etc not allowed
makeSafeContrastNames <- reactive({
contrasts <- getAllContrasts()
names <- lapply(contrasts, function(x) paste(ucfirst(prettifyVariablename(x['Variable'])), paste(ucfirst(x['Group.2']), ucfirst(x['Group.1']), sep = "_vs_"), sep = "."))
names <- lapply(names, function(name) {
name <- sub("\\+", "_POS_", name)
name <- sub("\\-", "_NEG_", name)
})
names
})
########################################################################### Subset contrast-related variables according to filters
# Get the actual contrasts to which the numbers from the interface pertain
getSelectedContrasts <- reactive({
scn <- getSelectedContrastNumbers()
all_contrasts <- getAllContrasts()
lapply(scn, function(s) {
all_contrasts[s]
})
})
# Get the name of the currently selected contrast
getSelectedContrastNames <- reactive({
contrast_names <- makeContrastNames()
lapply(getSelectedContrastNumbers(), function(x) contrast_names[x])
})
# The same, but with safe names that won't get mangled by plotting etc
getSafeSelectedContrastNames <- reactive({
contrast_names <- makeSafeContrastNames()
lapply(getSelectedContrastNumbers(), function(scns) {
contrast_names[scns]
})
})
# Get samples for currently selected contrast
getSelectedContrastSamples <- reactive({
contrast_samples <- getContrastSamples()
selected_contrasts <- getSelectedContrastNumbers()
contrast_samples[[selected_contrasts]]
})
########################################################################### Process data in response to contrasts filters
# If we're only looking at a single contrast filter with a single contrast (e.g. for a fold change plot etc), then we can simplify.
singleContrast <- reactive({
selected_contrasts <- getSelectedContrastNumbers()
length(selected_contrasts) == 1 && length(selected_contrasts[[1]]) == 1
})
# Filter contrasts tables down to the contrasts of interest
selectedContrastsTables <- reactive({
selected_contrasts <- getSelectedContrastNumbers()
contrast_tables <- contrastsTablesToMatchMatrix()
req(selected_contrasts, contrast_tables)
# Selected contrasts is a list, one for each filter set. Each one can have multiple contrasts
withProgress(message = "Filtering to specified features", value = 0, {
lapply(selected_contrasts, function(scs_set) {
lapply(scs_set, function(s) {
ct <- contrast_tables[[s]]
if (singleContrast()) {
simplifyContrastTable(ct)
} else {
ct
}
})
})
})
})
# Apply user filters to results of contrastsTables(). Called on first page load and on subsequent clicks of 'Apply'.
filteredContrastsTables <- reactive({
selected_contrasts_tables <- selectedContrastsTables()
req(length(selected_contrasts_tables) > 0)
if (getFilterRows()) {
ese <- getExperiment()
assay <- getAssay()
fold_change <- getFoldChange()
fold_change_card <- getFoldChangeCard()
p_value <- getPval()
p_value_card <- getPvalCard()
q_value <- getQval()
q_value_card <- getQvalCard()
withProgress(message = "Applying filters", value = 0, {
fcts <- lapply(1:length(selected_contrasts_tables), function(i) {
sct <- selected_contrasts_tables[[i]]
lapply(sct, function(s) {
filter <- evaluateCardinalFilter(s[["Fold change"]], fold_change_card[i], fold_change[i])
if (pvalsAvailable()) {
filter <- filter & evaluateCardinalFilter(s[["p value"]], p_value_card[i], p_value[i])
}
if (qvalsAvailable()) {
filter <- filter & evaluateCardinalFilter(s[["q value"]], q_value_card[i], q_value[i])
}
s[filter, , drop = FALSE]
})
})
})
} else {
fcts <- selected_contrasts_tables
}
fcts
})
# Find the list of features that result from combining all the filters
selectFilterFinalFeatures <- reactive({
filtered_contrasts_tables <- filteredContrastsTables()
validate(need(length(filtered_contrasts_tables) > 0, "Waiting for filtered contrasts tables"))
lapply(filtered_contrasts_tables, function(fcts) {
Reduce(intersect, lapply(fcts, function(fct) {
rownames(fct)
}))
})
})
selectFinalFeatures <- reactive({
filter_final_features <- selectFilterFinalFeatures()
withProgress(message = "Selecting final feature set", value = 0, {
comb_op <- getFilterSetCombinationOperator()
Reduce(get(comb_op), filter_final_features)
})
})
# The output of filteredContrastsTables() are significant results for each filter set, and each contrast within those.
labelledContrastsTable <- reactive({
ese <- getExperiment()
sff <- selectFinalFeatures()
filtered_contrast_tables <- filteredContrastsTables()
metafields <- c()
if (!is.null(getMetafields)) {
metafields <- getMetafields()
}
withProgress(message = "Making labelled table", value = 0, {
final_contrasts_table <- unique(rbindlist(lapply(filtered_contrast_tables, function(fcts) {
rbindlist(lapply(fcts, function(fct) {
labelMatrix(fct[sff, , drop = FALSE], ese = ese, metafields = metafields)
}))
})))
})
})
# Use labelledContrastsTable to get the labelled matrix and add some links.
linkedLabelledContrastsTable <- reactive({
lct <- labelledContrastsTable()
if (length(eselist@url_roots) > 0) {
lct <- linkMatrix(lct, eselist@url_roots)
}
lct
})
# A summary table of differential expression
makeDifferentialSetSummary <- reactive({
fcts <- filteredContrastsTables()
selected_contrasts <- getSelectedContrasts()
queries <- getQueryStrings()
eid <- getExperimentId()
summaries <- lapply(1:length(fcts), function(i) {
summary <- data.frame(cbind(query = queries[i], do.call(rbind, selected_contrasts[[i]])))
colnames(summary) <- c("Query", "Variable", "group 1", "group 2")
summary[[paste0("Differential ", eid, "s (up)")]] <- unlist(lapply(fcts[[i]], function(x) sum(x[, "Fold change"] > 0)))
summary[[paste0("Differential ", eid, "s (down)")]] <- unlist(lapply(fcts[[i]], function(x) sum(x[, "Fold change"] < 0)))
summary[[paste0("Differential ", eid, "s (total)")]] <- unlist(lapply(fcts[[i]], nrow))
summary
})
if (length(summaries) == 1) {
summaries[[1]][, -1]
} else {
rbindlist(summaries)
}
})
getQueryStrings <- reactive({
contrast_names <- makeContrastNames()
# Get the current filters, getting the name for the contrast(s)
fvs <- lapply(filterset_values, function(fv) {
fv$contrasts <- paste(contrast_names[fv$contrasts], collapse = ", ")
fv
})
unlist(lapply(fvs, function(x) {
paste("<p>", paste(unlist(lapply(grep("card", names(x[-1]), invert = TRUE, value = TRUE), function(y) {
paste(y, x[[paste0(y, "_card")]], x[[y]])
})), collapse = " AND "), "in <i>", x[[1]], "</i></p>")
}))
})
makeQuerySummary <- reactive({
filters <- getQueryStrings()
# Get the list of features resulting from each filter
filter_final_features <- selectFilterFinalFeatures()
# Make a table of the number of features resulting from each filter
query_summary <- data.frame(filter = filters, features = unlist(lapply(filter_final_features, length)), stringsAsFactors = FALSE)
exp_id <- getExperimentId()
colnames(query_summary)[2] <- paste0(exp_id, "s")
# Convert to labels
labelfield <- getLabelField()
if (!is.null(labelfield)) {
ese <- getExperiment()
filter_final_labels <- lapply(filter_final_features, function(x) convertIds(x, ese, labelfield))
query_summary[[paste0(labelfield, "s")]] <- unlist(lapply(filter_final_labels, function(x) length(unique(x))))
}
query_summary
})
########################################################################### Tell the user something about the query and its results
output$summary <- renderUI({
query_summary <- makeQuerySummary()
comb_op <- getFilterSetCombinationOperator()
operator <- ifelse(comb_op == "intersect", "AND", "OR")
query_summary[-1, 1] <- paste0("<p><b>", operator, "</b></p>", query_summary[-1, 1])
if (ncol(query_summary) == 2) {
column_widths <- c(8, 4)
} else {
column_widths <- c(6, 3, 3)
}
makeFluidRow <- function(row) {
do.call(fluidRow, lapply(1:length(row), function(r) {
column(column_widths[r], HTML(row[r]))
}))
}
summary_bits <- list(h4("Query Summary"), makeFluidRow(prettifyVariablename(colnames(query_summary))), br(), apply(query_summary, 1, makeFluidRow))
if (nrow(query_summary) > 1) {
sff_in <- selectFinalFeatures()
sff <- unique(sff_in)
ese <- getExperiment()
summary_row <- c(comb_op, length(sff))
labelfield <- getLabelField()
if (!is.null(labelfield)) {
labels <- convertIds(ids = sff, ese, labelfield)
summary_row <- c(summary_row, length(unique(labels[!is.na(labels)])))
}
summary_bits <- c(summary_bits, list(hr(), makeFluidRow(summary_row)))
}
list(tags$br(), do.call(wellPanel, summary_bits))
})
########################################################################### Return the reactive that allow other modules to interact with this one
list(
getFoldChange = getFoldChange, getFoldChangeCard = getFoldChangeCard, getQval = getQval, getQvalCard = getQvalCard, getPval = getPval, getPvalCard = getPvalCard,
getAllContrasts = getAllContrasts, getSelectedContrasts = getSelectedContrasts, getSelectedContrastNumbers = getSelectedContrastNumbers, getSelectedContrastNames = getSelectedContrastNames,
getSafeSelectedContrastNames = getSafeSelectedContrastNames, getContrastSamples = getContrastSamples, getSelectedContrastSamples = getSelectedContrastSamples,
contrastsTables = contrastsTables, filteredContrastsTables = filteredContrastsTables, labelledContrastsTable = labelledContrastsTable, linkedLabelledContrastsTable = linkedLabelledContrastsTable,
makeDifferentialSetSummary = makeDifferentialSetSummary, getQueryStrings = getQueryStrings, selectedContrastsTables = selectedContrastsTables
)
}
#' Calculate fold change between two vectors
#'
#' @param vec1 First vector
#' @param vec2 Second vector
#'
#' @return Vector of fold changes
#'
#' @export
#' @examples
#' foldChange(c(2, 2, 2), c(2, 4, 8))
#'
foldChange <- function(vec1, vec2) {
fc <- vec2 / vec1
fc[vec1 == vec2] <- 1
fc[which(fc < 1)] <- -1 / fc[which(fc < 1)]
fc
}
#' Make a complete set of filters for a contrast: the contrast itself, fold
#' change, and where applicable p- and q- values.
#'
#' @param ns A namespace function (created with \code{\link[shiny]{NS}} to be
#' used in creating field IDs.
#' @param ese ExploratorySummarizedExperiment object
#' @param assay Assay in \code{ese}
#' @param contrasts A list of lists specifying contrasts.
#' @param contrast_numbers A named vector of indices corresponding to
#' \code{contrasts}.
#' @param multiple Allow multiple contrasts to be selected? Passed to
#' \code{\link{makeContrastControl}}.
#' @param show_controls Show controls? Setting to false will cause them to be
#' hidden.
#' @param default_foldchange Default value for the fold change field
#' @param default_pval Default value for the p value field
#' @param default_qval Default value for the q value field
#' @param index Index. Will be used to differentiate mutiple copies of the
#' field set.
#' @param filter_rows Use fold change and p value etc to filter values?
#' @param select_all_contrasts Select all contrasts by default?
#'
#' @return output An HTML tag object that can be rendered as HTML using
#' as.character()
makeContrastFilterSet <- function(ns, ese, assay, contrasts, contrast_numbers, multiple, show_controls, default_foldchange = 2, default_pval = 0.05, default_qval = 0.1,
index = "", filter_rows = TRUE, select_all_contrasts = FALSE) {
contrast_field_set <- list(makeContrastControl(ns(paste0("contrasts", index)), contrasts, contrast_numbers,
multiple = multiple, show_controls = show_controls,
select_all = select_all_contrasts
))
if (filter_rows) {
# p value field
if ("pvals" %in% names(ese@contrast_stats[[assay]]) && !is.null(ese@contrast_stats[[assay]]$pvals)) {
pval_field <- cardinalNumericField(ns(paste0("p_value", index)), ns(paste0("p_value_card", index)),
value = default_pval, label = "p value", min = 0,
max = 1, step = 0.01
)
} else {
pval_field <- (ns(paste0("p_value", index)), values = "NULL")
pval_field <- (ns(paste0("p_value_card", index)), values = "NULL")
}
contrast_field_set <- c(contrast_field_set, list(pval_field))
# q value field
if ("qvals" %in% names(ese@contrast_stats[[assay]]) && !is.null(ese@contrast_stats[[assay]]$qvals)) {
qval_field <- cardinalNumericField(ns(paste0("q_value", index)), ns(paste0("q_value_card", index)),
value = default_qval, label = "q value", min = 0,
max = 1, step = 0.01
)
} else {
qval_field <- (ns(paste0("q_value", index)), values = "NULL")
qval_field <- (ns(paste0("q_value_card", index)), values = "NULL")
}
contrast_field_set <- c(contrast_field_set, list(qval_field))
fold_change_field <- cardinalNumericField(ns(paste0("fold_change", index)), ns(paste0("fold_change_card", index)),
value = default_foldchange, label = "fold change",
cardinality = ">= or <= -", step = 0.5
)
contrast_field_set <- c(contrast_field_set, list(fold_change_field))
}
tags$fieldset(id = paste0("contrast", index), contrast_field_set, class = "shinyngs-contrast")
}
#' Make a select field for picking one or more contrasts
#'
#' @param id An id to apply to form elements
#' @param contrasts A list of lists specifying contrasts.
#' @param contrast_numbers A named vector of indices corresponding to
#' \code{contrasts}.
#' @param multiple Allow multiple contrasts to be selected?
#' @param show_controls Show controls? Setting to false will cause them to be
#' hidden.
#' @param select_all Select all contrasts by default?
#'
#' @return output An HTML tag object that can be rendered as HTML using
#' as.character()
makeContrastControl <- function(id, contrasts, contrast_numbers, multiple = FALSE, show_controls = TRUE, select_all = FALSE) {
if (!is.null(contrast_numbers)) {
if (multiple) {
selected <- contrast_numbers
if (!select_all) {
selected <- contrast_numbers[1]
}
cont_control <- selectInput(id, "Contrast(s):", choices = contrast_numbers, selected = selected, selectize = TRUE, multiple = TRUE)
} else {
cont_control <- selectInput(id, "Contrast(s):", contrast_numbers)
}
if (!show_controls) {
cont_control <- shinyjs::hidden(cont_control)
}
cont_control
}
}
#' Simplify a contrast table
#'
#' By default the contrast tables are created with three initial columns to
#' indicate the contrast: the metadata variable and the two values of that
#' variable that define the contrast. But if there is only one contrast then
#' this make the table overly cumbersome, an we can simplify it by simply
#' naming the average column to the values of the contrast variable.
#'
#' @param table Three-column contrast table
#'
#' @return output Simplified table
simplifyContrastTable <- function(table) {
if (length(unique(table$Variable)) > 1 | length(unique(table[["Condition 1"]])) > 1 | length(unique(table[["Condition 2"]])) > 1) {
stop("Table represents multiple contrasts, it cannot be simplified.")
}
colnames(table)[4:5] <- c(as.character(table[1, "Condition 1"]), as.character(table[1, "Condition 2"]))
table[, 4:ncol(table)]
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.