Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

from_Seurat updates #118

Merged
merged 12 commits into from
Sep 19, 2023
88 changes: 55 additions & 33 deletions R/Seurat.R
Original file line number Diff line number Diff line change
Expand Up @@ -105,22 +105,46 @@ to_Seurat <- function(obj) { # nolint
#' @rdname Seurat
#'
#' @description `from_Seurat()` converts a Seurat object to an AnnData object.
#' Only one assay can be converted at a time.
#'
#' @param seurat_obj An object inheriting from Seurat.
#'
#' @param output_class Name of the AnnData class. Must be one of `"HDF5AnnData"`
#' or `"InMemoryAnnData"`.
#'
#' @param output_class Name of the AnnData class. Must be one of `"HDF5AnnData"` or `"InMemoryAnnData"`.
#' @param assay Assay to be converted. If NULL, `DefaultAssay()` is used.
#' @param X Which of 'counts', 'data', or 'scale.data' will be used for X. By default, 'counts' will be used (if it is
#' not empty), followed by 'data', then 'scale.data'. The remaining non-empty slots will be stored in different
#' layers.
#' @param ... Additional arguments passed to the generator function.
#' See the "Details" section for more information on which parameters
#'
#' @export
# TODO: Add parameter to choose which how counts, data and scaled.data are translated into X and layers
# TODO: add tests with Seurat objects not created by anndataR
from_Seurat <- function(seurat_obj, output_class = c("InMemoryAnnData", "HDF5AnnData"), ...) { # nolint
from_Seurat <- function(seurat_obj, output_class = c("InMemoryAnnData", "HDF5AnnData"), assay = NULL, X = "counts", ...) { # nolint

stopifnot(inherits(seurat_obj, "Seurat"))

if (!is.null(X)) {
if (!X %in% c("counts", "data", "scale.data")) {
stop("X must be NULL or one of: 'counts', 'data', 'scale.data'")
}
}

# If a specific assay is selected, use it
if (!is.null(assay)) {
if (!assay %in% names(seurat_obj@assays)) {
stop("'assay' must be NULL or one of: ", paste0("'", names(seurat_obj@assays), "'", collapse = ", "))
}
assay_name <- assay
} else {
assay_name <- SeuratObject::DefaultAssay(seurat_obj)

# If Seurat object contains multiple assays, notify user the Default one is used
if (length(names(seurat_obj@assays)) > 1) {
message(
"There are ", length(names(seurat_obj@assays)), " assays in the Seurat object; using the default assay ('",
assay_name, "'). You can use the `assay` parameter to select a specific assay."
)
}
}

# get obs_names
# trackstatus: class=Seurat, feature=set_obs_names, status=done
obs_names <- colnames(seurat_obj)
Expand All @@ -132,11 +156,11 @@ from_Seurat <- function(seurat_obj, output_class = c("InMemoryAnnData", "HDF5Ann

# construct var_names
# trackstatus: class=Seurat, feature=set_var_names, status=done
var_names <- rownames(seurat_obj)
var_names <- rownames(seurat_obj@assays[[assay_name]])

# construct var
# trackstatus: class=Seurat, feature=set_var, status=done
var <- seurat_obj@assays[[[email protected]]]@meta.features
var <- seurat_obj@assays[[assay_name]]@meta.features
rownames(var) <- NULL

# use generator to create new AnnData object
Expand All @@ -149,33 +173,31 @@ from_Seurat <- function(seurat_obj, output_class = c("InMemoryAnnData", "HDF5Ann
...
)

# trackstatus: class=Seurat, feature=set_X, status=wip
# trackstatus: class=Seurat, feature=set_layers, status=wip
for (assay_name in names(seurat_obj@assays)) {
# TODO: Maybe we shouldn't use counts but instead data
assay_data <- SeuratObject::GetAssayData(seurat_obj, "counts", assay = assay_name)

if (nrow(assay_data) != length(var_names) || !identical(rownames(assay_data), var_names)) {
warning(
"Skipping assay '", assay_name, "' because it has different feature names ",
"than the active assay ('", [email protected], "')."
)
next
}
if (ncol(assay_data) != length(obs_names) || !identical(colnames(assay_data), obs_names)) {
warning(
"Skipping assay '", assay_name, "' because it has different cell names ",
"than the active assay ('", [email protected], "')."
)
next
if (!is.null(X)) {
# Check if the slot is not empty
if (all(dim(SeuratObject::GetAssayData(seurat_obj, slot = X, assay = assay_name)) == 0)) {
stop("The '", X, "' slot is empty.")
}

# remove names
assay_data <- SeuratObject::GetAssayData(seurat_obj, slot = X, assay = assay_name)

# Remove names
dimnames(assay_data) <- list(NULL, NULL)
if (assay_name == [email protected]) {
ad$X <- Matrix::t(assay_data)
} else {
ad$layers[[assay_name]] <- Matrix::t(assay_data)
ad$X <- Matrix::t(assay_data)
} else {
# Cannot compare other values with NULL
X <- "none"
}

# Add the remaining non-empty slots as layers
slots <- c("counts", "data", "scale.data")
slots <- slots[slots != X]

for (slot in slots) {
if (!all(dim(SeuratObject::GetAssayData(seurat_obj, slot = slot)) == 0)) {
assay_data <- SeuratObject::GetAssayData(seurat_obj, slot = slot, assay = assay_name)
dimnames(assay_data) <- list(NULL, NULL)
ad$layers[[slot]] <- Matrix::t(assay_data)
}
}

Expand Down
15 changes: 11 additions & 4 deletions man/Seurat.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading