diff --git a/R/make-seurat.R b/R/make-seurat.R index 297c9c3..e18c96f 100644 --- a/R/make-seurat.R +++ b/R/make-seurat.R @@ -136,9 +136,22 @@ sce_to_seurat <- function( # add altExps as needed. for (alt_exp_name in names(alt_exps)) { alt_exp <- alt_exps[[alt_exp_name]] - stopifnot( - "All altExps must contain a `counts` assay." = "counts" %in% assayNames(alt_exp) - ) + + if (!"counts" %in% assayNames(alt_exp)) { + warning( + "The altExp `", alt_exp_name, + "` does not have a `counts` assay, so it will be skipped." + ) + next + } + + if (nrow(alt_exp) == 1 && seurat_assay_version == "v5") { + warning( + "The altExp `", alt_exp_name, "` has only one feature;", + " for Seurat v5 compatibility, a dummy feature will be added with all zero counts." + ) + alt_exp <- add_dummy_feature(alt_exp) + } # check name compatibility for Seurat alt_exp_rownames <- rownames(alt_exp) @@ -167,3 +180,36 @@ sce_to_seurat <- function( return(sobj) } + + +#' Add a dummy feature to a SingleCellExperiment object with all zeros +#' +#' @param sce A SingleCellExperiment object. Should contain only one row/feature. +#' If larger, it will be returned unmodified. +#' @param feature_name The name to give to the new dummy feature +#' +#' @returns A SingleCellExperiment object with a dummy feature added (all zeros) +add_dummy_feature <- function(sce, feature_name = "null-feature") { + # add a dummy feature an SCE object + if (nrow(sce) > 1) { + message( + "No need for a dummy feature in a SingleCellExperiment object", + " with more than one row. Returning original object." + ) + return(sce) + } + if (feature_name %in% rownames(sce)) { + stop("A feature named `", feature_name, "` already exists in the SingleCellExperiment object.") + } + + # duplicate the existing row, rename + sce <- sce[c(1, 1), ] + rownames(sce) <- c(rownames(sce)[1], feature_name) + # set all rowData to NA, all assays to 0 + rowData(sce)[feature_name, ] <- NA + assays(sce) <- assays(sce) |> purrr::map(\(x) { + x[feature_name, ] <- 0 + return(x) + }) + return(sce) +} diff --git a/man/add_dummy_feature.Rd b/man/add_dummy_feature.Rd new file mode 100644 index 0000000..caf5f49 --- /dev/null +++ b/man/add_dummy_feature.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/make-seurat.R +\name{add_dummy_feature} +\alias{add_dummy_feature} +\title{Add a dummy feature to a SingleCellExperiment object with all zeros} +\usage{ +add_dummy_feature(sce, feature_name = "null-feature") +} +\arguments{ +\item{sce}{A SingleCellExperiment object. Should contain only one row/feature. +If larger, it will be returned unmodified.} + +\item{feature_name}{The name to give to the new dummy feature} +} +\value{ +A SingleCellExperiment object with a dummy feature added (all zeros) +} +\description{ +Add a dummy feature to a SingleCellExperiment object with all zeros +} diff --git a/rOpenScPCA.Rproj b/rOpenScPCA.Rproj index ea83efd..42e57c1 100644 --- a/rOpenScPCA.Rproj +++ b/rOpenScPCA.Rproj @@ -1,4 +1,5 @@ Version: 1.0 +ProjectId: e30188e8-608a-431a-87d2-5349807b3970 RestoreWorkspace: No SaveWorkspace: No diff --git a/tests/testthat/test-make-seurat.R b/tests/testthat/test-make-seurat.R index 941348d..e5d62ce 100644 --- a/tests/testthat/test-make-seurat.R +++ b/tests/testthat/test-make-seurat.R @@ -238,3 +238,44 @@ test_that("Conversion works for non-processed samples", { expect_null(names(seurat_obj@reductions)) }) + +test_that("conversion works with 1 feature altExps", { + sce <- readRDS(test_path("data", "scpca_sce.rds")) + altsce <- sce[1, ] + rownames(altsce) <- c("F1") + altExps(sce) <- list( + alt1 = altsce + ) + + expect_warning(seurat_obj <- sce_to_seurat(sce, use_symbols = FALSE)) + expect_s4_class(seurat_obj, "Seurat") + + expect_setequal(names(seurat_obj@assays), c("RNA", "spliced", "alt1")) + expect_equal(Seurat::DefaultAssay(seurat_obj), "RNA") + + # assay types + expect_s4_class(seurat_obj[["RNA"]], "Assay5") + expect_s4_class(seurat_obj[["spliced"]], "Assay5") + expect_s4_class(seurat_obj[["alt1"]], "Assay5") + + expect_equal(nrow(seurat_obj[["alt1"]]), 2) + expect_setequal(rownames(seurat_obj[["alt1"]]), c("F1", "null-feature")) + expect_setequal(colnames(seurat_obj[["alt1"]]), colnames(sce)) + + + # test v3 conversion does not add a row + expect_no_warning(seurat_obj <- sce_to_seurat(sce, use_symbols = FALSE, seurat_assay_version = "v3")) + expect_s4_class(seurat_obj, "Seurat") + + expect_setequal(names(seurat_obj@assays), c("RNA", "spliced", "alt1")) + expect_equal(Seurat::DefaultAssay(seurat_obj), "RNA") + + # assay types + expect_s4_class(seurat_obj[["RNA"]], "Assay") + expect_s4_class(seurat_obj[["spliced"]], "Assay") + expect_s4_class(seurat_obj[["alt1"]], "Assay") + + expect_equal(nrow(seurat_obj[["alt1"]]), 1) + expect_setequal(rownames(seurat_obj[["alt1"]]), c("F1")) + expect_setequal(colnames(seurat_obj[["alt1"]]), colnames(sce)) +})