From 00284a4529830f88cb5eb1900bd62776f5bd9843 Mon Sep 17 00:00:00 2001 From: yun Date: Fri, 2 Feb 2024 00:07:16 +0800 Subject: [PATCH] enable `drop` dimention when subseting --- DESCRIPTION | 1 - NAMESPACE | 10 +++- R/Class-BPCellsSeed.R | 55 +++++++++++------ R/Class-BindMatrix.R | 12 +++- R/Class-Convert.R | 21 ++++++- R/Class-subset.R | 113 +++++++++++++++-------------------- R/Matrix-Methods.R | 50 ++++++++++------ R/Seed-Methods.R | 102 +++++++++++++++++++------------ R/utils.R | 44 ++++---------- man/BPCellsMatrix-methods.Rd | 28 ++++++--- man/BPCellsSeed-methods.Rd | 44 +++++++------- man/BPCellsSeed.Rd | 14 ++++- man/convert_mode.Rd | 6 ++ man/internal-methods.Rd | 17 ++---- tests/testthat/setup.R | 46 +++++++++++--- 15 files changed, 340 insertions(+), 223 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 60f5d18..0c5bb22 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -20,7 +20,6 @@ Imports: cli, rlang (>= 1.1.0), S4Vectors, - SparseArray, BiocSingular Remotes: bnprks/BPCells diff --git a/NAMESPACE b/NAMESPACE index 6dd25ea..d8770ff 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,5 +1,9 @@ # Generated by roxygen2: do not edit by hand +S3method(base::as.array,BPCellsMatrix) +S3method(base::as.array,BPCellsSeed) +S3method(base::as.matrix,BPCellsMatrix) +S3method(base::as.matrix,BPCellsSeed) export(BPCellsArray) export(BPCellsMatrix) export(BPCellsSeed) @@ -55,6 +59,8 @@ exportMethods(DelayedArray) exportMethods(OLD_extract_sparse_array) exportMethods(acbind) exportMethods(arbind) +exportMethods(as.array) +exportMethods(as.matrix) exportMethods(binarize) exportMethods(bindCOLS) exportMethods(bindROWS) @@ -68,10 +74,10 @@ exportMethods(colSums) exportMethods(colVars) exportMethods(convert_mode) exportMethods(crossprod) +exportMethods(drop) exportMethods(expm1) exportMethods(expm1_slow) exportMethods(extract_array) -exportMethods(extract_sparse_array) exportMethods(is_sparse) exportMethods(log1p) exportMethods(log1p_slow) @@ -138,7 +144,6 @@ importFrom(MatrixGenerics,rowSds) importFrom(MatrixGenerics,rowVars) importFrom(S4Vectors,bindCOLS) importFrom(S4Vectors,bindROWS) -importFrom(SparseArray,extract_sparse_array) importFrom(methods,cbind2) importFrom(methods,rbind2) importFrom(methods,show) @@ -155,5 +160,6 @@ importMethodsFrom(BPCells,t) importMethodsFrom(DelayedArray,OLD_extract_sparse_array) importMethodsFrom(DelayedArray,dim) importMethodsFrom(DelayedArray,dimnames) +importMethodsFrom(DelayedArray,drop) importMethodsFrom(DelayedArray,extract_array) importMethodsFrom(DelayedArray,is_sparse) diff --git a/R/Class-BPCellsSeed.R b/R/Class-BPCellsSeed.R index 85e7d11..1582347 100644 --- a/R/Class-BPCellsSeed.R +++ b/R/Class-BPCellsSeed.R @@ -4,8 +4,9 @@ #' BPCells package. The purpose for `BPCellsSeed` class is to provide the common #' methods for all low-level BPCells seed classes. #' -#' @param x A `BPCellsSeed` object or a `IterableMatrix` object from BPCells, -#' details see the method signature. +#' @param x A `IterableMatrix` object from BPCells, a matrix-like object which +#' can be coerced into dgCMatrix, or a `BPCellsSeed` object. Details see the +#' method signature. #' @name BPCellsSeed NULL @@ -20,6 +21,14 @@ NULL methods::setClass("BPCellsSeed", contains = c(get_class("IterableMatrix"), "VIRTUAL") ) + +methods::setValidity("BPCellsSeed", function(object) { + if (length(dim(object)) != 2L) { + cli::cli_abort("{.pkg BPCells} can only support 2-dim matrix") + } + TRUE +}) + methods::setClass("BPCellsUnaryOpsSeed", contains = c("BPCellsSeed", "VIRTUAL")) methods::setClass("BPCellsNaryOpsSeed", contains = c("BPCellsSeed", "VIRTUAL")) methods::setClass("BPCellsBasicSeed", contains = c("BPCellsSeed", "VIRTUAL")) @@ -30,12 +39,38 @@ methods::setGeneric("BPCellsSeed", function(x) { standardGeneric("BPCellsSeed") }) +#' @export +#' @rdname BPCellsSeed +methods::setMethod("BPCellsSeed", "ANY", function(x) { + x <- coerce_dgCMatrix(x) + methods::callGeneric() +}) + #' @export #' @rdname BPCellsSeed methods::setMethod("BPCellsSeed", "BPCellsSeed", function(x) { x }) +#' @export +#' @rdname BPCellsSeed +methods::setMethod("BPCellsSeed", "matrix", function(x) { + mode <- storage_mode(x) + x <- methods::as(x, "dgCMatrix") + seed <- methods::callGeneric() + convert_mode(seed, mode) +}) + +#' @export +#' @rdname BPCellsSeed +methods::setMethod("BPCellsSeed", "dgCMatrix", function(x) { + methods::new( + "BPCellsdgCMatrixSeed", + dim = dim(x), dimnames = dimnames(x), + transpose = FALSE, mat = x + ) +}) + ############################################################# # used to extract the actual entity of `BPCellsSeed` objet. methods::setGeneric("entity", function(x, ...) standardGeneric("entity")) @@ -75,19 +110,3 @@ methods::setMethod( BPCellsdgCMatrixSeed(x = x) } ) - -#' @export -methods::setAs("dgCMatrix", "BPCellsdgCMatrixSeed", function(from) { - methods::new( - "BPCellsdgCMatrixSeed", - dim = dim(from), dimnames = dimnames(from), - transpose = FALSE, mat = from - ) -}) - -#' @export -methods::setAs("ANY", "BPCellsdgCMatrixSeed", function(from) { - methods::as(coerce_dgCMatrix(from), "BPCellsdgCMatrixSeed") -}) - -############################################################ diff --git a/R/Class-BindMatrix.R b/R/Class-BindMatrix.R index 72390a9..895da1a 100644 --- a/R/Class-BindMatrix.R +++ b/R/Class-BindMatrix.R @@ -31,7 +31,7 @@ methods::setClass("BPCellsRowBindMatrixSeed", methods::setMethod("summary", "BPCellsColBindMatrixSeed", function(object) { sprintf( "Concatenate %s of %d matrix objects (threads=%d)", - storage_axis(object), + if (object@transpose) "rows" else "cols", length(object@matrix_list), object@threads ) @@ -515,7 +515,15 @@ combine_seeds <- function(.fn, mode, seeds, ...) { where = "BPCells" ) mode <- mode %||% compatible_storage_mode(seeds) - seeds <- lapply(seeds, convert_mode_inform, mode = mode, arg = NULL) + mode <- match.arg(mode, BPCells_MODE) + seeds <- lapply(seeds, function(seed, mode) { + if (storage_mode(seed) != mode) { + cli::cli_inform("Converting into {mode} data type") + BPCellsSeed(BPCells::convert_matrix_type(seed, type = mode)) + } else { + seed + } + }, mode = mode) out <- Reduce(function(x, y) fn(x = x, y = y, ...), seeds) BPCellsSeed(out) } diff --git a/R/Class-Convert.R b/R/Class-Convert.R index c874355..9eba04b 100644 --- a/R/Class-Convert.R +++ b/R/Class-Convert.R @@ -99,5 +99,24 @@ methods::setMethod("storage_mode", "BPCellsSeed", function(object) { #' @export #' @rdname convert_mode methods::setMethod("storage_mode", "BPCellsMatrix", function(object) { - BPCells:::matrix_type(object@seed) + object <- object@seed + methods::callGeneric() +}) + +#' @export +#' @rdname convert_mode +methods::setMethod("storage_mode", "dgCMatrix", function(object) { + "double" +}) + +#' @export +#' @rdname convert_mode +methods::setMethod("storage_mode", "matrix", function(object) { + mode <- storage.mode(object) + switch(mode, + integer = "uint32_t", + double = , + numeric = "double", + cli::cli_abort("{.pkg BPCells} cannot support {.field {mode}} mode") + ) }) diff --git a/R/Class-subset.R b/R/Class-subset.R index 26a9c1b..089bd2a 100644 --- a/R/Class-subset.R +++ b/R/Class-subset.R @@ -27,14 +27,16 @@ methods::setMethod("summary", "BPCellsSubsetSeed", function(object) { ################ BPCellsMatrix Methods ################## #' @inheritParams BPCellsSeed-methods #' @return -#' - `[`: A [BPCellsMatrix] object. +#' - `[`: A [BPCellsMatrix] object or an atomic vector. #' @order 2 #' @export #' @rdname BPCellsMatrix-methods methods::setMethod( "[", c("BPCellsMatrix", "ANY", "ANY"), function(x, i, j, ..., drop = FALSE) { - DelayedArray(x@seed[i, j, ..., drop = drop]) + assert_bool(drop) + array <- DelayedArray(x@seed[i, j, ..., drop = FALSE]) + if (drop) drop(array) else array } ) @@ -44,7 +46,9 @@ methods::setMethod( methods::setMethod( "[", c("BPCellsMatrix", "missing", "ANY"), function(x, i, j, ..., drop = FALSE) { - DelayedArray(x@seed[, j, ..., drop = drop]) + assert_bool(drop) + array <- DelayedArray(x@seed[, j, ..., drop = FALSE]) + if (drop) drop(array) else array } ) @@ -53,7 +57,9 @@ methods::setMethod( methods::setMethod( "[", c("BPCellsMatrix", "ANY", "missing"), function(x, i, j, ..., drop = FALSE) { - DelayedArray(x@seed[i, , ..., drop = drop]) + assert_bool(drop) + array <- DelayedArray(x@seed[i, , ..., drop = FALSE]) + if (drop) drop(array) else array } ) @@ -62,7 +68,8 @@ methods::setMethod( methods::setMethod( "[", c("BPCellsMatrix", "missing", "missing"), function(x, i, j, ..., drop = FALSE) { - return(x) + assert_bool(drop) + if (drop) drop(x) else x } ) @@ -74,7 +81,7 @@ methods::setMethod( #' @rdname BPCellsMatrix-methods methods::setMethod( "[<-", c("BPCellsMatrix", "ANY", "ANY", "ANY"), - function(x, i, j, ..., mode = NULL, value) { + function(x, i, j, ..., value) { x <- x@seed DelayedArray(methods::callGeneric()) } @@ -85,11 +92,14 @@ methods::setMethod( # IterableMatrix method but some classes of BPCells do have their own `[` # method, so we re-dispatch method for every seed class. BPCellsSubset_internal <- function(x, i, j, ..., drop = FALSE) { - BPCellsSeed(methods::callNextMethod()) + assert_bool(drop) + seed <- BPCellsSeed(methods::callNextMethod()) + if (drop) drop(seed) else seed } #' @param i,j Row and Column index. -#' @param drop Ignored, always be `FALSE`. +#' @param drop A bool, if `TRUE`, any extents of length one will be removed and +#' return an atomic vector. #' @importMethodsFrom BPCells [ #' @export #' @rdname BPCellsSeed-methods @@ -171,7 +181,7 @@ methods::setMethod("[", "BPCellsTransformedSeed", BPCellsSubset_internal) #' @rdname BPCellsSeed-methods methods::setMethod( "[<-", c("BPCellsSeed", "ANY", "ANY", "ANY"), - function(x, i, j, ..., mode = NULL, value) { + function(x, i, j, ..., value) { value <- coerce_dgCMatrix(value) methods::callGeneric() } @@ -180,78 +190,53 @@ methods::setMethod( #' @export #' @rdname internal-methods methods::setMethod( - "[<-", c("BPCellsSeed", "ANY", "ANY", "dgCMatrix"), - function(x, i, j, ..., mode = NULL, value) { + "[<-", c("BPCellsSeed", "ANY", "ANY", "matrix"), + function(x, i, j, ..., value) { + x_mode <- storage_mode(x) + value_mode <- storage_mode(value) + value <- methods::as(value, "dgCMatrix") if (x@transpose) { - value <- t(methods::as(t(value), "BPCellsdgCMatrixSeed")) + value <- t(methods::as(t(value), "BPCellsSeed")) } else { - value <- methods::as(value, "BPCellsdgCMatrixSeed") + value <- methods::as(value, "BPCellsSeed") } - methods::callGeneric() - } -) - -#' @export -#' @rdname internal-methods -methods::setMethod( - "[<-", c("BPCellsSeed", "ANY", "ANY", "BPCellsSeed"), - function(x, i, j, ..., mode = NULL, value) { - i <- BPCells:::selection_index(i, nrow(x), rownames(x)) - ni <- if (length(i) > 0) seq_len(nrow(x))[-i] else seq_len(nrow(x)) - x_i <- x[i, ] - x_ni <- x[ni, ] - # dispatch the "BPCellsSeed", "missing", "ANY", "BPCellsSeed" method - x_i[, j, ..., mode = mode] <- value - rbind2(x_i, x_ni, mode = mode)[order(c(i, ni)), ] - } -) - -#' @export -#' @rdname internal-methods -methods::setMethod( - "[<-", c("BPCellsSeed", "ANY", "missing", "BPCellsSeed"), - function(x, i, j, ..., mode = NULL, value) { - i <- BPCells:::selection_index(i, nrow(x), rownames(x)) - ni <- if (length(i) > 0) seq_len(nrow(x))[-i] else seq_len(nrow(x)) - x_i <- x[i, ] - x_ni <- x[ni, ] - if (any(dim(x_i) != dim(value))) { - cli::cli_abort("Mismatched dimensions in assignment to subset") + if (x_mode == "uint32_t" && value_mode != "uint32_t") { + cli::cli_warn("Convert {.arg value} into {.field uint32_t} mode") + value <- convert_mode(value, "uint32_t") + } else if (x_mode != "uint32_t" && value_mode == "uint32_t") { + cli::cli_warn("Convert {.arg value} into {.field {x_mode}} mode") + value <- convert_mode(value, x_mode) + } else { + value <- convert_mode(value, x_mode) } - rownames(value) <- rownames(x_i) - colnames(value) <- colnames(x_i) - rbind2(value, x_ni, mode = mode)[order(c(i, ni)), ] + methods::callGeneric() } ) #' @export #' @rdname internal-methods methods::setMethod( - "[<-", c("BPCellsSeed", "missing", "ANY", "BPCellsSeed"), - function(x, i, j, ..., mode = NULL, value) { - j <- BPCells:::selection_index(j, ncol(x), colnames(x)) - nj <- if (length(j) > 0) seq_len(ncol(x))[-j] else seq_len(ncol(x)) - x_j <- x[, j] - x_nj <- x[, nj] - if (any(dim(x_j) != dim(value))) { - cli::cli_abort("Mismatched dimensions in assignment to subset") + "[<-", c("BPCellsSeed", "ANY", "ANY", "dgCMatrix"), + function(x, i, j, ..., value) { + if (x@transpose) { + value <- t(methods::as(t(value), "BPCellsSeed")) + } else { + value <- methods::as(value, "BPCellsSeed") } - rownames(value) <- rownames(x_j) - colnames(value) <- colnames(x_j) - cbind2(value, x_nj, mode = mode)[, order(c(j, nj))] + methods::callGeneric() } ) #' @export #' @rdname internal-methods methods::setMethod( - "[<-", c("BPCellsSeed", "missing", "missing", "BPCellsSeed"), + "[<-", c("BPCellsSeed", "ANY", "ANY", "BPCellsSeed"), function(x, i, j, ..., value) { - if (any(dim(x) != dim(value))) { - cli::cli_abort("Mismatched dimensions in assignment to subset") - } - rownames(value) <- rownames(x) - colnames(value) <- colnames(x) - value + fn <- methods::getMethod("[<-", "IterableMatrix", "BPCells") + fn( + x = x, + i = rlang::maybe_missing(i), j = rlang::maybe_missing(j), ..., + value = value + ) } ) diff --git a/R/Matrix-Methods.R b/R/Matrix-Methods.R index d71133b..79e00aa 100644 --- a/R/Matrix-Methods.R +++ b/R/Matrix-Methods.R @@ -1,7 +1,7 @@ #' Basic operations for `BPCellsMatrix` object #' #' @param x,object A `BPCellsMatrix` object. -#' @inheritParams BPCellsSeed-methods +#' @inheritParams BPCellsMatrix-methods #' @inherit BPCellsSeed-methods seealso #' @name BPCellsMatrix-methods NULL @@ -14,6 +14,38 @@ methods::setMethod("show", "BPCellsMatrix", function(object) { show_bpcells(object@seed, "DelayedMatrix", class(object)) }) +#' @importMethodsFrom DelayedArray drop +#' @export +#' @rdname BPCellsMatrix-methods +methods::setMethod("drop", "BPCellsMatrix", drop_internal) + +#' @export +methods::setAs("BPCellsMatrix", "dgCMatrix", function(from) { + methods::as(from@seed, "dgCMatrix") +}) + +# S3/S4 combo for as.array.BPCellsMatrix +#' @exportS3Method base::as.array +#' @rdname BPCellsMatrix-methods +as.array.BPCellsMatrix <- function(x, drop = FALSE) { + as.array(x@seed, drop = drop) +} + +#' @export +#' @rdname BPCellsMatrix-methods +methods::setMethod("as.array", "BPCellsMatrix", as.array.BPCellsMatrix) + +#' @exportS3Method base::as.matrix +#' @rdname BPCellsMatrix-methods +as.matrix.BPCellsMatrix <- function(x) { + as.matrix(x@seed) +} + +#' @export +#' @rdname BPCellsMatrix-methods +methods::setMethod("as.matrix", "BPCellsMatrix", as.matrix.BPCellsMatrix) + +########################################################## #' @return #' - `t`: A [BPCellsMatrix] object. #' @importMethodsFrom BPCells t @@ -36,19 +68,3 @@ methods::setMethod("t", "BPCellsMatrix", function(x) { #' @importMethodsFrom DelayedArray OLD_extract_sparse_array #' @noRd NULL - -#' @export -methods::setAs("BPCellsMatrix", "dgCMatrix", function(from) { - methods::as(from@seed, "dgCMatrix") -}) - -#' @export -methods::setAs("BPCellsMatrix", "matrix", function(from) { - methods::as(from@seed, "matrix") -}) - -#' @export -methods::setAs("ANY", "BPCellsArray", .as_BPCellsDirArray) - -#' @export -methods::setAs("ANY", "BPCellsMatrix", .as_BPCellsDirArray) diff --git a/R/Seed-Methods.R b/R/Seed-Methods.R index 82d9d9b..ddaab15 100644 --- a/R/Seed-Methods.R +++ b/R/Seed-Methods.R @@ -1,7 +1,8 @@ #' House of BPCellsSeed methods #' -#' Following methods are used by [BPCellsSeed-class] objects, you should always -#' use the methods of [BPCellsMatrix-class] +#' Following methods are used by [BPCellsSeed][BPCellsSeed-class] object, you +#' shouldn't use this directly, just use the methods of +#' [BPCellsMatrix][BPCellsMatrix-methods] #' #' @param x,object A [BPCellsSeed][BPCellsSeed-class] object. #' @param value @@ -51,6 +52,59 @@ methods::setMethod("type", "BPCellsSeed", function(x) { #' @rdname BPCellsSeed-methods methods::setMethod("is_sparse", "BPCellsSeed", function(x) TRUE) + +############################################### +#' @export +methods::setAs( + "BPCellsSeed", "dgCMatrix", + function(from) methods::callNextMethod() +) + +#' @importMethodsFrom DelayedArray drop +#' @export +#' @rdname BPCellsSeed-methods +methods::setMethod("drop", "BPCellsSeed", drop_internal) + +# S3/S4 combo for as.array.BPCellsSeed +#' @exportS3Method base::as.array +#' @rdname BPCellsSeed-methods +as.array.BPCellsSeed <- function(x, drop = FALSE) { + assert_bool(drop) + ans <- as.matrix(x) + if (drop) ans <- drop(ans) + ans +} + +#' @export +#' @rdname BPCellsSeed-methods +methods::setMethod("as.array", "BPCellsSeed", as.array.BPCellsSeed) + +#' @exportS3Method base::as.matrix +#' @rdname BPCellsSeed-methods +as.matrix.BPCellsSeed <- function(x) { + ans <- as.matrix(methods::as(x, "dgCMatrix")) # always be numeric mode + if (type(x) == "integer") { + if (all(ans < .Machine$integer.max)) { + storage.mode(ans) <- "integer" + } else { + cli::cli_warn( + "Using `double` mode since some values exceed {.code .Machine$integer.max}" + ) + } + } + ans +} + +#' @export +#' @rdname BPCellsSeed-methods +methods::setMethod("as.matrix", "BPCellsSeed", as.matrix.BPCellsSeed) + +#' @export +methods::setAs("ANY", "BPCellsSeed", function(from) { + BPCellsSeed(from) +}) + +############################################### #' @inheritParams S4Arrays::extract_array #' @return #' - `extract_array`: A dense matrix. @@ -60,12 +114,16 @@ methods::setMethod("is_sparse", "BPCellsSeed", function(x) TRUE) methods::setMethod( "extract_array", "BPCellsSeed", function(x, index) { - out <- as.matrix(extract_bpcells_array(x, index)) - storage.mode(out) <- type(x) - out + slice <- S4Arrays:::subset_by_Nindex(x, index) + as.matrix(slice) } ) +extract_dgCMatrix <- function(x, index) { + slice <- S4Arrays:::subset_by_Nindex(x, index) + methods::as(slice, "dgCMatrix") +} + #' @return #' - `OLD_extract_sparse_array`: A #' [SparseArraySeed][DelayedArray::SparseArraySeed-class] object. @@ -75,26 +133,12 @@ methods::setMethod( methods::setMethod( "OLD_extract_sparse_array", "BPCellsSeed", function(x, index) { - methods::as(extract_bpcells_array(x, index), "SparseArraySeed") + methods::as(extract_dgCMatrix(x, index), "SparseArraySeed") } ) #' @return -#' - `extract_sparse_array`: A -#' [SparseArray][SparseArray::SVT_SparseArray-class] object. -#' @importFrom SparseArray extract_sparse_array -#' @export -#' @rdname BPCellsSeed-methods -methods::setMethod( - "extract_sparse_array", "BPCellsSeed", - function(x, index) { - methods::as(extract_bpcells_array(x, index), "SparseArray") - } -) - -#' @return -#' - `chunkdim`: `NULL` or the chunk dimensions in an integer vector parallel to -#' `dim(x)`. +#' - `chunkdim`: the chunk dimensions in an integer vector parallel to `dim(x)`. #' @importFrom DelayedArray chunkdim #' @export #' @rdname BPCellsSeed-methods @@ -110,19 +154,3 @@ methods::setMethod( #' @export #' @rdname BPCellsSeed-methods methods::setMethod("t", "BPCellsSeed", function(x) methods::callNextMethod()) - -#' @export -methods::setAs("BPCellsSeed", "matrix", function(from) { - out <- methods::as(from, "dgCMatrix") - out <- as.matrix(out) - if (type(from) == "integer") { - if (all(out < .Machine$integer.max)) { - storage.mode(out) <- "integer" - } else { - cli::cli_warn( - "Using double data type since some values exceed {.code .Machine$integer.max}" - ) - } - } - out -}) diff --git a/R/utils.R b/R/utils.R index 83a44ef..df3de5d 100644 --- a/R/utils.R +++ b/R/utils.R @@ -18,27 +18,6 @@ BPCells_get <- local({ } }) -extract_bpcells_array <- function(x, index) { - if (length(index) > 2L) { - cli::cli_abort(c( - "{.arg index} must be a list with length <= 2", - i = "BPCells only support matrix operations" - )) - } - i <- index[[1L]] - j <- index[[2L]] - if (is.null(i) && is.null(j)) { - out <- x - } else if (!is.null(i) && !is.null(j)) { - out <- x[i, j] - } else if (!is.null(i)) { - out <- x[i, ] - } else { - out <- x[, j] - } - methods::as(out, "dgCMatrix") -} - coerce_dgCMatrix <- function(x, arg = rlang::caller_arg(x), call = rlang::caller_env()) { tryCatch( methods::as(x, "dgCMatrix"), @@ -110,16 +89,6 @@ compatible_storage_mode <- function(list) { BPCells_MODE[max(match(actual_modes, BPCells_MODE))] } -convert_mode_inform <- function(seed, mode, arg = rlang::caller_arg(seed)) { - mode <- match.arg(mode, BPCells_MODE) - if (storage_mode(seed) != mode) { - cli::cli_inform("Converting {.arg {arg}} into {mode} data type") - BPCells::convert_matrix_type(seed, type = mode) - } else { - seed - } -} - swap_axis <- function(.fn, object, column, row, ...) { if (object@transpose) { .fn(object, row, ...) @@ -137,6 +106,19 @@ wrapMatrix <- function(class, m, ...) { ) } +drop_internal <- function(x) { + perm <- which(dim(x) != 1L) + if (length(perm) >= 2L) { + return(x) + } + ## In-memory realization. + ## We want to propagate the names so we use 'as.array(x, drop=TRUE)' + ## rather than 'as.vector(x)' (both are equivalent on an Array + ## derivative with less than 2 effective dimensions except that + ## the former propagates the names and the latter doesn't). + as.array(x, drop = TRUE) +} + # Use chartr() for safety since toupper() fails to convert i to I in Turkish # locale lower_ascii <- "abcdefghijklmnopqrstuvwxyz" diff --git a/man/BPCellsMatrix-methods.Rd b/man/BPCellsMatrix-methods.Rd index 4c2b30e..23ccd59 100644 --- a/man/BPCellsMatrix-methods.Rd +++ b/man/BPCellsMatrix-methods.Rd @@ -12,6 +12,11 @@ \alias{colnames<-,BPCellsMatrix,atomic-method} \alias{colnames<-} \alias{BPCellsMatrix-methods} +\alias{drop,BPCellsMatrix-method} +\alias{as.array.BPCellsMatrix} +\alias{as.array,BPCellsMatrix-method} +\alias{as.matrix.BPCellsMatrix} +\alias{as.matrix,BPCellsMatrix-method} \alias{t,BPCellsMatrix-method} \alias{t} \alias{expm1_slow} @@ -40,7 +45,7 @@ \S4method{[}{BPCellsMatrix,ANY,ANY,ANY}(x, i, j, ..., drop = FALSE) -\S4method{[}{BPCellsMatrix,ANY,ANY,ANY}(x, i, j, ..., mode = NULL) <- value +\S4method{[}{BPCellsMatrix,ANY,ANY,ANY}(x, i, j, ...) <- value \S4method{dimnames}{BPCellsMatrix,ListOrNULL}(x) <- value @@ -48,6 +53,16 @@ \S4method{colnames}{BPCellsMatrix,atomic}(x) <- value +\S4method{drop}{BPCellsMatrix}(x) + +\method{as.array}{BPCellsMatrix}(x, drop = FALSE) + +\S4method{as.array}{BPCellsMatrix}(x, drop = FALSE) + +\method{as.matrix}{BPCellsMatrix}(x) + +\S4method{as.matrix}{BPCellsMatrix}(x) + \S4method{t}{BPCellsMatrix}(x) expm1_slow(x) @@ -89,13 +104,8 @@ pow_slow(e1, e2) \item{...}{Not used currently.} -\item{drop}{Ignored, always be \code{FALSE}.} - -\item{mode}{Storage mode of BPCells matrix, one of \code{uint32_t} (unsigned -32-bit integer), \code{float} (32-bit real number), or \code{double} (64-bit real -number). R cannot differentiate 32-bit and 64-bit real number, so -\link[=BPCellsSeed-methods]{type} method always return "double" for both \code{float} -and \code{double} mode.} +\item{drop}{A bool, if \code{TRUE}, any extents of length one will be removed and +return an atomic vector.} \item{value}{\itemize{ \item \verb{dimnames<-}: A list of dimnames or \code{NULL}. @@ -114,7 +124,7 @@ number.} } \value{ \itemize{ -\item \code{[}: A \link{BPCellsMatrix} object. +\item \code{[}: A \link{BPCellsMatrix} object or an atomic vector. } \itemize{ diff --git a/man/BPCellsSeed-methods.Rd b/man/BPCellsSeed-methods.Rd index dfb457f..f4c0286 100644 --- a/man/BPCellsSeed-methods.Rd +++ b/man/BPCellsSeed-methods.Rd @@ -11,9 +11,13 @@ \alias{BPCellsSeed-methods} \alias{type,BPCellsSeed-method} \alias{is_sparse,BPCellsSeed-method} +\alias{drop,BPCellsSeed-method} +\alias{as.array.BPCellsSeed} +\alias{as.array,BPCellsSeed-method} +\alias{as.matrix.BPCellsSeed} +\alias{as.matrix,BPCellsSeed-method} \alias{extract_array,BPCellsSeed-method} \alias{OLD_extract_sparse_array,BPCellsSeed-method} -\alias{extract_sparse_array,BPCellsSeed-method} \alias{chunkdim,BPCellsSeed-method} \alias{t,BPCellsSeed-method} \alias{expm1_slow,BPCellsSeed-method} @@ -43,7 +47,7 @@ \usage{ \S4method{show}{BPCellsSeed}(object) -\S4method{[}{BPCellsSeed,ANY,ANY,ANY}(x, i, j, ..., mode = NULL) <- value +\S4method{[}{BPCellsSeed,ANY,ANY,ANY}(x, i, j, ...) <- value \S4method{dimnames}{BPCellsSeed,list}(x) <- value @@ -57,12 +61,20 @@ \S4method{is_sparse}{BPCellsSeed}(x) +\S4method{drop}{BPCellsSeed}(x) + +\method{as.array}{BPCellsSeed}(x, drop = FALSE) + +\S4method{as.array}{BPCellsSeed}(x, drop = FALSE) + +\method{as.matrix}{BPCellsSeed}(x) + +\S4method{as.matrix}{BPCellsSeed}(x) + \S4method{extract_array}{BPCellsSeed}(x, index) \S4method{OLD_extract_sparse_array}{BPCellsSeed}(x, index) -\S4method{extract_sparse_array}{BPCellsSeed}(x, index) - \S4method{chunkdim}{BPCellsSeed}(x) \S4method{t}{BPCellsSeed}(x) @@ -120,12 +132,6 @@ \item{...}{Not used currently.} -\item{mode}{Storage mode of BPCells matrix, one of \code{uint32_t} (unsigned -32-bit integer), \code{float} (32-bit real number), or \code{double} (64-bit real -number). R cannot differentiate 32-bit and 64-bit real number, so -\link[=BPCellsSeed-methods]{type} method always return "double" for both \code{float} -and \code{double} mode.} - \item{value}{\itemize{ \item \verb{dimnames<-}: A list of dimnames or \code{NULL}. \item \verb{[<-}: A matrix which can be coerced into @@ -133,6 +139,9 @@ and \code{double} mode.} \item \code{pmin_scalar}: Single positive numeric value. }} +\item{drop}{A bool, if \code{TRUE}, any extents of length one will be removed and +return an atomic vector.} + \item{index}{ An unnamed list of integer vectors, one per dimension in \code{x}. Each vector is called a \emph{subscript} and can only contain @@ -154,8 +163,6 @@ and \code{double} mode.} number.} \item{digits}{Integer indicating the number of decimal places} - -\item{drop}{Ignored, always be \code{FALSE}.} } \value{ \itemize{ @@ -189,13 +196,7 @@ differentiate 32-bit and 64-bit real number. See } \itemize{ -\item \code{extract_sparse_array}: A -\link[SparseArray:SVT_SparseArray-class]{SparseArray} object. -} - -\itemize{ -\item \code{chunkdim}: \code{NULL} or the chunk dimensions in an integer vector parallel to -\code{dim(x)}. +\item \code{chunkdim}: the chunk dimensions in an integer vector parallel to \code{dim(x)}. } \itemize{ @@ -203,8 +204,9 @@ differentiate 32-bit and 64-bit real number. See } } \description{ -Following methods are used by \linkS4class{BPCellsSeed} objects, you should always -use the methods of \linkS4class{BPCellsMatrix} +Following methods are used by \link[=BPCellsSeed-class]{BPCellsSeed} object, you +shouldn't use this directly, just use the methods of +\link[=BPCellsMatrix-methods]{BPCellsMatrix} } \seealso{ \itemize{ diff --git a/man/BPCellsSeed.Rd b/man/BPCellsSeed.Rd index a607a4a..d3e15c2 100644 --- a/man/BPCellsSeed.Rd +++ b/man/BPCellsSeed.Rd @@ -7,7 +7,10 @@ \name{BPCellsSeed} \alias{BPCellsSeed} \alias{BPCellsSeed-class} +\alias{BPCellsSeed,ANY-method} \alias{BPCellsSeed,BPCellsSeed-method} +\alias{BPCellsSeed,matrix-method} +\alias{BPCellsSeed,dgCMatrix-method} \alias{BPCellsSeed,Iterable_dgCMatrix_wrapper-method} \alias{BPCellsSeed,ColBindMatrices-method} \alias{BPCellsSeed,RowBindMatrices-method} @@ -28,8 +31,14 @@ \usage{ BPCellsSeed(x) +\S4method{BPCellsSeed}{ANY}(x) + \S4method{BPCellsSeed}{BPCellsSeed}(x) +\S4method{BPCellsSeed}{matrix}(x) + +\S4method{BPCellsSeed}{dgCMatrix}(x) + \S4method{BPCellsSeed}{Iterable_dgCMatrix_wrapper}(x) \S4method{BPCellsSeed}{ColBindMatrices}(x) @@ -59,8 +68,9 @@ BPCellsSeed(x) \S4method{BPCellsSeed}{MatrixSubset}(x) } \arguments{ -\item{x}{A \code{BPCellsSeed} object or a \code{IterableMatrix} object from BPCells, -details see the method signature.} +\item{x}{A \code{IterableMatrix} object from BPCells, a matrix-like object which +can be coerced into dgCMatrix, or a \code{BPCellsSeed} object. Details see the +method signature.} } \description{ The \code{BPCellsSeed} class just inherits from the \code{IterableMatrix} class in diff --git a/man/convert_mode.Rd b/man/convert_mode.Rd index 1ca9342..221d358 100644 --- a/man/convert_mode.Rd +++ b/man/convert_mode.Rd @@ -7,6 +7,8 @@ \alias{storage_mode} \alias{storage_mode,BPCellsSeed-method} \alias{storage_mode,BPCellsMatrix-method} +\alias{storage_mode,dgCMatrix-method} +\alias{storage_mode,matrix-method} \title{Convert the storage type of a BPCellsArray object} \usage{ convert_mode(object, ...) @@ -20,6 +22,10 @@ storage_mode(object) \S4method{storage_mode}{BPCellsSeed}(object) \S4method{storage_mode}{BPCellsMatrix}(object) + +\S4method{storage_mode}{dgCMatrix}(object) + +\S4method{storage_mode}{matrix}(object) } \arguments{ \item{object}{A \link[=BPCellsSeed-class]{BPCellsSeed} or diff --git a/man/internal-methods.Rd b/man/internal-methods.Rd index d8a6597..1caf2dc 100644 --- a/man/internal-methods.Rd +++ b/man/internal-methods.Rd @@ -23,11 +23,9 @@ \alias{[,BPCellsMatrix,missing,ANY,ANY-method} \alias{[,BPCellsMatrix,ANY,missing,ANY-method} \alias{[,BPCellsMatrix,missing,missing,ANY-method} +\alias{[<-,BPCellsSeed,ANY,ANY,matrix-method} \alias{[<-,BPCellsSeed,ANY,ANY,dgCMatrix-method} \alias{[<-,BPCellsSeed,ANY,ANY,BPCellsSeed-method} -\alias{[<-,BPCellsSeed,ANY,missing,BPCellsSeed-method} -\alias{[<-,BPCellsSeed,missing,ANY,BPCellsSeed-method} -\alias{[<-,BPCellsSeed,missing,missing,BPCellsSeed-method} \alias{<,BPCellsMatrix,numeric-method} \alias{>,numeric,BPCellsMatrix-method} \alias{<=,BPCellsMatrix,numeric-method} @@ -81,15 +79,11 @@ \S4method{[}{BPCellsMatrix,missing,missing,ANY}(x, i, j, ..., drop = FALSE) -\S4method{[}{BPCellsSeed,ANY,ANY,dgCMatrix}(x, i, j, ..., mode = NULL) <- value +\S4method{[}{BPCellsSeed,ANY,ANY,matrix}(x, i, j, ...) <- value -\S4method{[}{BPCellsSeed,ANY,ANY,BPCellsSeed}(x, i, j, ..., mode = NULL) <- value +\S4method{[}{BPCellsSeed,ANY,ANY,dgCMatrix}(x, i, j, ...) <- value -\S4method{[}{BPCellsSeed,ANY,missing,BPCellsSeed}(x, i, j, ..., mode = NULL) <- value - -\S4method{[}{BPCellsSeed,missing,ANY,BPCellsSeed}(x, i, j, ..., mode = NULL) <- value - -\S4method{[}{BPCellsSeed,missing,missing,BPCellsSeed}(x, i, j, ...) <- value +\S4method{[}{BPCellsSeed,ANY,ANY,BPCellsSeed}(x, i, j, ...) <- value \S4method{<}{BPCellsMatrix,numeric}(e1, e2) @@ -145,7 +139,8 @@ number.} \item{i, j}{Row and Column index.} -\item{drop}{Ignored, always be \code{FALSE}.} +\item{drop}{A bool, if \code{TRUE}, any extents of length one will be removed and +return an atomic vector.} \item{value}{A matrix which can be coerced into \link[Matrix:dgCMatrix-class]{dgCMatrix}.} diff --git a/tests/testthat/setup.R b/tests/testthat/setup.R index 9a0a8de..07f7e7a 100644 --- a/tests/testthat/setup.R +++ b/tests/testthat/setup.R @@ -5,7 +5,7 @@ tmpdir <- normalizePath(tmpdir, mustWork = TRUE) common_test <- function( obj, actual_path, ..., mat = NULL, - seed_fn, name, + seed_fn, name, skip_multiplication = FALSE) { # for TransformedMatrix, it often contain float values transformed <- methods::is(seed_fn(obj), "BPCellsTransformedSeed") @@ -86,41 +86,73 @@ common_test <- function( ) pseudo_mat <- matrix(sample(mat, length(mat)), nrow = nrow(mat)) - cli::cli_inform("{.field [<-} {seed_name} works as expected") + storage.mode(pseudo_mat) <- "double" + cli::cli_inform("{.field [<-} for double {seed_name} works as expected") testthat::test_that( - sprintf("`[<-()` %s works as expected", seed_name), + sprintf("`[<-()` for double %s works as expected", seed_name), { seed <- seed_fn(obj) + seed <- convert_mode(seed, "double") + # double mode of seed + testthat::expect_identical(storage_mode(seed), "double") seed[1:10, ] <- pseudo_mat[1:10, ] mat[1:10, ] <- pseudo_mat[1:10, ] testthat::expect_s4_class(seed, "BPCellsSeed") + testthat::expect_identical(storage_mode(seed), "double") testthat::expect_equal(as.matrix(seed), mat) seed[, 1:10] <- pseudo_mat[, 1:10] mat[, 1:10] <- pseudo_mat[, 1:10] testthat::expect_s4_class(seed, "BPCellsSeed") + testthat::expect_identical(storage_mode(seed), "double") testthat::expect_equal(as.matrix(seed), mat) seed[1:10, 1:10] <- pseudo_mat[1:10, 1:10] mat[1:10, 1:10] <- pseudo_mat[1:10, 1:10] testthat::expect_s4_class(seed, "BPCellsSeed") - testthat::expect_equal(as.matrix(seed), mat) - testthat::expect_identical(storage_mode(seed), "double") + testthat::expect_equal(as.matrix(seed), mat) + } + ) - seed[1:10, mode = "uint32_t"] <- pseudo_mat[1:10, ] - mat[1:10, ] <- pseudo_mat[1:10, ] + cli::cli_inform("{.field [<-} for integer {seed_name} works as expected") + testthat::test_that( + sprintf("`[<-()` for integer %s works as expected", seed_name), + { + seed <- seed_fn(obj) + seed <- convert_mode(seed, "uint32_t") storage.mode(mat) <- "integer" + # '[<-()' will convert pseudo_mat into integer automatically + # Here: we convert it into integer mode manually for test equality + pseudo_mat2 <- pseudo_mat + storage.mode(pseudo_mat2) <- "integer" + testthat::expect_identical(storage_mode(seed), "uint32_t") + testthat::expect_warning(seed[1:10, ] <- pseudo_mat[1:10, ]) + mat[1:10, ] <- pseudo_mat2[1:10, ] + testthat::expect_s4_class(seed, "BPCellsSeed") + testthat::expect_identical(storage_mode(seed), "uint32_t") + testthat::expect_equal(as.matrix(seed), mat) + + testthat::expect_warning(seed[, 1:10] <- pseudo_mat[, 1:10]) + mat[, 1:10] <- pseudo_mat2[, 1:10] + testthat::expect_s4_class(seed, "BPCellsSeed") + testthat::expect_identical(storage_mode(seed), "uint32_t") + testthat::expect_equal(as.matrix(seed), mat) + + testthat::expect_warning(seed[1:10, 1:10] <- pseudo_mat[1:10, 1:10]) + mat[1:10, 1:10] <- pseudo_mat2[1:10, 1:10] testthat::expect_s4_class(seed, "BPCellsSeed") testthat::expect_identical(storage_mode(seed), "uint32_t") testthat::expect_equal(as.matrix(seed), mat) } ) + cli::cli_inform("{.field [<-} {matrix_name} works as expected") testthat::test_that( sprintf("`[<-()` %s works as expected", matrix_name), { obj <- BPCellsArray(obj) + obj <- convert_mode(obj, "double") obj[1:10, ] <- pseudo_mat[1:10, ] mat[1:10, ] <- pseudo_mat[1:10, ] testthat::expect_s4_class(obj, "BPCellsMatrix")