R/plots.R
8d45c2ac
 #' Plot Fraction Spatial Variance vs Q-value
 #'
56bf569c
 #' @param results results from SpatialDE.
976d698e
 #' @param ms_results model selection results, should be a data frame with
56bf569c
 #'   columns `g` for gene names and `model` for the model selected.
7ee19b9f
 #' @param certain_only only plot results with narrow 95% confidence interval.
8d45c2ac
 #' @param log_x Whether to display x axis in log scale.
976d698e
 #' @param do_label display gene names for statistically significant genes,
56bf569c
 #'   default `TRUE`.
 #' @param covariate_names names of covariates as a reference, default to `NULL`.
 #'
 #' @return A `ggplot2` object.
0cbcea45
 #'
51cde63b
 #' @examples
6b99d290
 #' ## Mock up a SpatialExperiment object wit 400 cells and 3 genes
51cde63b
 #' set.seed(42)
6b99d290
 #' spe <- mockSVG(size = 20, tot_genes = 3, de_genes = 1, return_SPE = TRUE)
 #'
51cde63b
 #' ## Run spatialDE with S4 integration
 #' results <- spatialDE(spe)
6b99d290
 #'
51cde63b
 #' ## Run model search
0cbcea45
 #' msearch <- modelSearch(spe, de_results = results, qval_thresh = NULL,
51cde63b
 #'   verbose = FALSE)
0cbcea45
 #'
51cde63b
 #' plot <- FSV_sig(results, msearch)
976d698e
 #'
56bf569c
 #' @references
 #' Svensson, V., Teichmann, S. & Stegle, O. SpatialDE: identification of
 #' spatially variable genes. Nat Methods 15, 343–346 (2018).
 #' \url{https://doi.org/10.1038/nmeth.4636}
 #'
 #' [**SpatialDE 1.1.3**](https://pypi.org/project/SpatialDE/1.1.3/): the version
 #' of the Python package used under the hood.
 #'
 #' @author Davide Corso, Milan Malfait, Lambda Moses
976d698e
 #'
0cbcea45
 #' @import ggplot2
8d45c2ac
 #' @export
976d698e
 FSV_sig <- function(results, ms_results = NULL, certain_only = FALSE,
56bf569c
                     log_x = FALSE, do_label = TRUE, covariate_names = NULL) {
8d45c2ac
   if (!is.null(ms_results)) {
0cbcea45
       results <- merge(results, ms_results[, c("g", "model")],
                        by = "g", all = TRUE, suffixes = c("", "_bic"))
8d45c2ac
   } else {
0cbcea45
       names(results)[names(results) == "model"] <- "model_bic"
8d45c2ac
   }
0cbcea45
   results$FSV95conf <- 2 * sqrt(results$s2_FSV)
   results$conf_categories <- cut(results$FSV95conf, c(0, 0.1, 1, Inf))
   ll <- levels(results$conf_categories)
   results$conf_categories <- factor(results$conf_categories, levels = rev(ll))
   results$is_covariate <- FALSE
   results$color_categories <- ifelse(results$model_bic == "SE", "general",
                                      ifelse(results$model_bic == "PER",
                                             "periodic", "linear"))
8d45c2ac
   if (!is.null(covariate_names)) {
0cbcea45
       results$is_covariate <- results$g %in% covariate_names
8d45c2ac
   }
   if (certain_only) {
0cbcea45
       results <- results[results$conf_categories == "(0,0.1]",]
8d45c2ac
   }
a1968624
   FSV <- qval <- color_categories <- conf_categories <- is_covariate <-
       g <- NULL
8d45c2ac
   colors_use <- scales::hue_pal()(length(unique(results$model_bic)))
   p <- ggplot(results, aes(FSV, qval)) +
976d698e
       geom_hline(yintercept = 0.05, linetype = 2) +
       scale_color_manual(
           values = colors_use, na.translate = TRUE,
           na.value = "black",
           guide = guide_legend(title = "model")
       ) +
       scale_y_continuous(trans = .reverse_log10()) +
       annotate(geom = "text", x = 0, y = 0.05, label = "0.05")
8d45c2ac
   if (!is.null(covariate_names)) {
976d698e
       p <- p +
           scale_shape_manual(
               values = c(16, 4),
               guide = guide_legend(title = "covariate")
           )
8d45c2ac
   }
   if (!certain_only) {
976d698e
       p <- p +
           geom_point(aes(
               color = color_categories, size = conf_categories,
               shape = is_covariate
           ), alpha = 0.5) +
           scale_size_manual(
               values = c(0.7, 1.5, 3),
               guide = guide_legend(title = "confidence \n category")
           )
8d45c2ac
   } else {
976d698e
       p <- p +
           geom_point(
               aes(color = color_categories, shape = is_covariate),
               alpha = 0.5
           )
8d45c2ac
   }
   if (log_x) {
976d698e
       p <- p +
           scale_x_log10()
8d45c2ac
   }
   if (do_label) {
0cbcea45
       gene_label <- results[results$qval < 0.05, c("FSV", "qval", "g")]
976d698e
       p <- p + ggrepel::geom_label_repel(aes(label = g), data = gene_label)
8d45c2ac
   }
   p
 }
 
