Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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')
)
})