Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
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
2 changes: 1 addition & 1 deletion apis/python/examples/soco-batch-query.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
for i, ctot_id in enumerate(ctot_ids):
soma_slices = soco.query(
obs_attrs=["cell_type_ontology_term_id"],
obs_query_string=f'cell_type_ontology_term_id == "{ctot_id}"',
obs_query_string=f'cell_type_ontology_term_id == {ctot_id!r}',
)
if soma_slices == []:
continue
Expand Down
2 changes: 1 addition & 1 deletion apis/python/examples/uniformizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def main() -> int:
return uniformizer.add_soma(args.dataset_id, args.soma)
else:
raise Exception(
f'Internal coding error: handler for "{args.func_name}" not found.'
f'Internal coding error: handler for {args.func_name!r} not found.'
)


Expand Down
2 changes: 1 addition & 1 deletion apis/python/src/tiledbsoma/annotation_dataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def __repr__(self) -> str:
"""
Default display of soma.obs and soma.var.
"""
return ", ".join(f"'{key}'" for key in self.keys())
return ", ".join(f"{key!r}" for key in self.keys())

# ----------------------------------------------------------------
def __len__(self) -> int:
Expand Down
4 changes: 2 additions & 2 deletions apis/python/src/tiledbsoma/annotation_matrix_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def __repr__(self) -> str:
"""
Default display of soma.obsm and soma.varm.
"""
return ", ".join(f"'{key}'" for key in self.keys())
return ", ".join(f"{key!r}" for key in self.keys())

# ----------------------------------------------------------------
def __iter__(self) -> Iterator[AnnotationMatrix]:
Expand All @@ -68,7 +68,7 @@ def __getattr__(self, name: str) -> Optional[AnnotationMatrix]:
with self._open() as G:
if name not in G:
raise AttributeError(
f"'{self.__class__.__name__}' object has no attribute '{name}'"
f"{self.__class__.__name__!r} object has no attribute {name!r}"
)
return self[name]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def __repr__(self) -> str:
"""
Default display of soma.obsp and soma.varp.
"""
return ", ".join(f"'{key}'" for key in self.keys())
return ", ".join(f"{key!r}" for key in self.keys())

# ----------------------------------------------------------------
def __getattr__(self, name: str) -> Optional[AssayMatrix]:
Expand All @@ -68,7 +68,7 @@ def __getattr__(self, name: str) -> Optional[AssayMatrix]:
with self._open() as G:
if name not in G:
raise AttributeError(
f"'{self.__class__.__name__}' object has no attribute '{name}'"
f"{self.__class__.__name__!r} object has no attribute {name!r}"
)
return self[name]

Expand Down
4 changes: 2 additions & 2 deletions apis/python/src/tiledbsoma/assay_matrix_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def __repr__(self) -> str:
"""
Default display of soma.X.
"""
return ", ".join(f"'{key}'" for key in self.keys())
return ", ".join(f"{key!r}" for key in self.keys())

# ----------------------------------------------------------------
def __getattr__(self, name: str) -> Optional[AssayMatrix]:
Expand All @@ -65,7 +65,7 @@ def __getattr__(self, name: str) -> Optional[AssayMatrix]:
with self._open() as G:
if name not in G:
raise AttributeError(
f"'{self.__class__.__name__}' object has no attribute '{name}'"
f"{self.__class__.__name__!r} object has no attribute {name!r}"
)
return self[name]

Expand Down
2 changes: 1 addition & 1 deletion apis/python/src/tiledbsoma/util_tiledb.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def show_tiledb_group_array_schemas(uri: str, ctx: Optional[tiledb.Ctx] = None)

# ----------------------------------------------------------------
def list_fragments(array_uri: str) -> None:
print(f"Listing fragments for array: '{array_uri}'")
print(f"Listing fragments for array: {array_uri!r}")
vfs = tiledb.VFS()