072a2550
 # Inverse log scale
 #
 # Custom transform for inverted log transformed axis in ggplot2. 
 # Return a ggplot2 compatible transformation object
8d45c2ac
 .reverse_log10 <- function() {
976d698e
     trans <- function(x) -log10(x)
     inv <- function(x) 10^(-x)
     scales::trans_new("reverse_log10", trans, inv,
         scales::log_breaks(base = 10),
         domain = c(1e-100, Inf)
     )
8d45c2ac
 }
59c9fcdd
 
 
 #' Plot Spatial Patterns of Multiple Genes
 #'
976d698e
 #' @param x A numeric `matrix` of stabilized counts (e.g. resulting from
20c8a43a
 #' [stabilize()]) where genes are rows and cells are columns.
 #'
 #'    Alternatively, a \linkS4class{SpatialExperiment} object.
 #'
 #' @param ... For the generic, arguments to pass to specific methods.
 #' @param coordinates A `data.frame` with sample coordinates. Each row is a
 #'   sample, the columns with coordinates should be named 'x' and 'y'.
 #'
 #'   For the *SpatialExperiment* method, coordinates are taken from
 #'   `spatialCoords(x)`.
 #'
 #' @param assay_type A `character` string specifying the assay from `x` to use
 #'   as input. Defaults to `"counts"`.
59c9fcdd
 #' @param genes_plot character vector specifying which genes are to be plotted.
976d698e
 #' @param viridis_option This function uses the `viridis` palette to color
 #'   cells for gene expression. Four options are available: "magma" (or "A"),
 #'   "inferno" (or "B"), "plasma" (or "C"),
20c8a43a
 #'   "viridis" (or "D", the default option) and "cividis" (or "E").
59c9fcdd
 #' @param ncol Number of columns to arrange the plots.
 #' @param point_size Point size of each plot.
976d698e
 #' @param dark_theme Whether dark background should be used; this is helpful to
59c9fcdd
 #'   highlight cells with high expression when using the \code{viridis} palette.
 #'
 #' @return This function draws a plot for each specified genes
 #'
20c8a43a
 #' @examples
6b99d290
 #' ## Mock up a SpatialExperiment object wit 400 cells and 3 genes
20c8a43a
 #' set.seed(42)
6b99d290
 #' spe <- mockSVG(size = 20, tot_genes = 3, de_genes = 1, return_SPE = TRUE)
20c8a43a
 #'
 #' ## Run spatialDE
 #' results <- spatialDE(spe)
976d698e
 #'
20c8a43a
 #' ordered_spe_results <- results[order(results$qval), ]
 #' head(ordered_spe_results)
