From e5a266beedc2532bf054b42c4efa16c3ebf96cde Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Fri, 15 Nov 2024 09:57:04 -0500 Subject: [PATCH 01/21] fix: account for when no morphology focus imgs found --- R/convenience_xenium.R | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/R/convenience_xenium.R b/R/convenience_xenium.R index 931ce3fa0..ace0e77a3 100644 --- a/R/convenience_xenium.R +++ b/R/convenience_xenium.R @@ -506,12 +506,16 @@ setMethod( focus_dir <- img_focus_path focus_files <- list.files(focus_dir, full.names = TRUE) focus_files <- focus_files[!dir.exists(focus_files)] # ignore matches to export dir - nbound <- length(focus_files) - 1L - focus_names <- c("dapi", sprintf("bound%d", seq_len(nbound))) - names(focus_files) <- focus_names - - # append to rest of entries - load_images <- c(load_images, focus_files) + if (length(focus_files) > 0L) { + nbound <- length(focus_files) - 1L + focus_names <- c( + "dapi", sprintf("bound%d", seq_len(nbound)) + ) + names(focus_files) <- focus_names + + # append to rest of entries + load_images <- c(load_images, focus_files) + } } # ensure that input is list From 92fdce2e3fbaaafe60fe6aef833f562e2e77d74f Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:29:19 -0500 Subject: [PATCH 02/21] fix: focus path detection when no focus directory but image is present --- R/convenience_xenium.R | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/R/convenience_xenium.R b/R/convenience_xenium.R index ace0e77a3..f144b2356 100644 --- a/R/convenience_xenium.R +++ b/R/convenience_xenium.R @@ -497,15 +497,20 @@ setMethod( load_images <- lapply(load_images, normalizePath, mustWork = FALSE) img_focus_path <- normalizePath(img_focus_path, mustWork = FALSE) + # replace shortname + load_images[load_images == "focus"] <- img_focus_path + # [exception] handle focus image dir - is_focus <- load_images == "focus" | load_images == img_focus_path - # split the focus image dir away from other entries - load_images <- load_images[!is_focus] + is_focus_dir <- load_images == img_focus_path & # exists? + dir.exists(img_focus_path) # and is directory? - if (any(is_focus)) { + if (any(is_focus_dir)) { + # split the focus image dir away from other entries + load_images <- load_images[!is_focus_dir] focus_dir <- img_focus_path focus_files <- list.files(focus_dir, full.names = TRUE) - focus_files <- focus_files[!dir.exists(focus_files)] # ignore matches to export dir + # ignore matches to export dir (if it is a subdirectory) + focus_files <- focus_files[!dir.exists(focus_files)] if (length(focus_files) > 0L) { nbound <- length(focus_files) - 1L focus_names <- c( From ede47acd8f8b7f05435db614a0bb52048170c00c Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:15:30 -0500 Subject: [PATCH 03/21] Update convenience_xenium.R --- R/convenience_xenium.R | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/R/convenience_xenium.R b/R/convenience_xenium.R index f144b2356..873f258d0 100644 --- a/R/convenience_xenium.R +++ b/R/convenience_xenium.R @@ -500,10 +500,15 @@ setMethod( # replace shortname load_images[load_images == "focus"] <- img_focus_path - # [exception] handle focus image dir - is_focus_dir <- load_images == img_focus_path & # exists? - dir.exists(img_focus_path) # and is directory? + is_dir <- dir.exists(img_focus_path) + is_focus <- load_images == img_focus_path + is_focus_image <- is_focus & !is_dir + is_focus_dir <- is_focus & is_dir + + # handle matches to single focus images instead of a directory + names(load_images[is_focus_image]) <- "dapi" + # [exception] handle focus image dir if (any(is_focus_dir)) { # split the focus image dir away from other entries load_images <- load_images[!is_focus_dir] From 12412c32570cc7508b888789d7e78b67c2caea5b Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:47:25 -0500 Subject: [PATCH 04/21] fix: list naming indexing --- R/convenience_xenium.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/convenience_xenium.R b/R/convenience_xenium.R index 873f258d0..bf4131822 100644 --- a/R/convenience_xenium.R +++ b/R/convenience_xenium.R @@ -506,7 +506,7 @@ setMethod( is_focus_dir <- is_focus & is_dir # handle matches to single focus images instead of a directory - names(load_images[is_focus_image]) <- "dapi" + names(load_images)[is_focus_image] <- "dapi" # [exception] handle focus image dir if (any(is_focus_dir)) { From a13453e61965314f21dd786f59d51ec62581553a Mon Sep 17 00:00:00 2001 From: Junxiang Xu Date: Tue, 19 Nov 2024 02:59:11 -0500 Subject: [PATCH 05/21] segmentations --- NAMESPACE | 2 + R/cell_segmentation.R | 185 ++++++++++++++++++ .../configuration/genv_segmentation.yml | 23 +++ man/doMesmerSegmentation.Rd | 50 +++++ man/doStardistSegmentation.Rd | 53 +++++ 5 files changed, 313 insertions(+) create mode 100644 inst/python/configuration/genv_segmentation.yml create mode 100644 man/doMesmerSegmentation.Rd create mode 100644 man/doStardistSegmentation.Rd diff --git a/NAMESPACE b/NAMESPACE index 8aca099c2..1c8052de4 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -177,9 +177,11 @@ export(doLeidenClusterIgraph) export(doLeidenSubCluster) export(doLouvainCluster) export(doLouvainSubCluster) +export(doMesmerSegmentation) export(doRandomWalkCluster) export(doSNNCluster) export(doScrubletDetect) +export(doStardistSegmentation) export(estimateAutomatedImageRegistrationWithSIFT) export(estimateImageBg) export(exportGiottoViewer) diff --git a/R/cell_segmentation.R b/R/cell_segmentation.R index d7c7560c5..81e1a85f6 100644 --- a/R/cell_segmentation.R +++ b/R/cell_segmentation.R @@ -240,3 +240,188 @@ doCellposeSegmentation <- function( rast <- terra::rast(masks) terra::writeRaster(rast, mask_output, overwrite = TRUE) } + + + + +#' +#' @title perform Mesmer(Deepcell) segmentation +#' @description +#' +#' perform the Giotto Wrapper of mesmer segmentation. This is for a model +#' inference to generate segmentation mask file from input image. +#' main parameters needed +#' @name doMesmerSegmentation +#' @param Image_dir character, required. Provide a path to a IF image. +#' @param python_env python environment with deepcell installed. +#' default = "giotto_segmentation". See deepcell official website for more details. +#' @param mask_output required. Provide a path to the output mask file. +#' @param Nucleus_channel channel number for Nuclei, default to 1 +#' @param Memberane_channel channel number for cell boundary, default to 2 +#' @param pixel_per_micron physical micron size per pixel, default to 0.25 +#' @returns No return variable, as this will write directly to output path +#' provided. +#' @examples +#' # example code +#' doMesmerSegmentation( +#' Image_dir = input_image, +#' mask_output = output, +#' Nucleus_channel = 1, +#' Memberane_channel = 2, +#' pixel_per_micron = 0.5 +#' ) +#' @export +doMesmerSegmentation <- function(Image_dir, + python_env = 'giotto_segmentation', + Nucleus_channel = 1, + Memberane_channel = 2, + pixel_per_micron = 0.25, + mask_output, + verbose = F, ...){ + ## Load required python libraries + GiottoClass::set_giotto_python_path(python_env) + GiottoUtils::package_check("deepcell", repository = "pip") + tiff <- reticulate::import("tifffile") + np <- reticulate::import("numpy") + deepcell <- reticulate::import("deepcell.applications") + message("successfully loaded giotto environment with deepcell.") + + # Initialize the Mesmer application from DeepCell + mesmer <- deepcell$Mesmer() + + GiottoUtils::vmsg( + .v = verbose, .is_debug = FALSE, "Loading Image... ", + ) + GiottoUtils::package_check("terra") + rast = terra::rast(Image_dir) + # Convert the R matrix to a NumPy array explicitly + Nucleus_channel_np <- np$array(drop(terra::as.array(rast[[as.numeric(Nucleus_channel)]]))) + Membrane_channel_np <- np$array(drop(terra::as.array(rast[[as.numeric(Memberane_channel)]]))) + stacked_array <- np$stack(list(Nucleus_channel_np, Membrane_channel_np), axis = as.integer(-1)) + # Add a new axis to the stacked array to fit Mesmer input + stacked_array <- np$expand_dims(stacked_array, axis = as.integer(0)) + + GiottoUtils::vmsg(.v = verbose, .is_debug = FALSE, "Segmenting Image...") + + segmentation_predictions = mesmer$predict(stacked_array, image_mpp=pixel_per_micron) + mask <- segmentation_predictions[1,,,1] + mask_r <- reticulate::py_to_r(mask) + + GiottoUtils::vmsg( + .v = verbose, .is_debug = FALSE, + "Segmentation finished... Saving mask file..." + ) + + rast <- terra::rast(mask_r) + terra::writeRaster(rast, mask_output, overwrite = TRUE) +} + + + +#' +#' @title perform Stardist segmentation +#' @description +#' +#' perform the Giotto Wrapper of Stardist 2D segmentation. This is for a model +#' inference to generate segmentation mask file from input image. +#' main parameters needed +#' @name doStardistSegmentation +#' @param Image_dir character, required. Provide a path to an image. +#' @param python_env python environment with Stardist installed. +#' default = "giotto_segmentation". See Stardist official website for more details. +#' @param mask_output required. Provide a path to the output mask file. +#' @param model_name Name of the model to run inference. Default to '2D_versatile_fluo'. +#' If using HE model, input image must be RGB, else the nuclei_channel must be given +#' @param nuclei_channel Required using IF based nuclei segmentation, channel number of the nuclei staining. +#' @param prob_thresh prob_thresh for model (if not given use model default) +#' @param nms_thresh nms_thresh for model (if not given use model default) +#' @returns No return variable, as this will write directly to output path provided. +#' @examples +#' # example code +#' doStardistSegmentation( +#' Image_dir = input_image, +#' mask_output = output, +#' model_name = '2D_versatile_fluo', +#' nuclei_channel = 3 +#' ) +#' +#' @export +doStardistSegmentation <- function(Image_dir, + python_env = 'giotto_segmentation', + mask_output, + model_name = '2D_versatile_fluo', + nuclei_channel = NULL, + prob_thresh = NULL, + nms_thresh = NULL, + verbose = F, + ...){ + # Import the necessary Python modules + ## Load required python libraries + GiottoClass::set_giotto_python_path(python_env) + GiottoUtils::package_check("stardist", repository = "pip") + stardist <- reticulate::import("stardist.models") + csbdeep <- reticulate::import("csbdeep.utils") + np <- reticulate::import("numpy") + + # Load the StarDist2D model + model_name <- match.arg( + model_name, unique(c("2D_versatile_fluo", "2D_versatile_he", "2D_paper_dsb2018", "2D_demo", model_name)) + ) + GiottoUtils::vmsg( + .v = verbose, .is_debug = FALSE, "Loading model ", + model_name + ) + + model <- stardist$StarDist2D$from_pretrained(model_name) + + # Load the image + GiottoUtils::vmsg( + .v = verbose, .is_debug = FALSE, "Loading Image from ", + Image_dir + ) + GiottoUtils::package_check("terra") + rast = terra::rast(Image_dir) + if (model_name != '2D_versatile_he' & is.null(nuclei_channel)){ + stop('using IF based nuclei segmentation, please specify nuclei channel') + } + else if( model_name == '2D_versatile_he'){ + img <- np$array(terra::as.array(rast)) + } + else { + img <- np$array(drop(terra::as.array(rast[[as.numeric(nuclei_channel)]]))) + } + + # Normalize the image + normalized_img <- csbdeep$normalize(img) + + # Perform prediction with StarDist2D model + results <- model$predict_instances(normalized_img, + prob_thresh = prob_thresh, + nms_thresh = nms_thresh) + + # Extract the labels (first output from predict_instances) + mask <- results[[1]] + GiottoUtils::vmsg( + .v = verbose, .is_debug = FALSE, + "Segmentation finished... Saving mask file..." + ) + rast_m <- terra::rast(mask) + terra::writeRaster(rast_m, mask_output, overwrite = TRUE) +} + + + + + + + + + + + + + + + + + diff --git a/inst/python/configuration/genv_segmentation.yml b/inst/python/configuration/genv_segmentation.yml new file mode 100644 index 000000000..77572f481 --- /dev/null +++ b/inst/python/configuration/genv_segmentation.yml @@ -0,0 +1,23 @@ +name: giotto_segmentation +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - python=3.8 + - pip + - numpy + - pandas + - networkx + - python-igraph + - leidenalg + - scikit-learn + - tifffile + - pip: + - cellpose + - deepcell + - stardist + - python-louvain + - smfishHmrf + - git+https://github.com/wwang-chcn/bento-tools.git@giotto_install + diff --git a/man/doMesmerSegmentation.Rd b/man/doMesmerSegmentation.Rd new file mode 100644 index 000000000..b300e775a --- /dev/null +++ b/man/doMesmerSegmentation.Rd @@ -0,0 +1,50 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cell_segmentation.R +\name{doMesmerSegmentation} +\alias{doMesmerSegmentation} +\title{perform Mesmer(Deepcell) segmentation} +\usage{ +doMesmerSegmentation( + Image_dir, + python_env = "giotto_segmentation", + Nucleus_channel = 1, + Memberane_channel = 2, + pixel_per_micron = 0.25, + mask_output, + verbose = F, + ... +) +} +\arguments{ +\item{Image_dir}{character, required. Provide a path to a IF image.} + +\item{python_env}{python environment with deepcell installed. +default = "giotto_segmentation". See deepcell official website for more details.} + +\item{Nucleus_channel}{channel number for Nuclei, default to 1} + +\item{Memberane_channel}{channel number for cell boundary, default to 2} + +\item{pixel_per_micron}{physical micron size per pixel, default to 0.25} + +\item{mask_output}{required. Provide a path to the output mask file.} +} +\value{ +No return variable, as this will write directly to output path +provided. +} +\description{ +perform the Giotto Wrapper of mesmer segmentation. This is for a model +inference to generate segmentation mask file from input image. +main parameters needed +} +\examples{ +# example code +doMesmerSegmentation( + Image_dir = input_image, + mask_output = output, + Nucleus_channel = 1, + Memberane_channel = 2, + pixel_per_micron = 0.5 +) +} diff --git a/man/doStardistSegmentation.Rd b/man/doStardistSegmentation.Rd new file mode 100644 index 000000000..dbe02c9d5 --- /dev/null +++ b/man/doStardistSegmentation.Rd @@ -0,0 +1,53 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cell_segmentation.R +\name{doStardistSegmentation} +\alias{doStardistSegmentation} +\title{perform Stardist segmentation} +\usage{ +doStardistSegmentation( + Image_dir, + python_env = "giotto_segmentation", + mask_output, + model_name = "2D_versatile_fluo", + nuclei_channel = NULL, + prob_thresh = NULL, + nms_thresh = NULL, + verbose = F, + ... +) +} +\arguments{ +\item{Image_dir}{character, required. Provide a path to an image.} + +\item{python_env}{python environment with Stardist installed. +default = "giotto_segmentation". See Stardist official website for more details.} + +\item{mask_output}{required. Provide a path to the output mask file.} + +\item{model_name}{Name of the model to run inference. Default to '2D_versatile_fluo'. +If using HE model, input image must be RGB, else the nuclei_channel must be given} + +\item{nuclei_channel}{Required using IF based nuclei segmentation, channel number of the nuclei staining.} + +\item{prob_thresh}{prob_thresh for model (if not given use model default)} + +\item{nms_thresh}{nms_thresh for model (if not given use model default)} +} +\value{ +No return variable, as this will write directly to output path provided. +} +\description{ +perform the Giotto Wrapper of Stardist 2D segmentation. This is for a model +inference to generate segmentation mask file from input image. +main parameters needed +} +\examples{ +# example code +doStardistSegmentation( + Image_dir = input_image, + mask_output = output, + model_name = '2D_versatile_fluo', + nuclei_channel = 3 +) + +} From 9138988261272492daa86de5071e1896e92f8f85 Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Wed, 20 Nov 2024 12:08:00 -0500 Subject: [PATCH 06/21] change scrublet seed setting --- R/python_scrublet.R | 11 +++++------ inst/python/python_scrublet.py | 5 +++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/R/python_scrublet.R b/R/python_scrublet.R index 0f422ceae..2740134f0 100644 --- a/R/python_scrublet.R +++ b/R/python_scrublet.R @@ -73,11 +73,9 @@ doScrubletDetect <- function( # set seed if (!is.null(seed)) { - seed_number <- as.numeric(seed) - reticulate::py_set_seed( - seed = seed_number, - disable_hash_randomization = TRUE - ) + seed_number <- as.integer(seed) + } else { + seed_number <- random_seed() } # Set feat_type and spat_unit @@ -116,7 +114,8 @@ doScrubletDetect <- function( min_counts = min_counts, min_cells = min_cells, min_gene_variability_pctl = min_gene_variability_pctl, - n_prin_comps = n_prin_comps + n_prin_comps = n_prin_comps, + seed_number = seed_number ) scrublet_out <- data.table::data.table( diff --git a/inst/python/python_scrublet.py b/inst/python/python_scrublet.py index 4b856b53d..faedcc0e5 100644 --- a/inst/python/python_scrublet.py +++ b/inst/python/python_scrublet.py @@ -1,13 +1,14 @@ import scrublet as scr -def python_scrublet(counts_matrix, expected_doublet_rate, min_counts, min_cells, min_gene_variability_pctl, n_prin_comps): +def python_scrublet(counts_matrix, expected_doublet_rate, min_counts, min_cells, min_gene_variability_pctl, n_prin_comps, seed_number=1234): min_counts = int(min_counts) min_cells = int(min_cells) min_gene_variability_pctl = int(min_gene_variability_pctl) n_prin_comps = int(n_prin_comps) + random_state = int(seed_number) - scrub = scr.Scrublet(counts_matrix=counts_matrix, expected_doublet_rate=expected_doublet_rate) + scrub = scr.Scrublet(counts_matrix=counts_matrix, expected_doublet_rate=expected_doublet_rate, random_state=random_state) doublet_scores, predicted_doublets = scrub.scrub_doublets(min_counts=min_counts,min_cells=min_cells, min_gene_variability_pctl=min_gene_variability_pctl, n_prin_comps=n_prin_comps) return_list = [] From e3464a280e056d51007595cc8664599f826498ad Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Wed, 20 Nov 2024 14:48:33 -0500 Subject: [PATCH 07/21] allow access to transcript datatable --- R/convenience_xenium.R | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/R/convenience_xenium.R b/R/convenience_xenium.R index 9dc52cfc9..506410479 100644 --- a/R/convenience_xenium.R +++ b/R/convenience_xenium.R @@ -238,6 +238,7 @@ setMethod( dropcols = c(), qv_threshold = obj@qv, cores = determine_cores(), + output = c("giottoPoints", "data.table"), verbose = NULL) { .xenium_transcript( path = path, @@ -247,6 +248,7 @@ setMethod( dropcols = dropcols, qv_threshold = qv_threshold, cores = cores, + output = output, verbose = verbose ) } @@ -690,6 +692,7 @@ importXenium <- function(xenium_dir = NULL, qv_threshold = 20) { dropcols = c(), qv_threshold = 20, cores = determine_cores(), + output = c("giottoPoints", "data.table"), verbose = NULL) { if (missing(path)) { stop(wrap_txt( @@ -704,6 +707,8 @@ importXenium <- function(xenium_dir = NULL, qv_threshold = 20) { vmsg(.v = verbose, .is_debug = TRUE, "[TX_READ] FMT =", e) vmsg(.v = verbose, .is_debug = TRUE, path) + output <- match.arg(output, choices = c("giottoPoints", "data.table")) + # read in as data.table a <- list( path = path, @@ -726,6 +731,8 @@ importXenium <- function(xenium_dir = NULL, qv_threshold = 20) { y <- NULL # NSE var if (flip_vertical) tx[, y := -y] + if (output == "data.table") return(tx) + # create gpoints gpointslist <- createGiottoPoints( x = tx, @@ -734,7 +741,8 @@ importXenium <- function(xenium_dir = NULL, qv_threshold = 20) { verbose = FALSE ) - if (inherits(gpointslist, "list")) { + # enforce list + if (!inherits(gpointslist, "list")) { gpointslist <- list(gpointslist) } From ebff4234adc3116d928a6c5a0ba41fc6072d1710 Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Thu, 21 Nov 2024 14:11:05 -0500 Subject: [PATCH 08/21] refactor: var explained New internals - .varexp() - variance explained per PC - .cumvar() - cumulative variance per PC --- R/dimension_reduction.R | 55 +++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/R/dimension_reduction.R b/R/dimension_reduction.R index 8bd3bccaa..3b26e77b1 100644 --- a/R/dimension_reduction.R +++ b/R/dimension_reduction.R @@ -1928,24 +1928,9 @@ jackstrawPlot <- function( } n <- ncol(dat) m <- nrow(dat) - ndf <- min(m, n - 1, ncp) # this is a limitation of svd singular values - sum_of_squared_singular_vals <- sum(dat^2) - - # pick SVD fun based on whether partial or full is appropriate - # These biocsingular functions should not scale or center - svd_fun <- if (ndf >= 0.5 * m || ndf >= 100) { - BiocSingular::runExactSVD - } else { - BiocSingular::runIrlbaSVD - } # partial SVDs - - .calc_svd_var_explained <- function(x, k) { - res <- svd_fun(x, k = k) - singular_val_square <- res$d[1:k]^2 - return(singular_val_square / sum_of_squared_singular_vals) - } - - dstat <- .calc_svd_var_explained(dat, k = ndf) + ndf <- min(m, n - 1, ncp) # this is also calculated in .varexp + + dstat <- .varexp(dat, k = ncp) cum_var_explained <- cumsum(dstat) # randomize and compare @@ -1961,7 +1946,7 @@ jackstrawPlot <- function( for (i in seq_len(iter)) { pb() dat0 <- t(apply(dat, 1, sample)) - dstat0[i, ] <- .calc_svd_var_explained(dat0, k = ndf) + dstat0[i, ] <- .varexp(dat0, k = ncp) } }) @@ -1977,7 +1962,39 @@ jackstrawPlot <- function( return(list(r = r, p = p, cum_var_explained = cum_var_explained)) } +# calculate SVD variance explained, with support for partial SVDs +.varexp <- function(dat, k = 20) { + if (missing(dat)) { + stop("`dat` is required!") + } + n <- ncol(dat) + m <- nrow(dat) + ndf <- min(m, n - 1, k) # this is a limitation of svd singular values + sum_of_squared_singular_vals <- sum(dat^2) + + # pick SVD fun based on whether partial or full is appropriate + # These biocsingular functions should not scale or center + svd_fun <- if (ndf >= 0.5 * m || ndf >= 100) { + BiocSingular::runExactSVD + } else { + BiocSingular::runIrlbaSVD + } # partial SVDs + + res <- svd_fun(dat, k) + singular_val_square <- res$d[1:k]^2 + perc <- singular_val_square / sum_of_squared_singular_vals + return(perc) +} +# cumulative variance explained +.cumvar <- function(dat, k = 20, last = TRUE) { + a <- get_args_list(keep = c("dat", "k")) + res <- cumsum(do.call(.varexp, a)) + if (last) { + res <- tail(res, 1L) + } + return(res) +} #' @title signPCA From 4d29198a554ef6d802fbb408d96d5918e66a09f1 Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Fri, 22 Nov 2024 03:10:40 -0500 Subject: [PATCH 09/21] enh: labelTransfer with integration --- R/clustering.R | 178 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 163 insertions(+), 15 deletions(-) diff --git a/R/clustering.R b/R/clustering.R index 7cc71d97c..c7d41257c 100644 --- a/R/clustering.R +++ b/R/clustering.R @@ -3309,12 +3309,18 @@ getDendrogramSplits <- function( #' one of the sets to the other based on KNN similarity voting in that space. #' @param x target object #' @param y source object +#' @param spat_unit spatial unit. A character vector of 2 can also be passed +#' for x (1) and y (2). Setting defaults with `activeSpatUnit()` may be easier +#' @param feat_type feature type. A character vector of 2 can also be passed +#' for x (1) and y (2). Setting defaults with `activeFeatType()` may be easier #' @param source_cell_ids cell/spatial IDs with the source labels to transfer #' @param target_cell_ids cell/spatial IDs to transfer the labels to. #' IDs from `source_cell_ids` are always included as well. #' @param labels metadata column in source with labels to transfer #' @param k number of k-neighbors to train a KNN classifier #' @param name metadata column in target to apply the full set of labels to +#' @param integration_method character. Integration method to use when +#' transferring labels. Options are "none" (default) and "harmony". #' @param prob output knn probabilities together with label predictions #' @param reduction reduction on cells or features (default = "cells") #' @param reduction_method shared reduction method (default = "pca" space) @@ -3364,45 +3370,187 @@ setGeneric( function(x, y, ...) standardGeneric("labelTransfer") ) + +.lab_trnsfr_harmony <- function(x, y, + expression_values = "raw", + dimensions_to_use = 1:10, + spat_unit = NULL, + feat_type = NULL, + filter_params = list( + expression_threshold = 1, + feat_det_in_min_cells = 1, + min_det_feats_per_cell = 10 + ), + normalize_params = list(), + use_hvf = TRUE, + pca_params = list(), + integration_params = list(), + ... # passes to labelTransfer +) { + # NSE vars + cell_ID <- transfer <- transfer_prob <- NULL + + # match features + ufids <- intersect(featIDs(x), featIDs(y)) + if (length(ufids) == 0L) { + stop("labelTransfer harmony: No common features between `x` and `y`", + call. = FALSE) + } + + # get needed subobjects + xdata <- x[[ + c("expression", "spatial_locs"), + expression_values, + spat_unit = spat_unit[[1]], + feat_type = feat_type[[1]], + ]] + ydata <- y[[ + c("expression", "spatial_locs"), + expression_values, + spat_unit = spat_unit[[2]], + feat_type = feat_type[[2]], + ]] + ymeta <- y[[ + "cell_metadata", + spat_unit = spat_unit[[2]], + feat_type = feat_type[[2]] + ]] + + # harmonize nesting + objName(xdata) <- rep("join", length(xdata)) + objName(ydata) <- rep("join", length(ydata)) + ydata <- c(ydata, ymeta) + spatUnit(xdata) <- rep("join", length(xdata)) + featType(xdata) <- rep("join", length(xdata)) + spatUnit(ydata) <- rep("join", length(ydata)) + featType(ydata) <- rep("join", length(ydata)) + + # generate temp objects + xj <- setGiotto(giotto(initialize = FALSE), xdata, verbose = FALSE) + yj <- setGiotto(giotto(initialize = FALSE), ydata, verbose = FALSE) + + # join on intersected feats + j <- joinGiottoObjects( + list(xj[ufids], yj[ufids]), + gobject_names = c("x", "y") + ) + + # process + j <- do.call( + normalizeGiotto, args = c(list(gobject = j), normalize_params) + ) + if (use_hvf) { + j <- calculateHVF(j) + pca_params$feats_to_use = pca_params$feats_to_use %null% "hvf" + } else { + pca_params$feats_to_use = NULL + } + j <- do.call(runPCA, args = c(list(gobject = j), pca_params)) + + # TODO determine dims to use via cumvar >= 30% + + # harmony + integration_params$name <- "harmony" + integration_params$vars_use = "list_ID" + integration_params$dim_reduction_name = "pca" + integration_params$dimensions_to_use = dimensions_to_use + + j <- do.call(runGiottoHarmony, c(list(gobject = j), integration_params)) + + transfer_args <- list( + x = j, + source_cell_ids = spatIDs(j, subset = list_ID == "y"), + name = "transfer", + dimensions_to_use = dimensions_to_use, + reduction = "cells", + reduction_method = "harmony", + reduction_name = "harmony", + ... + ) + j <- do.call(labelTransfer, transfer_args) + + res <- pDataDT(j) + res <- res[list_ID == "x"] + res[, cell_ID := gsub("^x-", "", cell_ID)] + res <- res[, .(cell_ID, transfer, transfer_prob)] + + x <- addCellMetadata(x, + new_metadata = res, + by_column = TRUE, + column_cell_ID = "cell_ID" + ) + return(x) +} + + + #' @rdname labelTransfer #' @export setMethod("labelTransfer", signature(x = "giotto", y = "giotto"), function( x, y, - spat_unit = NULL, - feat_type = NULL, labels, k = 10, name = paste0("trnsfr_", labels), + integration_method = c("none", "harmony"), prob = TRUE, reduction = "cells", reduction_method = "pca", reduction_name = "pca", dimensions_to_use = 1:10, + spat_unit = NULL, + feat_type = NULL, return_gobject = TRUE, ...) { - # NSE vars - temp_name <- cell_ID <- temp_name_prob <- NULL package_check(pkg_name = "FNN", repository = "CRAN") - spat_unit <- set_default_spat_unit(x, spat_unit = spat_unit) - feat_type <- set_default_feat_type(x, - spat_unit = spat_unit, feat_type = feat_type + + integration_method <- match.arg(integration_method, choices = c( + "none", "harmony" + )) + + if (!labels %in% colnames(pDataDT(y))) { + stop("`labels` not found in cell metadata of `y`", call. = FALSE) + } + + # norm su and ft lengths + if (length(spat_unit) == 1L) { + spat_unit <- rep(spat_unit, 2L) + } + if (length(feat_type) == 1L) { + feat_type <- rep(feat_type, 2L) + } + + if (integration_method == "harmony") { + a <- get_args_list(...) + return(.lab_transfr_harmony(...)) + } + + # NSE vars + temp_name <- cell_ID <- temp_name_prob <- NULL + + su1 <- set_default_spat_unit(x, spat_unit = spat_unit[[1]]) + su2 <- set_default_spat_unit(y, spat_unit = spat_unit[[2]]) + ft1 <- set_default_feat_type( + x, spat_unit = su1, feat_type = feat_type[[1]] + ) + ft2 <- set_default_feat_type( + y, spat_unit = su2, feat_type = feat_type[[2]] ) # get data cx_src <- getCellMetadata(y, - spat_unit = spat_unit, - feat_type = feat_type, + spat_unit = su2, + feat_type = ft2, output = "data.table" ) cx_tgt <- getCellMetadata(x, - spat_unit = spat_unit, - feat_type = feat_type, + spat_unit = su1, + feat_type = ft1, output = "data.table" ) dim_coord <- getDimReduction(x, - spat_unit = spat_unit, - feat_type = feat_type, + spat_unit = su1, + feat_type = ft1, reduction = reduction, reduction_method = reduction_method, name = reduction_name, @@ -3479,8 +3627,8 @@ setMethod("labelTransfer", signature(x = "giotto", y = "giotto"), function( if (return_gobject) { x <- addCellMetadata(x, - spat_unit = spat_unit, - feat_type = feat_type, + spat_unit = su1, + feat_type = ft1, new_metadata = cx_tgt, by_column = TRUE, column_cell_ID = "cell_ID" From 5035de8452047f45a119e09c20711f73258c8c0d Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Fri, 22 Nov 2024 03:11:37 -0500 Subject: [PATCH 10/21] chore: docs --- man/labelTransfer.Rd | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/man/labelTransfer.Rd b/man/labelTransfer.Rd index dbf876a0b..e1d13ce8b 100644 --- a/man/labelTransfer.Rd +++ b/man/labelTransfer.Rd @@ -10,16 +10,17 @@ voting} \S4method{labelTransfer}{giotto,giotto}( x, y, - spat_unit = NULL, - feat_type = NULL, labels, k = 10, name = paste0("trnsfr_", labels), + integration_method = c("none", "harmony"), prob = TRUE, reduction = "cells", reduction_method = "pca", reduction_name = "pca", dimensions_to_use = 1:10, + spat_unit = NULL, + feat_type = NULL, return_gobject = TRUE, ... ) @@ -53,6 +54,9 @@ voting} \item{name}{metadata column in target to apply the full set of labels to} +\item{integration_method}{character. Integration method to use when +transferring labels. Options are "none" (default) and "harmony".} + \item{prob}{output knn probabilities together with label predictions} \item{reduction}{reduction on cells or features (default = "cells")} @@ -64,6 +68,12 @@ voting} \item{dimensions_to_use}{dimensions to use in shared reduction space (default = 1:10)} +\item{spat_unit}{spatial unit. A character vector of 2 can also be passed +for x (1) and y (2). Setting defaults with \code{activeSpatUnit()} may be easier} + +\item{feat_type}{feature type. A character vector of 2 can also be passed +for x (1) and y (2). Setting defaults with \code{activeFeatType()} may be easier} + \item{...}{ Arguments passed on to \code{\link[FNN:knn]{FNN::knn}} \describe{ From be6ad66cd1e804704dd49b9f2868a4e45b18345f Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Sun, 24 Nov 2024 21:22:41 -0500 Subject: [PATCH 11/21] chore: fix typo --- R/clustering.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/clustering.R b/R/clustering.R index c7d41257c..1b78563ec 100644 --- a/R/clustering.R +++ b/R/clustering.R @@ -3371,7 +3371,7 @@ setGeneric( ) -.lab_trnsfr_harmony <- function(x, y, +.lab_transfer_harmony <- function(x, y, expression_values = "raw", dimensions_to_use = 1:10, spat_unit = NULL, @@ -3522,7 +3522,7 @@ setMethod("labelTransfer", signature(x = "giotto", y = "giotto"), function( if (integration_method == "harmony") { a <- get_args_list(...) - return(.lab_transfr_harmony(...)) + return(.lab_transfer_harmony(...)) } # NSE vars From 4b22026801e35c92606fe66e41ecea226497e849 Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Sun, 24 Nov 2024 22:20:59 -0500 Subject: [PATCH 12/21] fix param passing --- R/clustering.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/clustering.R b/R/clustering.R index 1b78563ec..9222374db 100644 --- a/R/clustering.R +++ b/R/clustering.R @@ -3522,7 +3522,7 @@ setMethod("labelTransfer", signature(x = "giotto", y = "giotto"), function( if (integration_method == "harmony") { a <- get_args_list(...) - return(.lab_transfer_harmony(...)) + return(do.call(.lab_transfer_harmony, a)) } # NSE vars From bfc8d26e4dcb3b9c2132a6e199f46172400ddf5d Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Tue, 26 Nov 2024 09:24:19 -0500 Subject: [PATCH 13/21] Update clustering.R --- R/clustering.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/clustering.R b/R/clustering.R index 9222374db..0bcdf57d1 100644 --- a/R/clustering.R +++ b/R/clustering.R @@ -3399,13 +3399,13 @@ setGeneric( # get needed subobjects xdata <- x[[ - c("expression", "spatial_locs"), + c("expression"), expression_values, spat_unit = spat_unit[[1]], feat_type = feat_type[[1]], ]] ydata <- y[[ - c("expression", "spatial_locs"), + c("expression"), expression_values, spat_unit = spat_unit[[2]], feat_type = feat_type[[2]], From 74826a7628e4b5c0ae96e2f7179d611640c36ed5 Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Wed, 4 Dec 2024 15:17:19 -0500 Subject: [PATCH 14/21] fix: prevent drop during matrix indexing --- R/spatial_enrichment.R | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/R/spatial_enrichment.R b/R/spatial_enrichment.R index 8864acc02..ec2852a73 100644 --- a/R/spatial_enrichment.R +++ b/R/spatial_enrichment.R @@ -2279,7 +2279,7 @@ enrich_deconvolution <- function( ct_exp, cutoff) { ##### generate enrich 0/1 matrix based on expression matrix - ct_exp <- ct_exp[rowSums(ct_exp) > 0, ] + ct_exp <- ct_exp[rowSums(ct_exp) > 0, , drop = FALSE] enrich_matrix <- matrix(0, nrow = dim(ct_exp)[1], ncol = dim(ct_exp)[2]) rowmax_col <- Rfast::rowMaxs(ct_exp) for (i in seq_along(rowmax_col)) { @@ -2321,9 +2321,9 @@ enrich_deconvolution <- function( ct_gene <- c(ct_gene, sig_gene_j) } uniq_ct_gene <- intersect(rownames(expr), unique(ct_gene)) - select_sig_exp <- ct_exp[uniq_ct_gene, ct] + select_sig_exp <- ct_exp[uniq_ct_gene, ct, drop = FALSE] cluster_i_cell <- which(cluster_info == cluster_sort[i]) - cluster_cell_exp <- expr[uniq_ct_gene, cluster_i_cell] + cluster_cell_exp <- expr[uniq_ct_gene, cluster_i_cell, drop = FALSE] cluster_i_dwls <- optimize_deconvolute_dwls( cluster_cell_exp, select_sig_exp @@ -2383,9 +2383,9 @@ spot_deconvolution <- function( ct_gene <- c(ct_gene, sig_gene_j) } uniq_ct_gene <- intersect(rownames(expr), unique(ct_gene)) - select_sig_exp <- ct_exp[uniq_ct_gene, ct_i] + select_sig_exp <- ct_exp[uniq_ct_gene, ct_i, drop = FALSE] cluster_i_cell <- which(cluster_info == cluster_sort[i]) - cluster_cell_exp <- expr[uniq_ct_gene, cluster_i_cell] + cluster_cell_exp <- expr[uniq_ct_gene, cluster_i_cell, drop = FALSE] ###### calculate ###### overlap signature with spatial genes all_exp <- Matrix::rowMeans(cluster_cell_exp) From 11d2880dc424b31c9dd34ecb023c5de4e243b9ba Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Sun, 8 Dec 2024 03:46:28 -0500 Subject: [PATCH 15/21] enh: labelTransfer() harmony integration --- DESCRIPTION | 2 +- R/clustering.R | 120 +++++++++++++++++++++++++++++++++++++------------ R/normalize.R | 6 ++- 3 files changed, 96 insertions(+), 32 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index fccb7569a..5d2293a15 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -28,7 +28,7 @@ RoxygenNote: 7.3.2 Depends: R (>= 4.4.1), methods, - GiottoClass (>= 0.4.1) + GiottoClass (>= 0.4.5) Imports: BiocParallel, BiocSingular, diff --git a/R/clustering.R b/R/clustering.R index 0bcdf57d1..687069816 100644 --- a/R/clustering.R +++ b/R/clustering.R @@ -3385,27 +3385,35 @@ setGeneric( use_hvf = TRUE, pca_params = list(), integration_params = list(), + verbose = NULL, ... # passes to labelTransfer ) { # NSE vars cell_ID <- transfer <- transfer_prob <- NULL + # turn off lower level updates + options("giotto.update_param" = FALSE) + on.exit({ + options("giotto.update_param" = TRUE) + }, add = TRUE) + + vmsg(.v = verbose, "1. Creating temporary joined object...") # match features ufids <- intersect(featIDs(x), featIDs(y)) if (length(ufids) == 0L) { stop("labelTransfer harmony: No common features between `x` and `y`", call. = FALSE) } - + # get needed subobjects xdata <- x[[ - c("expression"), + "expression", expression_values, spat_unit = spat_unit[[1]], feat_type = feat_type[[1]], ]] ydata <- y[[ - c("expression"), + "expression", expression_values, spat_unit = spat_unit[[2]], feat_type = feat_type[[2]], @@ -3417,34 +3425,72 @@ setGeneric( ]] # harmonize nesting - objName(xdata) <- rep("join", length(xdata)) - objName(ydata) <- rep("join", length(ydata)) + objName(xdata) <- rep("raw", length(xdata)) + objName(ydata) <- rep("raw", length(ydata)) ydata <- c(ydata, ymeta) - spatUnit(xdata) <- rep("join", length(xdata)) - featType(xdata) <- rep("join", length(xdata)) - spatUnit(ydata) <- rep("join", length(ydata)) - featType(ydata) <- rep("join", length(ydata)) + spatUnit(xdata) <- rep("cell", length(xdata)) + featType(xdata) <- rep("rna", length(xdata)) + spatUnit(ydata) <- rep("cell", length(ydata)) + featType(ydata) <- rep("rna", length(ydata)) - # generate temp objects - xj <- setGiotto(giotto(initialize = FALSE), xdata, verbose = FALSE) - yj <- setGiotto(giotto(initialize = FALSE), ydata, verbose = FALSE) + dummy_sl_x <- data.table::data.table( + cell_ID = colnames(xdata[[1]][]), + sdimx = 0, sdimy = 0 + ) |> + createSpatLocsObj( + name = "raw", spat_unit = "cell", verbose = FALSE + ) + dummy_sl_y <- data.table::data.table( + cell_ID = colnames(ydata[[1]][]), + sdimx = 0, sdimy = 0 + ) |> + createSpatLocsObj( + name = "raw", spat_unit = "cell", verbose = FALSE + ) + dummy_instrs <- instructions(x) + + xdata <- c(xdata, dummy_sl_x) + ydata <- c(ydata, dummy_sl_y) + # generate temp objects + xj <- setGiotto(giotto(instructions = dummy_instrs, initialize = FALSE), + xdata, verbose = FALSE) + yj <- setGiotto(giotto(instructions = dummy_instrs, initialize = FALSE), + ydata, verbose = FALSE) + # join on intersected feats j <- joinGiottoObjects( list(xj[ufids], yj[ufids]), gobject_names = c("x", "y") ) + # cleanup + rm(y, xj, yj, xdata, ydata, ymeta, dummy_sl_x, dummy_sl_y, dummy_instrs) + + vmsg(.v = verbose, "2. Performing simple filter...") + j <- filterGiotto(j, + expression_threshold = 1, + min_det_feats_per_cell = 1, + feat_det_in_min_cells = 1, + verbose = FALSE + ) + + vmsg(.v = verbose, "3. Running normalize...") + normalize_params$verbose <- FALSE # process - j <- do.call( - normalizeGiotto, args = c(list(gobject = j), normalize_params) + j <- do.call(normalizeGiotto, + args = c(list(gobject = j), normalize_params) ) + if (use_hvf) { + vmsg(.v = verbose, "-- Calculating HVF...") j <- calculateHVF(j) pca_params$feats_to_use = pca_params$feats_to_use %null% "hvf" } else { - pca_params$feats_to_use = NULL + pca_params <- c(pca_params, list(feats_to_use = NULL)) } + + vmsg(.v = verbose, "4. Running PCA...") j <- do.call(runPCA, args = c(list(gobject = j), pca_params)) # TODO determine dims to use via cumvar >= 30% @@ -3455,25 +3501,29 @@ setGeneric( integration_params$dim_reduction_name = "pca" integration_params$dimensions_to_use = dimensions_to_use - j <- do.call(runGiottoHarmony, c(list(gobject = j), integration_params)) - - transfer_args <- list( - x = j, - source_cell_ids = spatIDs(j, subset = list_ID == "y"), - name = "transfer", - dimensions_to_use = dimensions_to_use, - reduction = "cells", - reduction_method = "harmony", - reduction_name = "harmony", - ... + vmsg(.v = verbose, "5. Generating shared Harmony embedding space...") + j <- do.call(runGiottoHarmony, + c(list(gobject = j), integration_params) ) + + # transfer + transfer_args <- list(...) + transfer_args$x <- j + transfer_args$source_cell_ids <- spatIDs(j, subset = list_ID == "y") + transfer_args$dimensions_to_use = dimensions_to_use + transfer_args$reduction = "cells" + transfer_args$reduction_method = "harmony" + transfer_args$reduction_name = "harmony" + + vmsg(.v = verbose, "6. Performing label transfer...") j <- do.call(labelTransfer, transfer_args) res <- pDataDT(j) res <- res[list_ID == "x"] res[, cell_ID := gsub("^x-", "", cell_ID)] - res <- res[, .(cell_ID, transfer, transfer_prob)] - + tname <- transfer_args$name + res <- res[, .SD, .SDcols = c("cell_ID", tname, paste0(tname, "_prob"))] + x <- addCellMetadata(x, new_metadata = res, by_column = TRUE, @@ -3483,6 +3533,7 @@ setGeneric( } +# ** giotto, giotto #### #' @rdname labelTransfer #' @export @@ -3522,7 +3573,15 @@ setMethod("labelTransfer", signature(x = "giotto", y = "giotto"), function( if (integration_method == "harmony") { a <- get_args_list(...) - return(do.call(.lab_transfer_harmony, a)) + a$integration_method <- NULL + # this function needs error handling or it locks the console + res <- tryCatch({ + do.call(.lab_transfer_harmony, a) + }, error = function(e) { + stop(wrap_txtf("labelTransfer: harmony:\n%s", e$message), + call. = FALSE) + }) + return(res) } # NSE vars @@ -3639,6 +3698,9 @@ setMethod("labelTransfer", signature(x = "giotto", y = "giotto"), function( } }) + +# ** giotto, missing #### + #' @rdname labelTransfer #' @export setMethod("labelTransfer", signature(x = "giotto", y = "missing"), function( diff --git a/R/normalize.R b/R/normalize.R index 510788d8f..47ca2eca5 100644 --- a/R/normalize.R +++ b/R/normalize.R @@ -425,8 +425,10 @@ normalizeGiotto <- function(gobject, ) ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### - gobject <- setGiotto(gobject, norm_expr, initialize = FALSE) - gobject <- setGiotto(gobject, norm_scaled_expr, initialize = FALSE) + gobject <- setGiotto( + gobject, norm_expr, verbose = verbose, initialize = FALSE) + gobject <- setGiotto( + gobject, norm_scaled_expr, verbose = verbose, initialize = FALSE) ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## 6. return Giotto object From 771acc2b0b5f5154de9213dcf711eba9a068fb66 Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Mon, 9 Dec 2024 01:47:31 -0500 Subject: [PATCH 16/21] enh: labelTransfer() harmony integration - docs - add cell_id subsets - plotting of labels in joined umap --- DESCRIPTION | 2 +- R/clustering.R | 129 ++++++++++++++++++++++++++++++++++++++----- man/labelTransfer.Rd | 39 ++++++++++++- 3 files changed, 153 insertions(+), 17 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 5d2293a15..9596ed514 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -37,7 +37,7 @@ Imports: dbscan (>= 1.1-3), ggraph, ggplot2 (>= 3.1.1), - GiottoUtils (>= 0.2.0), + GiottoUtils (>= 0.2.2), GiottoVisuals (>= 0.2.6), igraph (>= 1.2.4.1), Matrix (>= 1.6-2), diff --git a/R/clustering.R b/R/clustering.R index 687069816..1a4c8cf60 100644 --- a/R/clustering.R +++ b/R/clustering.R @@ -3320,15 +3320,20 @@ getDendrogramSplits <- function( #' @param k number of k-neighbors to train a KNN classifier #' @param name metadata column in target to apply the full set of labels to #' @param integration_method character. Integration method to use when -#' transferring labels. Options are "none" (default) and "harmony". +#' transferring labels. Options are "none" (default) and "harmony". See section +#' below for more info and params. #' @param prob output knn probabilities together with label predictions #' @param reduction reduction on cells or features (default = "cells") #' @param reduction_method shared reduction method (default = "pca" space) #' @param reduction_name name of shared reduction space (default name = "pca") #' @param dimensions_to_use dimensions to use in shared reduction space #' (default = 1:10) -#' @returns object `x` with new transferred labels added to metadata #' @inheritDotParams FNN::knn -train -test -cl -k -prob +#' @returns object `x` with new transferred labels added to metadata. If +#' running on `x` and `y` objects, `integration_method = "harmony"`, +#' `plot_join_labels = TRUE`, and `return_plot = TRUE` is set, output will +#' be instead a named list of `gobject` (updated `x`), and `label_source_plot` +#' and `label_target_plot` `ggplot2` objects #' @details #' This function trains a KNN classifier with [FNN::knn()]. #' The training data is from object `y` or `source_cell_ids` subset in `x` and @@ -3348,6 +3353,34 @@ getDendrogramSplits <- function( #' used to transfer labels from one set of annotated data to another dataset #' based on expression similarity after joining and integrating. #' +#' # integration_method +#' When running `labelTranfer()` on two `giotto` objects, an integration +#' pipeline can also be run to align the two datasets together before the +#' transfer. `integration_method = "harmony"` will make a temporary joined +#' object on shared features, filter to remove 0 values, run PCA, then harmony +#' integration, before performing the label transfer from `y` to `x` on the +#' integrated harmony embedding space. Additional params that can be used with +#' this method are:\cr +#' +#' * `source_cell_ids` - character. subset of `y` cells to use +#' * `target_cell_ids` - character. subset of `x` cells to use +#' * `expression_values` - character. expression values in `x` and `y` to use +#' to generate combined space. Default = `"raw"` +#' * `use_hvf` - logical. whether to calculate highly variable features to use +#' for PCA calculation. Default = `TRUE`, but setting `FALSE` is recommended if +#' any of `x` or `y` has roughly 1000 features or fewer +#' * `plot_join_labels` - logical. Whether to plot source labels and final +#' labels in the joine object UMAP. +#' * `normalize_params` - named list. Additional params to pass to +#' `normalizeGiotto()` if desired. +#' * `pca_params` - named list. Additional params to pass to `runPCA()` if +#' desired. +#' * `integration_params` - named list. Additional params to pass to +#' `runGiottoHarmony()` if desired. +#' * `plot_params` - named list. Additional params to pass to `plotUMAP()` if +#' desired. Only relevant when `plot_join_labels = TRUE` +#' * `verbose` - verbosity +#' #' @examples #' g <- GiottoData::loadGiottoMini("visium") #' id_subset <- sample(spatIDs(g), 300) @@ -3372,24 +3405,33 @@ setGeneric( .lab_transfer_harmony <- function(x, y, + source_cell_ids = NULL, + target_cell_ids = NULL, expression_values = "raw", dimensions_to_use = 1:10, spat_unit = NULL, feat_type = NULL, - filter_params = list( - expression_threshold = 1, - feat_det_in_min_cells = 1, - min_det_feats_per_cell = 10 - ), - normalize_params = list(), use_hvf = TRUE, + plot_join_labels = FALSE, + normalize_params = list(), pca_params = list(), integration_params = list(), + plot_params = list(), verbose = NULL, ... # passes to labelTransfer ) { # NSE vars cell_ID <- transfer <- transfer_prob <- NULL + + .check_plot_output <- function(pparam) { + direct_setting <- plot_params[[pparam]] + if (!is.null(direct_setting)) return(direct_setting) + any(instructions(x, pparam), instructions(y, pparam)) + } + + .show_plot <- .check_plot_output("show_plot") + .return_plot <- .check_plot_output("return_plot") + .save_plot <- .check_plot_output("save_plot") # turn off lower level updates options("giotto.update_param" = FALSE) @@ -3457,12 +3499,12 @@ setGeneric( xdata, verbose = FALSE) yj <- setGiotto(giotto(instructions = dummy_instrs, initialize = FALSE), ydata, verbose = FALSE) - - # join on intersected feats - j <- joinGiottoObjects( - list(xj[ufids], yj[ufids]), - gobject_names = c("x", "y") - ) + + xj <- subsetGiotto(xj, feat_ids = ufids, cell_ids = target_cell_ids) + yj <- subsetGiotto(yj, feat_ids = ufids, cell_ids = source_cell_ids) + + # join on intersected feats and specified cell_IDs + j <- joinGiottoObjects(list(xj, yj), gobject_names = c("x", "y")) # cleanup rm(y, xj, yj, xdata, ydata, ymeta, dummy_sl_x, dummy_sl_y, dummy_instrs) @@ -3529,6 +3571,65 @@ setGeneric( by_column = TRUE, column_cell_ID = "cell_ID" ) + + # plot outputs + if (!any(.save_plot, .return_plot, .show_plot) && plot_join_labels) { + return(x) # return early if plotting not needed + } + + vmsg(.v = verbose, "7. plot_join_labels = TRUE: Running UMAP...") + + j <- runUMAP(j, + dim_reduction_to_use = "harmony", + dim_reduction_name = "harmony", + name = "harmony_umap", + dimensions_to_use = dimensions_to_use + ) + + plotlabs <- unique(res[[tname]]) + default_colors <- getRainbowColors( + n = length(plotlabs), slim = c(0.5, 1), vlim = c(0.3, 1) + ) + names(default_colors) <- plotlabs + + plot_params$gobject <- j + plot_params$dim_reduction_name <- "harmony_umap" + plot_params$color_as_factor <- TRUE + plot_params$point_size <- plot_params$point_size %null% 0.5 + plot_params$point_border_stroke <- plot_params$point_border_stroke %null% 0 + plot_params$cell_color_code <- + plot_params$cell_color_code %null% default_colors + + p1_params <- p2_params <- plot_params + + p1_params$cell_color <- transfer_args$labels + p1_params$select_cells <- spatIDs(j, subset = list_ID == "y") + p1_params$other_point_size <- p1_params$other_point_size %null% 0.3 + p1_params$show_plot <- FALSE + p1_params$return_plot <- TRUE + p1_params$title <- "source labels" + + p2_params$cell_color <- tname + p2_params$show_plot <- FALSE + p2_params$return_plot <- TRUE + p2_params$title <- "final labels" + + plot_list <- list(label_source_plot = do.call(plotUMAP, p1_params), + label_final_plot = do.call(plotUMAP, p2_params)) + + if (.show_plot) { + print(plot_grid(plotlist = plot_list)) + } + if (.save_plot) { + plot_output_handler(x, plot_list[[1]], + save_plot = .save_plot, + default_save_name = "transferLabels_source", + ) + } + if (.return_plot) { + x <- c(list(gobject = x), plot_list) + } + return(x) } diff --git a/man/labelTransfer.Rd b/man/labelTransfer.Rd index e1d13ce8b..31da8a1eb 100644 --- a/man/labelTransfer.Rd +++ b/man/labelTransfer.Rd @@ -55,7 +55,8 @@ voting} \item{name}{metadata column in target to apply the full set of labels to} \item{integration_method}{character. Integration method to use when -transferring labels. Options are "none" (default) and "harmony".} +transferring labels. Options are "none" (default) and "harmony". See section +below for more info and params.} \item{prob}{output knn probabilities together with label predictions} @@ -86,7 +87,11 @@ for x (1) and y (2). Setting defaults with \code{activeFeatType()} may be easier IDs from \code{source_cell_ids} are always included as well.} } \value{ -object \code{x} with new transferred labels added to metadata +object \code{x} with new transferred labels added to metadata. If +running on \code{x} and \code{y} objects, \code{integration_method = "harmony"}, +\code{plot_join_labels = TRUE}, and \code{return_plot = TRUE} is set, output will +be instead a named list of \code{gobject} (updated \code{x}), and \code{label_source_plot} +and \code{label_target_plot} \code{ggplot2} objects } \description{ When two sets of data share an embedding space, transfer the labels from @@ -111,6 +116,36 @@ labels to the remaining cells in the target Giotto object. It can also be used to transfer labels from one set of annotated data to another dataset based on expression similarity after joining and integrating. } +\section{integration_method}{ +When running \code{labelTranfer()} on two \code{giotto} objects, an integration +pipeline can also be run to align the two datasets together before the +transfer. \code{integration_method = "harmony"} will make a temporary joined +object on shared features, filter to remove 0 values, run PCA, then harmony +integration, before performing the label transfer from \code{y} to \code{x} on the +integrated harmony embedding space. Additional params that can be used with +this method are:\cr +\itemize{ +\item \code{source_cell_ids} - character. subset of \code{y} cells to use +\item \code{target_cell_ids} - character. subset of \code{x} cells to use +\item \code{expression_values} - character. expression values in \code{x} and \code{y} to use +to generate combined space. Default = \code{"raw"} +\item \code{use_hvf} - logical. whether to calculate highly variable features to use +for PCA calculation. Default = \code{TRUE}, but setting \code{FALSE} is recommended if +any of \code{x} or \code{y} has roughly 1000 features or fewer +\item \code{plot_join_labels} - logical. Whether to plot source labels and final +labels in the joine object UMAP. +\item \code{normalize_params} - named list. Additional params to pass to +\code{normalizeGiotto()} if desired. +\item \code{pca_params} - named list. Additional params to pass to \code{runPCA()} if +desired. +\item \code{integration_params} - named list. Additional params to pass to +\code{runGiottoHarmony()} if desired. +\item \code{plot_params} - named list. Additional params to pass to \code{plotUMAP()} if +desired. Only relevant when \code{plot_join_labels = TRUE} +\item \code{verbose} - verbosity +} +} + \examples{ g <- GiottoData::loadGiottoMini("visium") id_subset <- sample(spatIDs(g), 300) From faf11e153da4bb0f1706b182513b7a0db6355604 Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Mon, 9 Dec 2024 02:35:04 -0500 Subject: [PATCH 17/21] chore: reexport dotplot --- R/suite_reexports.R | 2 ++ 1 file changed, 2 insertions(+) diff --git a/R/suite_reexports.R b/R/suite_reexports.R index 905811ab7..833d0b6c6 100644 --- a/R/suite_reexports.R +++ b/R/suite_reexports.R @@ -471,6 +471,8 @@ GiottoClass::writeGiottoLargeImage #' @export GiottoVisuals::addGiottoImageToSpatPlot #' @export +GiottoVisuals::dotPlot +#' @export GiottoVisuals::dimCellPlot #' @export GiottoVisuals::dimCellPlot2D From fbee521563cef809c8b2b14bb36eede2090bbe47 Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Mon, 9 Dec 2024 02:55:19 -0500 Subject: [PATCH 18/21] chore: update news --- DESCRIPTION | 2 +- NEWS.md | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 9596ed514..9b8ba061e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -38,7 +38,7 @@ Imports: ggraph, ggplot2 (>= 3.1.1), GiottoUtils (>= 0.2.2), - GiottoVisuals (>= 0.2.6), + GiottoVisuals (>= 0.2.10), igraph (>= 1.2.4.1), Matrix (>= 1.6-2), MatrixGenerics, diff --git a/NEWS.md b/NEWS.md index cc8301f61..b1d5eb507 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,24 @@ +# Giotto 4.1.6 (2024/12/09) + +## Bug fixes +* `doScrubletDetect()` seed setting + +## Enhancements +* `labelTransfer()` now has `integration_method = "harmony"` for label transferring with an integration pipeline. See ?labelTransfer and the `integration_method` section. +* `importXenium()` `load_transcripts()` can now return a `data.table` rather than the `giottoPoints` representation + +## New +* `.varexp()` internal for calculating SVD variance determined with support for partial SVDs +* `.cumvar()` internal for calculating cumulative variance explained +* re-export of `dotPlot()` from GiottoVisuals + +## Changes +* GiottoClass req raised to 0.4.5 +* GiottoUtils req raised to 0.2.2 +* GiottoVisuals req raised to 0.2.10 + + # Giotto 4.1.5 (2024/11/08) ## Enhancements From 74dd669252ced2d917054c9d5052df9d0b3de5ae Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Mon, 9 Dec 2024 03:02:32 -0500 Subject: [PATCH 19/21] chore: docs --- DESCRIPTION | 2 +- NAMESPACE | 2 ++ man/reexports.Rd | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 9b8ba061e..a869dc843 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: Giotto Title: Spatial Single-Cell Transcriptomics Toolbox -Version: 4.1.5 +Version: 4.1.6 Authors@R: c( person("Ruben", "Dries", email = "rubendries@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0001-7650-7754")), diff --git a/NAMESPACE b/NAMESPACE index 8aca099c2..2a8b3001f 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -180,6 +180,7 @@ export(doLouvainSubCluster) export(doRandomWalkCluster) export(doSNNCluster) export(doScrubletDetect) +export(dotPlot) export(estimateAutomatedImageRegistrationWithSIFT) export(estimateImageBg) export(exportGiottoViewer) @@ -751,6 +752,7 @@ importFrom(GiottoVisuals,dimGenePlot3D) importFrom(GiottoVisuals,dimPlot) importFrom(GiottoVisuals,dimPlot2D) importFrom(GiottoVisuals,dimPlot3D) +importFrom(GiottoVisuals,dotPlot) importFrom(GiottoVisuals,getColors) importFrom(GiottoVisuals,giottoSankeyPlan) importFrom(GiottoVisuals,plotHeatmap) diff --git a/man/reexports.Rd b/man/reexports.Rd index f9575eb02..15e96008b 100644 --- a/man/reexports.Rd +++ b/man/reexports.Rd @@ -223,6 +223,7 @@ \alias{updateGiottoPolygonObject} \alias{writeGiottoLargeImage} \alias{addGiottoImageToSpatPlot} +\alias{dotPlot} \alias{dimCellPlot} \alias{dimCellPlot2D} \alias{dimFeatPlot2D} @@ -301,6 +302,6 @@ below to see their documentation. \item{GiottoUtils}{\code{\link[GiottoUtils:pipe]{\%>\%}}, \code{\link[GiottoUtils]{getDistinctColors}}, \code{\link[GiottoUtils]{getRainbowColors}}} - \item{GiottoVisuals}{\code{\link[GiottoVisuals]{addGiottoImageToSpatPlot}}, \code{\link[GiottoVisuals]{dimCellPlot}}, \code{\link[GiottoVisuals:dimCellPlot]{dimCellPlot2D}}, \code{\link[GiottoVisuals]{dimFeatPlot2D}}, \code{\link[GiottoVisuals]{dimFeatPlot3D}}, \code{\link[GiottoVisuals:dimFeatPlot3D]{dimGenePlot3D}}, \code{\link[GiottoVisuals]{dimPlot}}, \code{\link[GiottoVisuals:dimPlot]{dimPlot2D}}, \code{\link[GiottoVisuals:dimPlot]{dimPlot3D}}, \code{\link[GiottoVisuals]{getColors}}, \code{\link[GiottoVisuals]{giottoSankeyPlan}}, \code{\link[GiottoVisuals]{plotHeatmap}}, \code{\link[GiottoVisuals]{plotMetaDataCellsHeatmap}}, \code{\link[GiottoVisuals]{plotMetaDataHeatmap}}, \code{\link[GiottoVisuals]{plotPCA}}, \code{\link[GiottoVisuals]{plotPCA_2D}}, \code{\link[GiottoVisuals]{plotPCA_3D}}, \code{\link[GiottoVisuals]{plotStatDelaunayNetwork}}, \code{\link[GiottoVisuals]{plotTSNE}}, \code{\link[GiottoVisuals]{plotTSNE_2D}}, \code{\link[GiottoVisuals]{plotTSNE_3D}}, \code{\link[GiottoVisuals]{plotUMAP}}, \code{\link[GiottoVisuals]{plotUMAP_2D}}, \code{\link[GiottoVisuals]{plotUMAP_3D}}, \code{\link[GiottoVisuals]{sankeyLabel}}, \code{\link[GiottoVisuals:sankeyLabel]{sankeyLabel<-}}, \code{\link[GiottoVisuals]{sankeyPlot}}, \code{\link[GiottoVisuals]{sankeyRelate}}, \code{\link[GiottoVisuals:sankeyRelate]{sankeyRelate<-}}, \code{\link[GiottoVisuals]{sankeySet}}, \code{\link[GiottoVisuals]{sankeySetAddresses}}, \code{\link[GiottoVisuals]{showClusterDendrogram}}, \code{\link[GiottoVisuals]{showClusterHeatmap}}, \code{\link[GiottoVisuals]{showColorInstructions}}, \code{\link[GiottoVisuals]{showSaveParameters}}, \code{\link[GiottoVisuals]{spatCellPlot}}, \code{\link[GiottoVisuals:spatCellPlot]{spatCellPlot2D}}, \code{\link[GiottoVisuals]{spatDeconvPlot}}, \code{\link[GiottoVisuals]{spatDimCellPlot}}, \code{\link[GiottoVisuals]{spatDimCellPlot2D}}, \code{\link[GiottoVisuals]{spatDimFeatPlot2D}}, \code{\link[GiottoVisuals]{spatDimFeatPlot3D}}, \code{\link[GiottoVisuals:spatDimFeatPlot3D]{spatDimGenePlot3D}}, \code{\link[GiottoVisuals]{spatDimPlot}}, \code{\link[GiottoVisuals:spatDimPlot]{spatDimPlot2D}}, \code{\link[GiottoVisuals]{spatDimPlot3D}}, \code{\link[GiottoVisuals]{spatFeatPlot2D}}, \code{\link[GiottoVisuals]{spatFeatPlot2D_single}}, \code{\link[GiottoVisuals]{spatFeatPlot3D}}, \code{\link[GiottoVisuals:spatFeatPlot3D]{spatGenePlot3D}}, \code{\link[GiottoVisuals]{spatInSituPlotDensity}}, \code{\link[GiottoVisuals]{spatInSituPlotHex}}, \code{\link[GiottoVisuals]{spatInSituPlotPoints}}, \code{\link[GiottoVisuals]{spatNetwDistributions}}, \code{\link[GiottoVisuals]{spatNetwDistributionsDistance}}, \code{\link[GiottoVisuals]{spatNetwDistributionsKneighbors}}, \code{\link[GiottoVisuals]{spatPlot}}, \code{\link[GiottoVisuals:spatPlot]{spatPlot2D}}, \code{\link[GiottoVisuals:spatPlot]{spatPlot3D}}, \code{\link[GiottoVisuals]{subsetSankeySet}}, \code{\link[GiottoVisuals]{violinPlot}}} + \item{GiottoVisuals}{\code{\link[GiottoVisuals]{addGiottoImageToSpatPlot}}, \code{\link[GiottoVisuals]{dimCellPlot}}, \code{\link[GiottoVisuals:dimCellPlot]{dimCellPlot2D}}, \code{\link[GiottoVisuals]{dimFeatPlot2D}}, \code{\link[GiottoVisuals]{dimFeatPlot3D}}, \code{\link[GiottoVisuals:dimFeatPlot3D]{dimGenePlot3D}}, \code{\link[GiottoVisuals]{dimPlot}}, \code{\link[GiottoVisuals:dimPlot]{dimPlot2D}}, \code{\link[GiottoVisuals:dimPlot]{dimPlot3D}}, \code{\link[GiottoVisuals]{dotPlot}}, \code{\link[GiottoVisuals]{getColors}}, \code{\link[GiottoVisuals]{giottoSankeyPlan}}, \code{\link[GiottoVisuals]{plotHeatmap}}, \code{\link[GiottoVisuals]{plotMetaDataCellsHeatmap}}, \code{\link[GiottoVisuals]{plotMetaDataHeatmap}}, \code{\link[GiottoVisuals]{plotPCA}}, \code{\link[GiottoVisuals]{plotPCA_2D}}, \code{\link[GiottoVisuals]{plotPCA_3D}}, \code{\link[GiottoVisuals]{plotStatDelaunayNetwork}}, \code{\link[GiottoVisuals]{plotTSNE}}, \code{\link[GiottoVisuals]{plotTSNE_2D}}, \code{\link[GiottoVisuals]{plotTSNE_3D}}, \code{\link[GiottoVisuals]{plotUMAP}}, \code{\link[GiottoVisuals]{plotUMAP_2D}}, \code{\link[GiottoVisuals]{plotUMAP_3D}}, \code{\link[GiottoVisuals]{sankeyLabel}}, \code{\link[GiottoVisuals:sankeyLabel]{sankeyLabel<-}}, \code{\link[GiottoVisuals]{sankeyPlot}}, \code{\link[GiottoVisuals]{sankeyRelate}}, \code{\link[GiottoVisuals:sankeyRelate]{sankeyRelate<-}}, \code{\link[GiottoVisuals]{sankeySet}}, \code{\link[GiottoVisuals]{sankeySetAddresses}}, \code{\link[GiottoVisuals]{showClusterDendrogram}}, \code{\link[GiottoVisuals]{showClusterHeatmap}}, \code{\link[GiottoVisuals]{showColorInstructions}}, \code{\link[GiottoVisuals]{showSaveParameters}}, \code{\link[GiottoVisuals]{spatCellPlot}}, \code{\link[GiottoVisuals:spatCellPlot]{spatCellPlot2D}}, \code{\link[GiottoVisuals]{spatDeconvPlot}}, \code{\link[GiottoVisuals]{spatDimCellPlot}}, \code{\link[GiottoVisuals]{spatDimCellPlot2D}}, \code{\link[GiottoVisuals]{spatDimFeatPlot2D}}, \code{\link[GiottoVisuals]{spatDimFeatPlot3D}}, \code{\link[GiottoVisuals:spatDimFeatPlot3D]{spatDimGenePlot3D}}, \code{\link[GiottoVisuals]{spatDimPlot}}, \code{\link[GiottoVisuals:spatDimPlot]{spatDimPlot2D}}, \code{\link[GiottoVisuals]{spatDimPlot3D}}, \code{\link[GiottoVisuals]{spatFeatPlot2D}}, \code{\link[GiottoVisuals]{spatFeatPlot2D_single}}, \code{\link[GiottoVisuals]{spatFeatPlot3D}}, \code{\link[GiottoVisuals:spatFeatPlot3D]{spatGenePlot3D}}, \code{\link[GiottoVisuals]{spatInSituPlotDensity}}, \code{\link[GiottoVisuals]{spatInSituPlotHex}}, \code{\link[GiottoVisuals]{spatInSituPlotPoints}}, \code{\link[GiottoVisuals]{spatNetwDistributions}}, \code{\link[GiottoVisuals]{spatNetwDistributionsDistance}}, \code{\link[GiottoVisuals]{spatNetwDistributionsKneighbors}}, \code{\link[GiottoVisuals]{spatPlot}}, \code{\link[GiottoVisuals:spatPlot]{spatPlot2D}}, \code{\link[GiottoVisuals:spatPlot]{spatPlot3D}}, \code{\link[GiottoVisuals]{subsetSankeySet}}, \code{\link[GiottoVisuals]{violinPlot}}} }} From d4f3e6f9a39e6c907c79543b99b46bede01f53b1 Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Mon, 9 Dec 2024 03:05:42 -0500 Subject: [PATCH 20/21] chore: docs --- NAMESPACE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NAMESPACE b/NAMESPACE index e8799628d..cf94c401c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -181,8 +181,8 @@ export(doMesmerSegmentation) export(doRandomWalkCluster) export(doSNNCluster) export(doScrubletDetect) -export(dotPlot) export(doStardistSegmentation) +export(dotPlot) export(estimateAutomatedImageRegistrationWithSIFT) export(estimateImageBg) export(exportGiottoViewer) From 402b4c979bc4c2fef9dcf7f0dff1261fe80c28a1 Mon Sep 17 00:00:00 2001 From: George Chen <72078254+jiajic@users.noreply.github.com> Date: Mon, 9 Dec 2024 03:07:55 -0500 Subject: [PATCH 21/21] Update NEWS.md --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index b1d5eb507..1fa9f3c82 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,6 +9,7 @@ * `importXenium()` `load_transcripts()` can now return a `data.table` rather than the `giottoPoints` representation ## New +* `doMesmerSegmentation()` and `doStardistSegmentation()` segmentation wrappers * `.varexp()` internal for calculating SVD variance determined with support for partial SVDs * `.cumvar()` internal for calculating cumulative variance explained * re-export of `dotPlot()` from GiottoVisuals