diff --git a/R/makeShinyCodes.R b/R/makeShinyCodes.R index f0b22d8..4a073ed 100644 --- a/R/makeShinyCodes.R +++ b/R/makeShinyCodes.R @@ -122,6 +122,7 @@ makeShinyCodes <- function( readr::write_file(wrSVmainB1(i), file = fname, append = TRUE) readr::write_file(wrSVmainB2(i), file = fname, append = TRUE) readr::write_file(wrSVmainB3(i), file = fname, append = TRUE) + readr::write_file(wrSVmainMOD(i), file = fname, append = TRUE) } readr::write_file(wrSVpost(), file = fname, append = TRUE) @@ -156,6 +157,7 @@ makeShinyCodes <- function( readr::write_file(wrUImainB1(shiny.prefix, defPtSiz[1]), file = fname, append = TRUE) readr::write_file(wrUImainB2(shiny.prefix), file = fname, append = TRUE) readr::write_file(wrUImainB3(shiny.prefix), file = fname, append = TRUE) + readr::write_file(wrUImainMOD(shiny.prefix), file = fname, append = TRUE) readr::write_file(glue::glue(', \n'), append = TRUE, file = fname) } else { for (i in seq_along(shiny.prefix)) { @@ -223,6 +225,11 @@ makeShinyCodes <- function( file = fname, append = TRUE ) + readr::write_file( + wrUImainMOD(shiny.prefix[i]), + file = fname, + append = TRUE + ) readr::write_file(glue::glue('), \n\n\n'), append = TRUE, file = fname) } } diff --git a/R/writer0.R b/R/writer0.R index daa52ba..f9e67b7 100644 --- a/R/writer0.R +++ b/R/writer0.R @@ -1293,7 +1293,27 @@ wrShFunc <- function() { ' }}\n', ' return(ggOut)\n', '}}\n', - '\n', + '\n', + '\n', + '\n', + '## Module score helper function \n', + 'scModuleScore <- function(inpGene, inpH5, geneList, seed = 42) {{\n', + ' valid <- geneList[present == TRUE]$gene\n', + ' bgPool <- setdiff(names(inpGene), valid)\n', + ' set.seed(seed)\n', + ' bgGenes <- sample(bgPool, min(length(valid), length(bgPool)))\n', + ' h5file <- H5File$new(inpH5, mode = "r")\n', + ' h5data <- h5file[["grp"]][["data"]]\n', + ' readRows <- function(genes) {{\n', + ' do.call(rbind, lapply(genes, function(g)\n', + ' pmax(h5data$read(args = list(inpGene[g], quote(expr=))), 0)))\n', + ' }}\n', + ' targetMat <- readRows(valid)\n', + ' bgMat <- readRows(bgGenes)\n', + ' h5file$close_all()\n', + ' colMeans(targetMat) - colMeans(bgMat)\n', + '}}\n', + '\n', '\n', '\n' ) diff --git a/R/writer1.R b/R/writer1.R index 02f58e0..eb32a8d 100644 --- a/R/writer1.R +++ b/R/writer1.R @@ -1519,6 +1519,174 @@ wrSVmainT1 <- function(prefix) { ) } +#' Write code for server.R - Module Score tab +#' +#' @rdname wrSVmainMOD +#' @export wrSVmainMOD +#' +wrSVmainMOD <- function(prefix) { + glue::glue( + ' ### Functions for Module Score tab \n', + ' output${prefix}comb_sub1.ui <- renderUI({{ \n', + ' sub = strsplit({prefix}conf[UI == input${prefix}comb_sub1]$fID, "\\\\|")[[1]] \n', + ' checkboxGroupInput("{prefix}comb_sub2", "Select which cells to show", inline = TRUE, \n', + ' choices = sub, selected = sub) \n', + ' }}) \n', + ' observeEvent(input${prefix}comb_sub1non, {{ \n', + ' sub = strsplit({prefix}conf[UI == input${prefix}comb_sub1]$fID, "\\\\|")[[1]] \n', + ' updateCheckboxGroupInput(session, inputId = "{prefix}comb_sub2", \n', + ' choices = sub, selected = NULL, inline = TRUE) \n', + ' }}) \n', + ' observeEvent(input${prefix}comb_sub1all, {{ \n', + ' sub = strsplit({prefix}conf[UI == input${prefix}comb_sub1]$fID, "\\\\|")[[1]] \n', + ' updateCheckboxGroupInput(session, inputId = "{prefix}comb_sub2", \n', + ' choices = sub, selected = sub, inline = TRUE) \n', + ' }}) \n', + '\n', + ' output${prefix}comb_mod_geneTxt <- renderUI({{ \n', + ' geneList <- scGeneList(input${prefix}comb_mod_inp, {prefix}gene[[input${prefix}comb_mod_ass]]) \n', + ' if (nrow(geneList) > 50) {{ \n', + ' HTML("More than 50 genes! Please reduce the list.") \n', + ' }} else {{ \n', + ' ok <- nrow(geneList[present == TRUE]) \n', + ' bad <- nrow(geneList[present == FALSE]) \n', + ' oup <- paste0(ok, " genes found and will be used for scoring") \n', + ' if (bad > 0) \n', + ' oup <- paste0(oup, "
", bad, " not found: ", \n', + ' paste0(geneList[present == FALSE]$gene, collapse = ", "), "") \n', + ' HTML(oup) \n', + ' }} \n', + ' }}) \n', + '\n', + ' {prefix}comb_mod_score <- reactive({{ \n', + ' geneList <- scGeneList(input${prefix}comb_mod_inp, {prefix}gene[[input${prefix}comb_mod_ass]]) \n', + ' shiny::validate( \n', + ' need(nrow(geneList[present == TRUE]) >= 2, \n', + ' "Please provide at least 2 valid genes."), \n', + ' need(nrow(geneList) <= 50, \n', + ' "More than 50 genes - please reduce the list.") \n', + ' ) \n', + ' inpH5 <- paste0("{prefix}assay_", input${prefix}comb_mod_ass, ".h5") \n', + ' scModuleScore({prefix}gene[[input${prefix}comb_mod_ass]], inpH5, geneList, \n', + ' seed = 42) \n', + ' }}) \n', + '\n', + ' {prefix}comb_mod_dr_oup <- reactive({{ \n', + ' score <- {prefix}comb_mod_score() \n', + ' inpdr <- input${prefix}comb_mod_dr \n', + ' ggData <- data.table({prefix}dimr[[inpdr]]); colnames(ggData) <- c("X","Y") \n', + ' rat <- (max(ggData$X) - min(ggData$X)) / (max(ggData$Y) - min(ggData$Y)) \n', + ' sub1 <- if (is.null(input${prefix}comb_sub1)) {prefix}conf$UI[1] else input${prefix}comb_sub1 \n', + ' ggData$sub <- {prefix}meta[, {prefix}conf[UI == sub1]$ID, with = FALSE][[1]] \n', + ' ggData$val <- score \n', + ' bgCells <- FALSE \n', + ' if (length(input${prefix}comb_sub2) != 0 && \n', + ' length(input${prefix}comb_sub2) != nlevels(ggData$sub)) {{ \n', + ' bgCells <- TRUE \n', + ' ggData2 <- ggData[!sub %in% input${prefix}comb_sub2] \n', + ' ggData <- ggData[sub %in% input${prefix}comb_sub2] \n', + ' }} \n', + ' if (input${prefix}comb_mod_ord == "Max-1st") {{ \n', + ' ggData <- ggData[order(val)] \n', + ' }} else if (input${prefix}comb_mod_ord == "Min-1st") {{ \n', + ' ggData <- ggData[order(-val)] \n', + ' }} else if (input${prefix}comb_mod_ord == "Random") {{ \n', + ' ggData <- ggData[sample(nrow(ggData))] \n', + ' }} \n', + ' inpfsz <- sList[input${prefix}comb_mod_fsz] \n', + ' inpcol <- cList[[input${prefix}comb_mod_col]] \n', + ' ggOut <- ggplot(ggData, aes(X, Y, color = val)) \n', + ' if (bgCells) \n', + ' ggOut <- ggOut + geom_point(data = ggData2, color = "snow2", \n', + ' size = input${prefix}comb_mod_siz / 2, shape = 16) \n', + ' ggOut <- ggOut + \n', + ' geom_point(size = input${prefix}comb_mod_siz, shape = 16) + \n', + ' xlab(paste0(inpdr,"1")) + ylab(paste0(inpdr,"2")) + \n', + ' sctheme(base_size = inpfsz, XYval = input${prefix}comb_mod_txt) + \n', + ' scale_color_gradientn("Module Score ", colours = inpcol) + \n', + ' guides(color = guide_colorbar(barwidth = 15)) \n', + ' asp <- input${prefix}comb_mod_asp \n', + ' if (asp == "Square") ggOut <- ggOut + coord_fixed(ratio = 1) \n', + ' else if (asp == "Fixed") ggOut <- ggOut + coord_fixed(ratio = rat) \n', + ' ggOut \n', + ' }}) \n', + ' output${prefix}comb_mod_dr_oup.ui <- renderUI({{ \n', + ' plotOutput("{prefix}comb_mod_dr_oup", height = pList[input${prefix}comb_mod_psz]) \n', + ' }}) \n', + ' output${prefix}comb_mod_dr_oup <- renderPlot({{ \n', + ' {prefix}comb_mod_dr_oup() + theme(legend.position = "none") \n', + ' }}) \n', + ' output${prefix}comb_mod_leg_oup.ui <- renderUI({{ \n', + ' plotOutput("{prefix}comb_mod_leg_oup", \n', + ' height = 72 * convertHeight( \n', + ' grobHeight(g_legend({prefix}comb_mod_dr_oup())), \n', + ' unitTo = "in", valueOnly = TRUE) + 50) \n', + ' }}) \n', + ' output${prefix}comb_mod_leg_oup <- renderPlot({{ \n', + ' grid.newpage(); grid.draw(g_legend({prefix}comb_mod_dr_oup())) \n', + ' }}) \n', + ' output${prefix}comb_mod_dr_oup.dl <- downloadHandler( \n', + ' filename = function() {{ \n', + ' paste0("{prefix}modscore_dimred_", input${prefix}comb_mod_dr, \n', + ' ".", input${prefix}comb_mod_dr_oup.f) \n', + ' }}, \n', + ' content = function(file) {{ \n', + ' p <- {prefix}comb_mod_dr_oup() + theme(legend.position = "none") \n', + ' ggsav(file, height = input${prefix}comb_mod_dr_oup.h, \n', + ' width = input${prefix}comb_mod_dr_oup.w, plot = p) \n', + ' }} \n', + ' ) \n', + '\n', + ' {prefix}comb_mod_vln_oup <- reactive({{ \n', + ' score <- {prefix}comb_mod_score() \n', + ' sub1 <- if (is.null(input${prefix}comb_sub1)) {prefix}conf$UI[1] else input${prefix}comb_sub1 \n', + ' ggData <- data.table( \n', + ' X = {prefix}meta[, {prefix}conf[UI == input${prefix}comb_grp]$ID, with = FALSE][[1]], \n', + ' sub = {prefix}meta[, {prefix}conf[UI == sub1]$ID, with = FALSE][[1]], \n', + ' val = score \n', + ' ) \n', + ' if (length(input${prefix}comb_sub2) != 0 && \n', + ' length(input${prefix}comb_sub2) != nlevels(ggData$sub)) \n', + ' ggData <- ggData[sub %in% input${prefix}comb_sub2] \n', + ' ggCol <- strsplit({prefix}conf[UI == input${prefix}comb_grp]$fCL, "\\\\|")[[1]] \n', + ' names(ggCol) <- levels(ggData$X) \n', + ' ggLvl <- levels(ggData$X)[levels(ggData$X) %in% unique(ggData$X)] \n', + ' ggData$X <- factor(ggData$X, levels = ggLvl) \n', + ' ggCol <- ggCol[ggLvl] \n', + ' inpfsz <- sList[input${prefix}comb_mod_fsz] \n', + ' if (input${prefix}comb_mod_vln_typ == "violin") {{ \n', + ' ggOut <- ggplot(ggData, aes(X, val, fill = X)) + geom_violin(scale = "width") \n', + ' }} else {{ \n', + ' ggOut <- ggplot(ggData, aes(X, val, fill = X)) + geom_boxplot() \n', + ' }} \n', + ' if (input${prefix}comb_mod_vln_pts) \n', + ' ggOut <- ggOut + geom_jitter(size = input${prefix}comb_mod_siz, shape = 16) \n', + ' ggOut + \n', + ' xlab(input${prefix}comb_grp) + ylab("Module Score") + \n', + ' sctheme(base_size = inpfsz, Xang = 45, XjusH = 1) + \n', + ' scale_fill_manual("", values = ggCol) + \n', + ' theme(legend.position = "none") \n', + ' }}) \n', + ' output${prefix}comb_mod_vln_oup.ui <- renderUI({{ \n', + ' plotOutput("{prefix}comb_mod_vln_oup", height = pList2[input${prefix}comb_mod_psz]) \n', + ' }}) \n', + ' output${prefix}comb_mod_vln_oup <- renderPlot({{{prefix}comb_mod_vln_oup()}}) \n', + ' output${prefix}comb_mod_vln_oup.dl <- downloadHandler( \n', + ' filename = function() {{ \n', + ' paste0("{prefix}modscore_violin_", input${prefix}comb_grp, \n', + ' ".", input${prefix}comb_mod_vln_oup.f) \n', + ' }}, \n', + ' content = function(file) {{ \n', + ' ggsav(file, height = input${prefix}comb_mod_vln_oup.h, \n', + ' width = input${prefix}comb_mod_vln_oup.w, plot = {prefix}comb_mod_vln_oup()) \n', + ' }} \n', + ' ) \n', + '\n', + '\n', + '\n' + ) +} + #' Write code for server.R #' #' @rdname wrSVpost diff --git a/R/writer2.R b/R/writer2.R index 784b169..fcc4f94 100644 --- a/R/writer2.R +++ b/R/writer2.R @@ -1076,6 +1076,128 @@ wrUImainB3 <- function(prefix) { ) } +#' Write code for ui.R - Module Score tab +#' +#' @rdname wrUImainMOD +#' @export wrUImainMOD +#' +wrUImainMOD <- function(prefix) { + glue::glue( + ' ### Tab: Module scoring \n', + ' tabPanel( \n', + ' HTML("Module scoring"), \n', + ' h4("Gene Module Score: Violin + Dimred"), \n', + ' "Compute a per-cell module score (mean expression of a gene list minus the mean of ", \n', + ' "a random background gene set of equal size), then visualise across cell groups ", \n', + ' "and on a reduced-dimension plot.", \n', + ' br(), br(), \n', + ' fluidRow( \n', + ' column( \n', + ' 4, \n', + ' selectInput("{prefix}comb_grp", "Group by:", \n', + ' choices = {prefix}conf[grp == TRUE]$UI, \n', + ' selected = {prefix}def$grp1) \n', + ' ), \n', + ' column( \n', + ' 4, \n', + ' actionButton("{prefix}comb_togL", "Toggle to subset cells"), \n', + ' conditionalPanel( \n', + ' condition = "input.{prefix}comb_togL % 2 == 1", \n', + ' selectInput("{prefix}comb_sub1", "Cell information to subset:", \n', + ' choices = {prefix}conf[grp == TRUE]$UI, \n', + ' selected = {prefix}def$grp1), \n', + ' uiOutput("{prefix}comb_sub1.ui"), \n', + ' actionButton("{prefix}comb_sub1all", "Select all groups", class = "btn btn-primary"), \n', + ' actionButton("{prefix}comb_sub1non", "Deselect all groups", class = "btn btn-primary") \n', + ' ) \n', + ' ) \n', + ' ), \n', + ' fluidRow( \n', + ' column( \n', + ' 3, style = "border-right: 2px solid black", \n', + ' textAreaInput("{prefix}comb_mod_inp", \n', + ' HTML("Gene list for module score
(max 50, separated by , ; or newline):"), \n', + ' height = "200px", \n', + ' value = paste0({prefix}def$genes[[1]], collapse = ", ")) %>% \n', + ' helper(type = "inline", size = "m", fade = TRUE, \n', + ' title = "Genes for module score", \n', + ' content = c("Input target genes to score per cell", \n', + ' "- Score = mean(target genes) - mean(random background genes)", \n', + ' "- Background pool size equals the target gene list size", \n', + ' "- Separate genes by comma, semicolon, or newline")), \n', + ' selectInput("{prefix}comb_mod_ass", "Assay:", \n', + ' choices = {prefix}def$assay, \n', + ' selected = {prefix}def$assay[1]), \n', + ' h4(htmlOutput("{prefix}comb_mod_geneTxt")), \n', + ' br(), \n', + ' actionButton("{prefix}comb_mod_tog", "Toggle graphics controls"), \n', + ' conditionalPanel( \n', + ' condition = "input.{prefix}comb_mod_tog % 2 == 1", \n', + ' sliderInput("{prefix}comb_mod_siz", "Point size:", \n', + ' min = 0, max = 4, value = 1.25, step = 0.25), \n', + ' radioButtons("{prefix}comb_mod_psz", "Plot size:", \n', + ' choices = c("Small", "Medium", "Large"), \n', + ' selected = "Medium", inline = TRUE), \n', + ' radioButtons("{prefix}comb_mod_fsz", "Font size:", \n', + ' choices = c("Small", "Medium", "Large"), \n', + ' selected = "Medium", inline = TRUE), \n', + ' radioButtons("{prefix}comb_mod_col", "Dimred colour palette:", \n', + ' choices = c("White-Red", "Blue-Yellow-Red", "Yellow-Green-Purple"), \n', + ' selected = "Blue-Yellow-Red"), \n', + ' radioButtons("{prefix}comb_mod_dr", "Reduction:", \n', + ' choices = {prefix}def$dimrd, \n', + ' selected = {prefix}def$dimrd[1]), \n', + ' radioButtons("{prefix}comb_mod_asp", "Aspect ratio:", \n', + ' choices = c("Square", "Fixed", "Free"), \n', + ' selected = "Square", inline = TRUE), \n', + ' checkboxInput("{prefix}comb_mod_txt", "Show axis text on dimred", value = FALSE), \n', + ' radioButtons("{prefix}comb_mod_ord", "Dimred plot order:", \n', + ' choices = c("Max-1st", "Min-1st", "Original", "Random"), \n', + ' selected = "Max-1st", inline = TRUE), \n', + ' radioButtons("{prefix}comb_mod_vln_typ", "Violin/boxplot type:", \n', + ' choices = c("violin", "boxplot"), \n', + ' selected = "violin", inline = TRUE), \n', + ' checkboxInput("{prefix}comb_mod_vln_pts", "Show data points on violin", value = FALSE) \n', + ' ) \n', + ' ), \n', + ' column( \n', + ' 5, style = "border-right: 2px solid black", \n', + ' h4("Module score on reduced dimensions"), \n', + ' uiOutput("{prefix}comb_mod_dr_oup.ui"), \n', + ' fluidRow(column(6, uiOutput("{prefix}comb_mod_leg_oup.ui"))), \n', + ' fluidRow( \n', + ' column(3, downloadButton("{prefix}comb_mod_dr_oup.dl", "Download")), \n', + ' column(3, radioButtons("{prefix}comb_mod_dr_oup.f", "Format:", \n', + ' choices = c("png", "pdf"), selected = "png", inline = TRUE)), \n', + ' column(3, numericInput("{prefix}comb_mod_dr_oup.h", "Height:", \n', + ' min = 4, max = 20, value = 8, step = 0.5)), \n', + ' column(3, numericInput("{prefix}comb_mod_dr_oup.w", "Width:", \n', + ' min = 4, max = 20, value = 8, step = 0.5)) \n', + ' ), \n', + ' br() \n', + ' ), \n', + ' column( \n', + ' 4, \n', + ' h4("Module score violin / boxplot"), \n', + ' uiOutput("{prefix}comb_mod_vln_oup.ui"), \n', + ' fluidRow( \n', + ' column(3, downloadButton("{prefix}comb_mod_vln_oup.dl", "Download")), \n', + ' column(3, radioButtons("{prefix}comb_mod_vln_oup.f", "Format:", \n', + ' choices = c("png", "pdf"), selected = "png", inline = TRUE)), \n', + ' column(3, numericInput("{prefix}comb_mod_vln_oup.h", "Height:", \n', + ' min = 4, max = 20, value = 8, step = 0.5)), \n', + ' column(3, numericInput("{prefix}comb_mod_vln_oup.w", "Width:", \n', + ' min = 4, max = 20, value = 8, step = 0.5)) \n', + ' ), \n', + ' br() \n', + ' ) \n', + ' ) # End of fluidRow (4 space) \n', + ' ) # End of tab (2 space) \n', + ' , \n', + ' \n' + ) +} + #' Write code for ui.R #' #' @rdname wrUImainS1