diff celesta_assign_cells.R @ 0:8001319743c0 draft

planemo upload for repository https://github.com/goeckslab/tools-mti/tree/main/tools/celesta commit 0ec46718dfd00f37ccae4e2fa133fa8393fe6d92
author goeckslab
date Wed, 28 Aug 2024 12:46:48 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/celesta_assign_cells.R	Wed Aug 28 12:46:48 2024 +0000
@@ -0,0 +1,153 @@
+# ---------------------------------------------------------------------------------
+# The main algorithim for CELESTA cell type assignment
+# ---------------------------------------------------------------------------------
+
+suppressWarnings(suppressMessages(library(janitor)))
+suppressWarnings(suppressMessages(library(optparse)))
+suppressWarnings(suppressMessages(library(dplyr)))
+suppressWarnings(suppressMessages(library(anndataR)))
+suppressWarnings(suppressMessages(library(Rmixmod)))
+suppressWarnings(suppressMessages(library(spdep)))
+suppressWarnings(suppressMessages(library(ggplot2)))
+suppressWarnings(suppressMessages(library(reshape2)))
+suppressWarnings(suppressMessages(library(zeallot)))
+suppressWarnings(suppressMessages(library(CELESTA)))
+
+### Define command line arguments
+option_list <- list(
+  make_option(c("-i", "--imagingdata"), action = "store", default = NA, type = "character",
+              help = "Path to imaging data"),
+  make_option(c("-p", "--prior"), action = "store", default = NA, type = "character",
+              help = "Path to prior marker info file"),
+  make_option(c("-x", "--xcol"), action = "store", default = NA, type = "character",
+              help = "Name of column in adata.obs containing X coordinate"),
+  make_option(c("-y", "--ycol"), action = "store", default = NA, type = "character",
+              help = "Name of column in adata.obs containing Y coordinate"),
+  make_option(c("--filter"), action = "store_true", type = "logical", default = FALSE,
+              help = "Boolean to filter cells or not (default: no filtering)"),
+  make_option(c("--highfilter"), action = "store", default = 0.9, type = "double",
+              help = "High marker threshold if filtering cells (default: 0.9)"),
+  make_option(c("--lowfilter"), action = "store", default = 0.4, type = "double",
+              help = "Low marker threshold if filtering cells (default: 0.4)"),
+  make_option(c("--maxiteration"), action = "store", default = 10, type = "integer",
+              help = "Maximum iterations allowed in the EM algorithm per round"),
+  make_option(c("--changethresh"), action = "store", default = 0.01, type = "double",
+              help = "Ending condition for the EM algorithm"),
+  make_option(c("--highexpthresh"), action = "store", default = "default_high_thresholds", type = "character",
+              help = "Path to file specifying high expression thresholds for anchor and index cells"),
+  make_option(c("--lowexpthresh"), action = "store", default = "default_low_thresholds", type = "character",
+              help = "Path to file specifying low expression thresholds for anchor and index cells")
+)
+
+### Functions
+anndata_to_celesta <- function(input_adata, x_col, y_col) {
+
+  #' Function to convert anndata object to dataframe readable by CELESTA
+  #' Coordinates columns in adata.obs are renamed to "X" and "Y", and placed at beginning of dataframe
+  #' Marker intensities are concatted columnwise to the X and Y coords. cols: X,Y,Marker_1,...Marker N
+
+  # initialize output as dataframe from adata.obs
+  celesta_input_dataframe <- data.frame(input_adata$obs)
+
+  # subset to X and Y coordinates from obs only
+  celesta_input_dataframe <- celesta_input_dataframe %>%
+    dplyr::select({{x_col}}, {{y_col}})
+
+  # rename X,Y column names to what CELESTA wants
+  colnames(celesta_input_dataframe) <- c("X", "Y")
+
+  # merge X,Y coords with marker intensities from adata.X
+  x_df <- data.frame(input_adata$X)
+  colnames(x_df) <- input_adata$var_names
+  celesta_input_dataframe <- cbind(celesta_input_dataframe, x_df)
+
+  return(celesta_input_dataframe)
+}
+
+### Main
+# parse args
+opt <- parse_args(OptionParser(option_list = option_list))
+
+# read anndata, convert to celesta format
+adata <- read_h5ad(opt$imagingdata)
+celesta_input_df <- anndata_to_celesta(adata, x_col = opt$xcol, y_col = opt$ycol)
+
+# read prior marker info
+prior <- read.csv(opt$prior, check.names = FALSE)
+
+# clean prior names and input dataframe names
+prior <- janitor::clean_names(prior, case = "all_caps")
+celesta_input_df <- janitor::clean_names(celesta_input_df, case = "all_caps")
+
+# instantiate celesta object
+CelestaObj <- CreateCelestaObject(
+  project_title = "",
+  prior_marker_info = prior,
+  imaging_data_file = celesta_input_df
+)
+
+# if filtering is specified, filter out cells outside high and low thresholds
+if (opt$filter) {
+  print("filtering cells based on expression")
+  CelestaObj <- FilterCells(CelestaObj,
+                            high_marker_threshold = opt$highfilter,
+                            low_marker_threshold = opt$lowfilter)
+} else {
+  print("Proceeding to cell type assignment without cell filtering")
+}
+
+# check for non-default expression threshold files
+if (opt$highexpthresh != "default_high_thresholds") {
+  # read high thresholds
+  print("Using custom high expression thresholds")
+  high_expression_thresholds <- read.csv(opt$highexpthresh)
+  hi_exp_thresh_anchor <- high_expression_thresholds$anchor
+  hi_exp_thresh_index <- high_expression_thresholds$index
+} else {
+  print("Using default high expression thresholds -- this may need adjustment")
+  hi_exp_thresh_anchor <- rep(0.7, length = 50)
+  hi_exp_thresh_index <- rep(0.5, length = 50)
+}
+
+if (opt$lowexpthresh != "default_low_thresholds") {
+  # read low thresholds
+  print("Using custom low expression thresholds")
+  low_expression_thresholds <- read.csv(opt$highexpthresh)
+  low_exp_thresh_anchor <- low_expression_thresholds$anchor
+  low_exp_thresh_index <- low_expression_thresholds$index
+} else {
+  print("Using default low expression thresholds")
+  low_exp_thresh_anchor <- rep(0.9, length = 50)
+  low_exp_thresh_index <- rep(1, length = 50)
+}
+
+# run cell type assignment
+CelestaObj <- AssignCells(CelestaObj,
+                          max_iteration = opt$maxiteration,
+                          cell_change_threshold = opt$changethresh,
+                          high_expression_threshold_anchor = hi_exp_thresh_anchor,
+                          low_expression_threshold_anchor = low_exp_thresh_anchor,
+                          high_expression_threshold_index = hi_exp_thresh_index,
+                          low_expression_threshold_index = low_exp_thresh_index,
+                          save_result = FALSE)
+
+# save object as an RDS file for cell type plotting
+# for the time being, this is not exposed to Galaxy
+saveRDS(CelestaObj, file = "celestaobj.rds")
+
+# rename celesta assignment columns so they are obvious in output anndata
+celesta_assignments <- CelestaObj@final_cell_type_assignment
+celesta_assignments <- janitor::clean_names(celesta_assignments)
+colnames(celesta_assignments) <- paste0("celesta_", colnames(celesta_assignments))
+
+# merge celesta assignments into anndata object
+adata$obs <- cbind(adata$obs, celesta_assignments)
+
+# print cell type value_counts to standard output
+print("----------------------------------------")
+print("Final cell type counts")
+print(adata$obs %>% dplyr::count(celesta_final_cell_type, sort = TRUE))
+print("----------------------------------------")
+
+# write output anndata file
+write_h5ad(adata, "result.h5ad")