From 91deb235fc25e97080f0e06eb87449dcfd37e424 Mon Sep 17 00:00:00 2001 From: iqraAmin Date: Sun, 22 Sep 2024 21:56:08 -0400 Subject: [PATCH] Enhance Seurat interoperability for Xenium dataset support --- R/interoperability.R | 53 ++++++++++++-------- tests/testthat/test-interoperability.R | 69 ++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 21 deletions(-) create mode 100644 tests/testthat/test-interoperability.R diff --git a/R/interoperability.R b/R/interoperability.R index 1063ec7c..14cd5941 100644 --- a/R/interoperability.R +++ b/R/interoperability.R @@ -767,7 +767,7 @@ giottoToAnnData <- function(gobject = NULL, # Feat Metadata for (su in spat_unit) { for (ft in names(gobject@expression[[su]])) { - cmeta <- get_cell_metadata( + cmeta <- getCellMetadata( gobject = gobject, spat_unit = su, feat_type = ft, @@ -775,7 +775,7 @@ giottoToAnnData <- function(gobject = NULL, set_defaults = FALSE ) - fm <- get_feature_metadata( + fm <- getFeatureMetadata( gobject = gobject, spat_unit = su, feat_type = ft, @@ -1289,7 +1289,7 @@ giottoToSeuratV4 <- function(gobject, } # add cell metadata meta_cells <- data.table::setDF( - get_cell_metadata( + getCellMetadata( gobject = gobject, spat_unit = spat_unit, feat_type = assay_use, @@ -1307,7 +1307,7 @@ giottoToSeuratV4 <- function(gobject, ) # add feature metadata meta_genes <- data.table::setDF( - get_feature_metadata( + getFeatureMetadata( gobject = gobject, spat_unit = spat_unit, feat_type = assay_use, @@ -1477,7 +1477,14 @@ giottoToSeuratV5 <- function(gobject, gobject = gobject, spat_unit = spat_unit ) - + assay_names <- names(gobject@expression$cell) + + # Identify assays with spaces and replace with underscores + new_assay_names <- gsub(" ", "_", assay_names) + + # Apply the new names to the gobject expression slot + names(gobject@expression$cell) <- new_assay_names + # verify if optional package is installed package_check(pkg_name = "Seurat", repository = "CRAN") loadNamespace("Seurat") @@ -1569,8 +1576,9 @@ giottoToSeuratV5 <- function(gobject, } # add cell metadata + names(gobject@cell_metadata$cell) <- gsub(" ", "_", names(gobject@cell_metadata$cell)) meta_cells <- data.table::setDF( - get_cell_metadata( + getCellMetadata( gobject = gobject, spat_unit = spat_unit, feat_type = assay_use, @@ -1579,21 +1587,22 @@ giottoToSeuratV5 <- function(gobject, ) ) rownames(meta_cells) <- meta_cells$cell_ID - meta_cells <- meta_cells[, -which(colnames(meta_cells) == "cell_ID")] + meta_cells <- meta_cells[, -which(colnames(meta_cells) == "cell_ID"), drop = FALSE] if (ncol(meta_cells) > 0) { colnames(meta_cells) <- paste0( assay_use, "_", colnames(meta_cells) ) - } sobj <- Seurat::AddMetaData(sobj, metadata = meta_cells[Seurat::Cells(sobj), ], col.name = names(meta_cells) ) - + } + # add feature metadata + names(gobject@feat_metadata$cell) <- gsub(" ", "_", names(gobject@feat_metadata$cell)) meta_genes <- data.table::setDF( - get_feature_metadata( + getFeatureMetadata( gobject = gobject, spat_unit = spat_unit, feat_type = assay_use, @@ -1602,15 +1611,14 @@ giottoToSeuratV5 <- function(gobject, ) ) rownames(meta_genes) <- meta_genes$feat_ID - for (i in seq_along(sobj@assays)) { - # Check if assay_slot has @meta.data or @meta.features - if ("meta.data" %in% slotNames(sobj@assays[[i]])) { - sobj@assays[[i]]@meta.data <- meta_genes - } else if ("meta.features" %in% slotNames(sobj@assays[[i]])) { - sobj@assays[[i]]@meta.features <- meta_genes - } else { - warning(paste("No suitable metadata slot found for assay", i)) - } + if ("meta.data" %in% slotNames(sobj@assays[[assay_use]])) { + sobj@assays[[assay_use]]@meta.data <- meta_genes + message(paste("Meta data updated for assay:", assay_use)) + } else if ("meta.features" %in% slotNames(sobj@assays[[assay_use]])) { + sobj@assays[[assay_use]]@meta.features <- meta_genes + message(paste("Meta features updated for assay:", assay_use)) + } else { + warning(paste("No suitable metadata slot found for assay", assay_use)) } # dim reduction @@ -2331,15 +2339,18 @@ seuratToGiottoV5 <- function(sobject, } # Subcellular name <- names(sobject@images) - # if (!is.null(subcellular_assay)){ if (length(sobject@assays[[subcellular_assay]]) == 1) { + if (!is.null(Seurat::Images( + object = sobject, + assay = spatial_assay + ))) { spat_coord <- Seurat::GetTissueCoordinates(sobject) colnames(spat_coord) <- c("sdimx", "sdimy") spat_coord$cell_ID <- rownames(spat_coord) exp <- exp[, c(intersect(spat_coord$cell_ID, colnames(exp)))] spat_loc <- spat_coord + } } - # } if (!length(sobject@images) == 0) { for (i in names(sobject@images)) { if ("molecules" %in% names(sobject@images[[i]]) == TRUE) { diff --git a/tests/testthat/test-interoperability.R b/tests/testthat/test-interoperability.R new file mode 100644 index 00000000..16b9f3ab --- /dev/null +++ b/tests/testthat/test-interoperability.R @@ -0,0 +1,69 @@ +library(testthat) +library(Giotto) +library(Seurat) + +giotto_obj <- GiottoData::loadGiottoMini("visium") +seurat_obj <- giottoToSeuratV5(giotto_obj) +spe_obj <- giottoToSpatialExperiment(giotto_obj) + +Seurat_obj2 <- SeuratData::LoadData("stxBrain", type = "anterior1" ) +giotto_obj2 <- seuratToGiottoV5(Seurat_obj2) + +giotto_obj_roundtrip <- seuratToGiottoV5(seurat_obj, "rna") + +test_that("giottotoseurat function handles basic conversion", { + +# Basic checks +expect_s4_class(seurat_obj, "Seurat") +expect_true(!is.null(seurat_obj@meta.data)) +expect_equal(nrow(seurat_obj@meta.data), + nrow(giotto_obj@cell_metadata$cell$rna@metaDT)) + +# Check if image slots are handled correctly +if ("images" %in% slotNames(giotto_obj)) { + expect_true(length(seurat_obj@images) > 0) + expect_equal(length(seurat_obj@images), length(giotto_obj@images)) + expect_s4_class(giotto_obj_roundtrip@images$alignment, "giottoLargeImage") + expect_s4_class(seurat_obj@images$alignment, "VisiumV1") +} +}) + +test_that("Giotto to Seurat Conversion Works",{ + expect_s4_class(seurat_obj, "Seurat") +}) + +test_that("Seurat to Giotto Conversion Works",{ + expect_s4_class(giotto_obj, "giotto") +}) + +test_that("Assay names are converted correctly (no spaces)", { + # Check for spaces in Seurat assay names + assay_names <- names(seurat_obj@assays) + expect_false(any(grepl(" ", assay_names))) # No spaces should be present +}) + +test_that("Data is consistent after roundtrip conversion", { + + identical(giotto_obj_roundtrip@expression, giotto_obj@expression) + # Check consistency of expression data + expect_equal(dim(giotto_obj@expression$cell$rna$raw), + dim(giotto_obj_roundtrip@expression$cell$rna$raw)) + + # Check consistency of cell metadata + expect_equal(nrow(giotto_obj@cell_metadata$cell$rna@metaDT), + nrow(giotto_obj_roundtrip@cell_metadata$cell$rna@metaDT)) +}) + +test_that("Feature metadata is transferred correctly", { + # Check if feature metadata (e.g., gene names) is correctly transferred + expect_equal(rownames(seurat_obj@assays$rna), + rownames(giotto_obj@expression$cell$rna$raw)) +}) + +test_that("Giotto to SpatialExperiment Conversion Works",{ + expect_s4_class(spe_obj[[1]], "SpatialExperiment") +}) + +#TODO +#spe +#annData \ No newline at end of file