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

Add support for assays stored as SeuratObject::Assay5 #23

Merged
merged 3 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ Roxygen: list(markdown = TRUE)
RoxygenNote: 7.2.3
Suggests:
testthat (>= 3.0.0),
Matrix
Matrix,
SeuratObject
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used in the tests to create a Seurat dataset with a v5 assay. running devtools::check complains if we don't add this as a dependency. For test dependencies it is recommended to add these to suggests

Config/testthat/edition: 3
Imports:
methods,
Expand Down
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Generated by roxygen2: do not edit by hand

export(counts_matrix_from_assay)
export(create_bugreport)
export(create_bugreport_from_seurat)
export(create_loupe)
Expand All @@ -18,6 +19,7 @@ importFrom(Seurat,Idents)
importFrom(Seurat,Reductions)
importFrom(hdf5r,H5File)
importFrom(hdf5r,H5T_STRING)
importFrom(methods,as)
importFrom(methods,is)
importFrom(utils,download.file)
importFrom(utils,packageVersion)
Expand Down
3 changes: 2 additions & 1 deletion R/err.R
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@ create_bugreport_from_seurat <- function(obj) {
assay <- namedAssay[[1]]
clusters <- select_clusters(obj)
projections <- select_projections(obj)
counts <- counts_matrix_from_assay(assay)

create_bugreport(
assay@counts,
counts,
clusters,
projections,
assay_name = assay_name,
Expand Down
3 changes: 2 additions & 1 deletion R/lib.R
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ create_loupe_from_seurat <- function(

assay_name <- names(namedAssay)
assay <- namedAssay[[1]]
counts <- counts_matrix_from_assay(assay)

clusters <- select_clusters(obj, dedup=dedup_clusters)
projections <- select_projections(obj)
Expand All @@ -58,7 +59,7 @@ create_loupe_from_seurat <- function(
}

success <- create_loupe(
assay@counts,
counts,
clusters=clusters,
projections=projections,
output_dir=output_dir,
Expand Down
23 changes: 22 additions & 1 deletion R/util.R
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ select_assay <- function(obj) {
for (i in seq_along(assay_priority)) {
name <- names(assay_priority[i])
assay <- Seurat::GetAssay(obj, assay=name)
counts <- counts_matrix_from_assay(assay)

if (length(assay@counts) > 0) {
if (length(counts) > 0) {
result = list()
result[[name]] = assay
return(result)
Expand All @@ -67,6 +68,26 @@ select_assay <- function(obj) {
NULL
}

#' Extract the counts matrix from the Assay
#'
#' @param assay A SeuratObject::Assay or SeuratObject::Assay5
#'
#' @return A sparse counts matrix

#' @importFrom methods as
#'
#' @export
counts_matrix_from_assay <- function(assay) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding this method to get the counts matrix from either of the assay versions instead of updating the select_assay method to just return the counts matrix directly. Doing this so that I don't break anyone's code if they are relying on select_assay

# convert to older format so that we can consistently access the counts matrix
# Seurat::Assay5 was introduced with Seurat-5.0 and stores the data completely differently
# within the `layers` slot.
if (is(assay, "Assay5")) {
assay <- suppressWarnings(methods::as(object = assay, Class = 'Assay'))
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

During conversion using as lots of warning pop up if the assay is missing fields even thought the conversion still succeeds.

Another alternative would be to do something like assay@layers[["counts"]] as it seems this is where the counts matrix is stored. By using the as conversion method we don't have to worry if the location changes.

conversion logic can be seen here

}

assay@counts
}

#' Select clusters from the assay
#'
#' @param obj A Seurat Object
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,12 @@ library("loupeR")
# Gene Expression RNA assay
assay <- seurat_obj[["RNA"]]

# get counts matrix from either the old or newer formats of assay
counts <- counts_matrix_from_assay(assay)

# convert the count matrix, clusters, and projections into a Loupe file
create_loupe(
assay@counts,
counts,
clusters = select_clusters(seurat_obj),
projections = select_projections(seurat_obj)
)
Expand Down
17 changes: 17 additions & 0 deletions man/counts_matrix_from_assay.Rd

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

24 changes: 19 additions & 5 deletions tests/testthat/test-util.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,29 @@ test_that("select_assay selects active assay", {
rna3 <- create_count_mat(5, 5)

obj <- Seurat::CreateSeuratObject(rna1, assay="rna1")
obj[["rna2_"]] = Seurat::CreateAssayObject(rna2, key="rna2_")
obj[["rna3_"]] = Seurat::CreateAssayObject(rna3, key="rna3_")
obj[["rna2"]] = Seurat::CreateAssayObject(rna2, key="rna2_")
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated change, but the names here didn't need to have an underscore even though the key parameter to CreateAssayObject requires it

obj[["rna3"]] = Seurat::CreateAssayObject(rna3, key="rna3_")

expect_equal(Seurat::DefaultAssay(object = obj), "rna1")
Seurat::DefaultAssay(object = obj) <- "rna2_"
expect_equal(Seurat::DefaultAssay(object = obj), "rna2_")
Seurat::DefaultAssay(object = obj) <- "rna2"
expect_equal(Seurat::DefaultAssay(object = obj), "rna2")

assay <- select_assay(obj)
expect_equal(names(assay), "rna2_")
expect_equal(names(assay), "rna2")
})

test_that("counts_matrix_from_assay works on different assay version", {
rna1 <- create_count_mat(5, 5)
rna2 <- create_count_mat(5, 5)
rna3 <- create_count_mat(5, 5)

obj <- Seurat::CreateSeuratObject(rna1, assay="rna1")
obj[["rna2"]] = Seurat::CreateAssayObject(rna2, key="rna2_")
obj[["rna3"]] = SeuratObject::CreateAssay5Object(rna3, key="rna3_")
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Seurat package doesn't export the underlying v5 create method like it does with the non v5.

Comment on lines +19 to +25
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
rna1 <- create_count_mat(5, 5)
rna2 <- create_count_mat(5, 5)
rna3 <- create_count_mat(5, 5)
obj <- Seurat::CreateSeuratObject(rna1, assay="rna1")
obj[["rna2"]] = Seurat::CreateAssayObject(rna2, key="rna2_")
obj[["rna3"]] = SeuratObject::CreateAssay5Object(rna3, key="rna3_")
rna <- create_count_mat(5, 5)
obj <- Seurat::CreateSeuratObject(rna, assay="rna1")
obj[["rna2"]] = Seurat::CreateAssayObject(rna, key="rna2_")
obj[["rna3"]] = SeuratObject::CreateAssay5Object(rna, key="rna3_")

R will pass function arguments by value rather than reference so you shouldn't need to construct multiple instances yourself

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually want to create three different matrices because I want their values to be different for the tests. Turns out R is copy by value, but only if the value is modified.


expect_equal(counts_matrix_from_assay(obj[["rna1"]]), rna1)
expect_equal(counts_matrix_from_assay(obj[["rna2"]]), rna2)
expect_equal(counts_matrix_from_assay(obj[["rna3"]]), rna3)
})

test_that("select_clusters selects Idents", {
Expand Down