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 uns #126

Merged
merged 14 commits into from
Dec 2, 2023
19 changes: 19 additions & 0 deletions R/AbstractAnnData.R
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ AbstractAnnData <- R6::R6Class("AbstractAnnData", # nolint
#' with all elements having the same number of rows and columns as `var`.
varp = function(value) {
.abstract_function("ad$varp")
},
#' @field uns The uns slot. Must be `NULL` or a named list.
uns = function(value) {
.abstract_function("ad$uns")
}
),
public = list(
Expand Down Expand Up @@ -236,6 +240,21 @@ AbstractAnnData <- R6::R6Class("AbstractAnnData", # nolint
collection
},

# @description `.validate_named_list()` checks for whether a value
# is NULL or a named list and throws an error if it is not.
.validate_named_list = function(collection, label) {
if (is.null(collection)) {
return(collection)
}

collection_names <- names(collection)
if (!is.list(collection) || ((length(collection) != 0) && is.null(collection_names))) {
stop(paste0(label, " must be a named list, was ", class(collection)))
}

collection
},

# @description `.validate_obsvar_dataframe()` checks that the
# object is a data.frame and removes explicit dimnames.
# @param df A data frame to validate. Should be an obs or a var.
Expand Down
8 changes: 6 additions & 2 deletions R/AnnData.R
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
#' @param varp The varp slot is used to store sparse multi-dimensional
#' annotation arrays. It must be either `NULL` or a named list, where each
#' element is a sparse matrix where each dimension has length `n_vars`.
#' @param uns The uns slot is used to store unstructured annotation. It must
#' be either `NULL` or a named list.
#'
#' @export
#'
Expand Down Expand Up @@ -67,7 +69,8 @@ AnnData <- function(
obsm = NULL,
varm = NULL,
obsp = NULL,
varp = NULL) {
varp = NULL,
uns = NULL) {
InMemoryAnnData$new(
obs_names = obs_names,
var_names = var_names,
Expand All @@ -78,6 +81,7 @@ AnnData <- function(
obsm = obsm,
varm = varm,
obsp = obsp,
varp = varp
varp = varp,
uns = uns
)
}
23 changes: 19 additions & 4 deletions R/HDF5AnnData.R
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ HDF5AnnData <- R6::R6Class("HDF5AnnData", # nolint
.n_vars = NULL,
.obs_names = NULL,
.var_names = NULL,
.obsm = NULL,
.varm = NULL,
.obsp = NULL,
.varp = NULL,
.compression = NULL
),
active = list(
Expand Down Expand Up @@ -191,6 +187,17 @@ HDF5AnnData <- R6::R6Class("HDF5AnnData", # nolint
write_h5ad_data_frame_index(value, private$.h5obj, "var", private$.compression, "_index")
private$.var_names <- value
}
},
#' @field uns The uns slot. Must be `NULL` or a named list.
uns = function(value) {
if (missing(value)) {
# trackstatus: class=HDF5AnnData, feature=get_uns, status=done
read_h5ad_element(private$.h5obj, "uns")
} else {
# trackstatus: class=HDF5AnnData, feature=set_uns, status=done
value <- private$.validate_named_list(value, "uns")
write_h5ad_element(value, private$.h5obj, "/uns")
}
}
),
public = list(
Expand Down Expand Up @@ -229,6 +236,8 @@ HDF5AnnData <- R6::R6Class("HDF5AnnData", # nolint
#' @param varp The varp slot is used to store sparse multi-dimensional
#' annotation arrays. It must be either `NULL` or a named list, where each
#' element is a sparse matrix where each dimension has length `n_vars`.
#' @param uns The uns slot is used to store unstructured annotation. It must
#' be either `NULL` or a named list.
#' @param compression The compression algorithm to use when writing the
#' HDF5 file. Can be one of `"none"`, `"gzip"` or `"lzf"`. Defaults to
#' `"none"`.
Expand All @@ -251,6 +260,7 @@ HDF5AnnData <- R6::R6Class("HDF5AnnData", # nolint
varm = NULL,
obsp = NULL,
varp = NULL,
uns = NULL,
compression = c("none", "gzip", "lzf")) {
if (!requireNamespace("rhdf5", quietly = TRUE)) {
stop("The HDF5 interface requires the 'rhdf5' package to be installed")
Expand Down Expand Up @@ -333,6 +343,10 @@ HDF5AnnData <- R6::R6Class("HDF5AnnData", # nolint
if (!is.null(varp)) {
self$varp <- varp
}

if (!is.null(uns)) {
self$uns <- uns
}
},

#' @description Number of observations in the AnnData object
Expand Down Expand Up @@ -400,6 +414,7 @@ to_HDF5AnnData <- function(adata, file, compression = c("none", "gzip", "lzf"))
layers = adata$layers,
obsp = adata$obsp,
varp = adata$varp,
uns = adata$uns,
compression = compression
)
}
23 changes: 20 additions & 3 deletions R/InMemoryAnnData.R
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ InMemoryAnnData <- R6::R6Class("InMemoryAnnData", # nolint
.obsm = NULL,
.varm = NULL,
.obsp = NULL,
.varp = NULL
.varp = NULL,
.uns = NULL
),
active = list(
#' @field X NULL or an observation x variable matrix (without
Expand Down Expand Up @@ -204,6 +205,17 @@ InMemoryAnnData <- R6::R6Class("InMemoryAnnData", # nolint
)
self
}
},
#' @field uns The uns slot. Must be `NULL` or a named list.
uns = function(value) {
if (missing(value)) {
# trackstatus: class=InMemoryAnnData, feature=get_uns, status=done
private$.uns
} else {
# trackstatus: class=InMemoryAnnData, feature=set_uns, status=done
private$.uns <- private$.validate_named_list(value, "uns")
self
}
}
),
public = list(
Expand Down Expand Up @@ -242,6 +254,8 @@ InMemoryAnnData <- R6::R6Class("InMemoryAnnData", # nolint
#' @param varp The varp slot is used to store sparse multi-dimensional
#' annotation arrays. It must be either `NULL` or a named list, where each
#' element is a sparse matrix where each dimension has length `n_vars`.
#' @param uns The uns slot is used to store unstructured annotation.
#' It must be either `NULL` or a named list.
initialize = function(obs_names,
var_names,
X = NULL,
Expand All @@ -251,7 +265,8 @@ InMemoryAnnData <- R6::R6Class("InMemoryAnnData", # nolint
obsm = NULL,
varm = NULL,
obsp = NULL,
varp = NULL) {
varp = NULL,
uns = NULL) {
# write obs and var first, because these are used by other validators
self$obs_names <- obs_names
self$var_names <- var_names
Expand All @@ -265,6 +280,7 @@ InMemoryAnnData <- R6::R6Class("InMemoryAnnData", # nolint
self$varm <- varm
self$obsp <- obsp
self$varp <- varp
self$uns <- uns
}
)
)
Expand Down Expand Up @@ -308,6 +324,7 @@ to_InMemoryAnnData <- function(adata) { # nolint
obsm = adata$obsm,
varm = adata$varm,
obsp = adata$obsp,
varp = adata$varp
varp = adata$varp,
uns = adata$uns
)
}
9 changes: 8 additions & 1 deletion R/read_h5ad_helpers.R
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,14 @@ read_h5ad_string_scalar <- function(file, name, version = "0.2.0") {
#' @noRd
read_h5ad_numeric_scalar <- function(file, name, version = "0.2.0") {
version <- match.arg(version)
rhdf5::h5read(file, name)
scalar <- rhdf5::h5read(file, name)

# If the numeric vector is Boolean it gets read as a factor by {rhdf5}
if (is.factor(scalar)) {
scalar <- as.logical(scalar)
}

return(scalar)
}

#' Read H5AD mapping
Expand Down
6 changes: 4 additions & 2 deletions R/write_h5ad.R
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
#'
#' # Write a SingleCellExperiment as an H5AD
#' if (requireNamespace("SingleCellExperiment", quietly = TRUE)) {
#' h5ad_file <- tempfile(fileext = ".h5ad")
#' ncells <- 100
#' counts <- matrix(rpois(20000, 5), ncol = ncells)
#' logcounts <- log2(counts + 1)
Expand All @@ -41,12 +40,14 @@
#' assays = list(counts = counts, logcounts = logcounts),
#' reducedDims = list(PCA = pca, tSNE = tsne)
#' )
#'
#' h5ad_file <- tempfile(fileext = ".h5ad")
#' write_h5ad(sce, h5ad_file)
#' }
#'
#' # Write a Seurat as a H5AD
#' if (requireNamespace("SeuratObject", quietly = TRUE)) {
#' # TODO: uncomment this code when the seurat converter is fixed
#' # h5ad_file <- tempfile(fileext = ".h5ad")
#' # counts <- matrix(1:15, 3L, 5L)
#' # dimnames(counts) <- list(
#' # letters[1:3],
Expand All @@ -63,6 +64,7 @@
#' # )
#' # obj <- SeuratObject::AddMetaData(obj, cell.metadata)
#' #
#' # h5ad_file <- tempfile(fileext = ".h5ad")
#' # write_h5ad(obj, h5ad_file)
#' }
write_h5ad <- function(object, path, compression = c("none", "gzip", "lzf")) {
Expand Down
5 changes: 4 additions & 1 deletion R/write_h5ad_helpers.R
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ write_h5ad_element <- function(value, file, name, compression = c("none", "gzip"
} else if (is.logical(value)) { # Logical values
if (any(is.na(value))) {
write_h5ad_nullable_boolean
} else if (length(value) == 1) {
# Single Booleans should be written as numeric scalars
write_h5ad_numeric_scalar
} else {
write_h5ad_dense_array
}
Expand Down Expand Up @@ -324,7 +327,7 @@ write_h5ad_numeric_scalar <- function(value, file, name, compression, version =
hdf5_write_compressed(file, name, value, compression)

# Write attributes
write_h5ad_encoding(file, name, "numeric", version)
write_h5ad_encoding(file, name, "numeric-scalar", version)
}

#' Write H5AD mapping
Expand Down
2 changes: 2 additions & 0 deletions man/AbstractAnnData.Rd

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

6 changes: 5 additions & 1 deletion man/AnnData.Rd

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

6 changes: 6 additions & 0 deletions man/HDF5AnnData.Rd

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

8 changes: 7 additions & 1 deletion man/InMemoryAnnData.Rd

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

6 changes: 4 additions & 2 deletions man/write_h5ad.Rd

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

Loading