Skip to content

Commit 36e5957

Browse files
committed
feat: add subset_seurat function for automatic layer repair in Seurat v5 objects
1 parent 4edb816 commit 36e5957

File tree

3 files changed

+126
-2
lines changed

3 files changed

+126
-2
lines changed

R/markersplot.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ MarkersPlot <- function(
380380
# subset the object to only include the comparison groups
381381
comp_groups <- unique(unlist(strsplit(unique(as.character(markers[[comparison_by_1]])), ":")))
382382
if (length(comp_groups) > 1) {
383-
object <- subset(object, subset = !!rlang::sym(comparison_by_2) %in% comp_groups)
383+
object <- subset_seurat(object, subset = !!rlang::sym(comparison_by_2) %in% comp_groups)
384384
} else {
385385
object@meta.data[[comparison_by_2]] <- ifelse(object@meta.data[[comparison_by_2]] == comp_groups, comp_groups, "Other")
386386
object@meta.data[[comparison_by_2]] <- factor(object@meta.data[[comparison_by_2]], levels = c(comp_groups, "Other"))
@@ -389,7 +389,7 @@ MarkersPlot <- function(
389389
# subset the object to only include the selected genes
390390
object <- tryCatch({
391391
# In case the features do not exist in some assays
392-
subset(object, features = genes)
392+
subset_seurat(object, features = genes)
393393
}, error = function(e) {
394394
object
395395
})

R/utils.R

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,94 @@ h5group_to_matrix <- function(x) {
7373
dims = rev(shape)
7474
)
7575
}
76+
77+
#' Subset function with automatic layer repair for Seurat v5 objects
78+
#'
79+
#' This function handles corrupted layers in Seurat v5 assays that can cause
80+
#' "incorrect number of dimensions" errors during subsetting.
81+
#'
82+
#' Strategy:
83+
#' 1. Evaluate subset normally
84+
#' 2. Check for corrupted layers proactively
85+
#' 3. Repair any corrupted layers before subsetting
86+
#' 4. Perform the subset operation
87+
#' 5. If subset still fails, re-throw the error
88+
#'
89+
#' Example: subset_seurat(obj, EFS %in% c("EFS_L", "EFS_S"), nCount_RNA > 1000)
90+
#' @param object Seurat object to subset
91+
#' @param ... Arguments passed to `subset()`
92+
#' @return Subsetted Seurat object with repaired layers if needed
93+
#' @keywords internal
94+
subset_seurat <- function(object, ...) {
95+
# Try subsetting first
96+
result <- tryCatch({
97+
subset(object, ...)
98+
}, error = function(e) {
99+
if (grepl("incorrect number of dimensions", e$message)) {
100+
warning("Subsetting failed due to corrupted layers. Attempting to repair...", call. = FALSE)
101+
102+
# Scan and repair assays with multiple layers
103+
for (assay_name in names(object@assays)) {
104+
assay <- object@assays[[assay_name]]
105+
106+
# Check if it's a v5 assay with layers
107+
if (inherits(assay, "Assay5") && length(assay@layers) > 1) {
108+
cat(sprintf("Scanning assay '%s' with %d layers...\n", assay_name, length(assay@layers)))
109+
110+
# Identify intact and corrupted layers
111+
intact_layers <- list()
112+
corrupted_layers <- character()
113+
114+
for (layer_name in names(assay@layers)) {
115+
layer <- assay@layers[[layer_name]]
116+
if (is.matrix(layer) || inherits(layer, "dgCMatrix")) {
117+
intact_layers[[layer_name]] <- layer
118+
} else {
119+
corrupted_layers <- c(corrupted_layers, layer_name)
120+
}
121+
}
122+
123+
# If all layers are corrupted, remove the assay
124+
if (length(intact_layers) == 0) {
125+
warning(sprintf("All layers in assay '%s' are corrupted. Removing assay.", assay_name), call. = FALSE)
126+
object@assays[[assay_name]] <- NULL
127+
} else {
128+
# Restore corrupted layers based on intact ones
129+
if (length(corrupted_layers) > 0) {
130+
# Get dimensions from first intact layer
131+
template_layer <- intact_layers[[1]]
132+
n_features <- nrow(template_layer)
133+
n_cells <- ncol(template_layer)
134+
135+
for (layer_name in corrupted_layers) {
136+
corrupted_data <- assay@layers[[layer_name]]
137+
138+
# Check if it's a 1D vector with correct number of features
139+
if (is.vector(corrupted_data) && length(corrupted_data) == n_features) {
140+
# Restore as single-column sparse matrix
141+
warning(sprintf("Restoring corrupted layer '%s' in assay '%s' (1D vector -> sparse matrix)",
142+
layer_name, assay_name), call. = FALSE)
143+
object@assays[[assay_name]]@layers[[layer_name]] <-
144+
Matrix::Matrix(corrupted_data, nrow = n_features, ncol = 1, sparse = TRUE)
145+
} else {
146+
# Cannot restore, remove it
147+
warning(sprintf("Cannot restore layer '%s' in assay '%s' (incompatible dimensions). Removing.",
148+
layer_name, assay_name), call. = FALSE)
149+
object@assays[[assay_name]]@layers[[layer_name]] <- NULL
150+
}
151+
}
152+
}
153+
}
154+
}
155+
}
156+
157+
# Try subsetting again after repairs
158+
subset(object, ...)
159+
} else {
160+
# Re-throw other errors
161+
stop(e)
162+
}
163+
})
164+
165+
return(result)
166+
}

man/subset_seurat.Rd

Lines changed: 33 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)