976d698e
 #'
 #' plots <- multiGenePlots(spe,
 #'     assay_type = "counts",
6b99d290
 #'     ordered_spe_results$g,
976d698e
 #'     point_size = 4,
 #'     viridis_option = "D"
 #' )
20c8a43a
 #'
 #' @seealso
 #' The individual steps performed by this function: [stabilize()],
 #' [spatialDE()].
 #'
 #' For further analysis of the DE results:
 #' [model_search()] and [spatial_patterns()].
 #'
 #' @references
 #' Svensson, V., Teichmann, S. & Stegle, O. SpatialDE: identification of
 #' spatially variable genes. Nat Methods 15, 343–346 (2018).
 #' \url{https://doi.org/10.1038/nmeth.4636}
 #'
 #' [**SpatialDE 1.1.3**](https://pypi.org/project/SpatialDE/1.1.3/): the version
 #' of the Python package used under the hood.
 #'
 #' @author Davide Corso, Milan Malfait, Lambda Moses
 #' @name multiGenePlots
 NULL
 
59c9fcdd
 #' @importFrom gridExtra grid.arrange
 #' @importFrom checkmate assert_data_frame assert_names
976d698e
 .multiGenePlots <- function(x, coordinates, genes_plot, viridis_option = "D",
     ncol = 2, point_size = 1, dark_theme = TRUE) {
     assert_data_frame(coordinates, any.missing = FALSE)
     assert_names(colnames(coordinates), identical.to = c("x", "y"))
 
     stabilized <- x
a1968624
     y <- NULL
976d698e
     pls <- lapply(
         seq_along(genes_plot),
         function(i) {
             p <- ggplot(
                 data = coordinates,
                 aes(
                     x = x, y = y,
                     color = stabilized[genes_plot[i], ]
                 )
             ) +
                 geom_point(size = point_size) +
                 ggtitle(genes_plot[i]) +
                 scale_color_viridis_c(option = viridis_option) +
                 labs(color = genes_plot[i])
 
             if (dark_theme) {
                 p <- p +
                     theme_dark()
             }
             p
         }
     )
     grid.arrange(grobs = pls, ncol = ncol)
20c8a43a
 }
 
 #' @import methods
 #' @export
 #' @rdname multiGenePlots
 setGeneric("multiGenePlots", function(x, ...) standardGeneric("multiGenePlots"))
 
 #' @export
 #' @rdname multiGenePlots
 setMethod("multiGenePlots", "matrix", .multiGenePlots)
 
 #' @export
 #' @rdname multiGenePlots
 #' @importFrom SummarizedExperiment assay
 #' @importFrom SpatialExperiment spatialCoords spatialCoordsNames
ae407f41
 #' @importFrom basilisk basiliskStart basiliskRun
20c8a43a
 setMethod("multiGenePlots", "SpatialExperiment",
976d698e
     function(x, assay_type = "counts", genes_plot, viridis_option = "D",
              ncol = 2, point_size = 1, dark_theme = TRUE) {
         ## Rename spatialCoords columns to "x", "y"
         spatialCoordsNames(x) <- c("x", "y")
         coordinates <- as.data.frame(spatialCoords(x))
 
ae407f41
         proc <- basiliskStart(spatialDE_env, testload="scipy.optimize")
         # Stabilize
976d698e
         counts <- assay(x, assay_type)
ae407f41
         
         assert_matrix(counts, any.missing = FALSE)
         .naiveDE_stabilize(proc, counts)
         stabilized <- basiliskRun(proc, function(store) {
           as.matrix(store$stabilized)
         }, persist=TRUE)
976d698e
 
         .multiGenePlots(
             x = stabilized,
             coordinates = coordinates,
             genes_plot = genes_plot,
             viridis_option = viridis_option,
             ncol = ncol,
             point_size = point_size,
             dark_theme = dark_theme
         )
     }
20c8a43a
 )