fragments = []
Expand Down
2 changes: 1 addition & 1 deletion apis/r/DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Description: Interface for working with 'TileDB'-based Stack of Matrices,
from and export to in-memory formats used by popular toolchains like
'Seurat', 'Bioconductor', and even 'AnnData' using the companion Python
package.
Version: 0.1.22.9000
Version: 0.1.22.9001
Authors@R: c(
person(given = "Aaron",
family = "Wolen",
Expand Down
7 changes: 4 additions & 3 deletions apis/r/NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

## Features

* New function `dataset_seurat_pbmc3k()` to download the pbmc3k dataset from 10X and import as a `Seurat` object without requiring any extra dependencies.
- The `SOMACollection`'s `to_seurat()` method gains a `somas` argument that makes it possible to select a subset of `SOMA`s and `X` layers to be retrieved (#571).
- New function `dataset_seurat_pbmc3k()` to download the pbmc3k dataset from 10X and import as a `Seurat` object without requiring any extra dependencies.

# tiledbsoma 0.1.19

Expand All @@ -14,15 +15,15 @@

## Features

- The `AnnotationMatrix`'s `to_matrix()` method now supports batched reads via the `batch_mode` argument. This functionality can also be leveraged from `SOMA`'s `get_seurat_dimreductions_list()` and `get_seurat_dimreduction()` methods.
- The `AnnotationMatrix`'s `to_matrix()` method now supports batched reads via the `batch_mode` argument. This functionality can also be leveraged from `SOMA`'s `get_seurat_dimreductions_list()` and `get_seurat_dimreduction()` methods (#548).

## Changes

- Decreased capacity of `AssayMatrix` arrays from 100,000 to 1,000 to improve remote-read performance (#543)

## Fixes

- Don't use default assay name when recreating a `Seurat` object (thanks @dan11mcguire)
- Don't use default assay name when recreating a `Seurat` object (thanks @dan11mcguire).

# tiledbsoma 0.1.12

Expand Down
51 changes: 43 additions & 8 deletions apis/r/R/SOMACollection.R
Original file line number Diff line number Diff line change
Expand Up @@ -199,14 +199,48 @@ SOMACollection <- R6::R6Class(
#' @description Convert to a [SeuratObject::Seurat] object.
#' @param project [`SeuratObject::Project`] name for the `Seurat` object
#' @param batch_mode logical, if `TRUE`, batch query mode is enabled for
#' retrieving `X`, `obsm`/`varm`, and `obsp`/`varp` layers. See
#' retrieving `X` layers. See
#' [`AssayMatrix$to_dataframe()`][`AssayMatrix`] for more information.
to_seurat = function(project = "SeuratProject", batch_mode = FALSE) {
stopifnot(is_scalar_character(project))
#' @param somas character vector, names of `SOMA`s to include as
#' [`SeuratObject::Assay`]s in the `Seurat` object. If `NULL`, all `SOMA`s
#' are included. Can also be a named list of character vectors, where each
#' element corresponds to a `SOMA` name and the value is a character vector
#' of `X` layers from that `SOMA` to include as assays (e.g., `list("RNA" =
#' c("counts", "logcounts"))`).
to_seurat = function(
project = "SeuratProject",
somas = NULL,
batch_mode = FALSE
) {
stopifnot(
is_scalar_character(project),
"'somas' must be a character vector or named list"
= is.null(somas) || is.character(somas) || is.list(somas)
)

# default list containing all somas and all layers
soma_list <- sapply(
names(self$somas),
FUN = function(x) c("counts", "data", "scale.data"),
simplify = FALSE
)

if (is.character(somas)) {
stopifnot(assert_subset(somas, names(soma_list)))
soma_list <- soma_list[somas]
} else if (is.list(somas)) {
stopifnot(assert_subset(names(somas), names(soma_list)))
soma_list <- somas
}

assays <- lapply(
X = self$somas,
FUN = function(x) x$to_seurat_assay(batch_mode = batch_mode)
assays <- mapply(
FUN = function(soma, layers, batch_mode) {
soma$to_seurat_assay(layers = layers, batch_mode = batch_mode)
},
soma = self$somas[names(soma_list)],
layers = soma_list,
MoreArgs = list(batch_mode = batch_mode),
SIMPLIFY = FALSE
)
nassays <- length(assays)

Expand Down Expand Up @@ -243,8 +277,9 @@ SOMACollection <- R6::R6Class(
# Retrieve list of all techniques used in any soma's obsm/varm
# dimensionality reduction arrays. The association between assay and
# dimreduction is maintained by the DimReduc's `assay.used` slot.
dimreductions <- lapply(self$somas,
function(x) x$get_seurat_dimreductions_list(batch_mode)
dimreductions <- lapply(
self$somas,
function(x) x$get_seurat_dimreductions_list()
)
object@reductions <- Reduce(base::c, dimreductions)

Expand Down
3 changes: 2 additions & 1 deletion apis/r/R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ file_path <- function(..., fsep = .Platform$file.sep) {
file.path(..., fsep = fsep)
}

#' Assert all values of `x` are a subset of `y`. @param x,y vectors of values
#' Assert all values of `x` are a subset of `y`.
#' @param x,y vectors of values
#' @param type A character vector of length 1 used in the error message
#' @return `TRUE` if all values of `x` are present in `y`, otherwise an
#' informative error is thrown with the missing values.
Expand Down
14 changes: 12 additions & 2 deletions apis/r/man/SOMACollection.Rd

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

15 changes: 15 additions & 0 deletions apis/r/tests/testthat/helpers.R
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,18 @@ with_allocation_size_preference <- function(value, .local_envir = parent.frame()
)
tiledb::set_allocation_size_preference(value)
}


#' Compare data.frames or matrices after sorting dimensions
#' Useful for comparing objects were numerically or lexicographically sorted
#' after being reconstituted from TileDB.
#' @noRd
expect_equal_after_ordering_dims <- function(object, expected, ...) {
stopifnot(
is.data.frame(object) || is_matrix(object),
is.data.frame(expected) || is_matrix(expected)
)

object <- object[rownames(expected), colnames(expected)]
testthat::expect_equal(object, expected, ...)
}
59 changes: 59 additions & 0 deletions apis/r/tests/testthat/test_SOMACollection_Seurat.R
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,62 @@ test_that("a dataset with empty cell identities is retrieved", {
# Should not trigger error
expect_silent(soco$to_seurat())
})


test_that("SOMAs and X layers can be subselected", {
tdb_uri <- withr::local_tempdir("test-multisoma-soco")

# add second assay
pbmc_small[["RNA2"]] <- SeuratObject::CreateAssayObject(
counts = SeuratObject::GetAssayData(pbmc_small[["RNA"]], "counts")
)

soco <- SOMACollection$new(uri = tdb_uri, verbose = TRUE)
soco$from_seurat(pbmc_small)

# verify both somas are present
expect_length(soco$somas, 2)

soco <- SOMACollection$new(uri = tdb_uri, verbose = TRUE)

# Both assays and all layers are retrieved by default
pbmc_small2 <- soco$to_seurat()
expect_setequal(
SeuratObject::Assays(pbmc_small2),
SeuratObject::Assays(pbmc_small)
)

expect_equal_after_ordering_dims(
SeuratObject::GetAssayData(pbmc_small2[["RNA"]], "counts"),
SeuratObject::GetAssayData(pbmc_small[["RNA"]], "counts")
)

expect_equal_after_ordering_dims(
SeuratObject::GetAssayData(pbmc_small2[["RNA"]], "data"),
SeuratObject::GetAssayData(pbmc_small[["RNA"]], "data")
)

expect_equal_after_ordering_dims(
SeuratObject::GetAssayData(pbmc_small2[["RNA"]], "scale.data"),
SeuratObject::GetAssayData(pbmc_small[["RNA"]], "scale.data")
)

# Only the RNA assay is retrieved
pbmc_small2 <- soco$to_seurat(somas = "RNA")
expect_equal(SeuratObject::Assays(pbmc_small2), "RNA")

# Only the RNA assay counts layer is retrieved
pbmc_small2 <- soco$to_seurat(somas = list(RNA = "counts"))
expect_equal(SeuratObject::Assays(pbmc_small2), "RNA")

# When the data layer is unset/empty it's set as identical to counts
expect_identical(
SeuratObject::GetAssayData(pbmc_small2[["RNA"]], "counts"),
SeuratObject::GetAssayData(pbmc_small2[["RNA"]], "data"),
)

expect_equal(
SeuratObject::GetAssayData(pbmc_small2[["RNA"]], "scale.data"),
new(Class = 'matrix')
)
})