#' Divide into batches
#'
#' Divide a single input object into multiple separate objects according to their batch of origin.
#'
#' @param x A matrix-like object where one dimension corresponds to cells and another represents features.
#' @param batch A factor specifying the batch to which each cell belongs.
#' @param byrow A logical scalar indicating whether rows correspond to cells.
#' @param restrict A subsetting vector specifying which cells should be used for correction.
#'
#' @details
#' This function is intended for internal use and other package developers.
#' It splits a single input object into multiple batches, allowing developers to use the same code for the scenario where \code{batch} is supplied with a single input.
#'
#' @return
#' A list containing:
#' \itemize{
#' \item \code{batches}, a named list of matrix-like objects where each element corresponds to a level of \code{batch} and contains all cells from that batch.
#' \item \code{reorder}, an integer vector to be applied to the combined \code{batches} to recover the ordering of cells in \code{x}.
#' \item \code{restricted}, a named list of integer vectors specifying which cells are to be used for correction.
#' Set to \code{NULL} if the input \code{restrict} was also \code{NULL}.
#' }
#'
#' @author Aaron Lun
#'
#' @examples
#' X <- matrix(rnorm(1000), ncol=100)
#' out <- divideIntoBatches(X, sample(3, 100, replace=TRUE))
#' names(out)
#'
#' # Recovering original order.
#' Y <- do.call(cbind, out$batches)
#' Z <- Y[,out$reorder]
#' all.equal(Z, X) # should be TRUE.
#'
#' @export
divideIntoBatches <- function(x, batch, byrow=FALSE, restrict=NULL) {
.check_valid_batch(x, batch, byrow=byrow)
batch <- as.factor(batch)
output <- vector("list", nlevels(batch))
names(output) <- levels(batch)
reorder <- integer(ncol(x))
last <- 0L
if (!is.null(restrict)) {
if (byrow) {
tmp <- .row_subset_to_index(x, restrict)
restrict <- logical(nrow(x))
} else {
tmp <- .col_subset_to_index(x, restrict)
restrict <- logical(ncol(x))
}
restrict[tmp] <- TRUE
restricted <- output
} else {
restricted <- NULL
}
for (b in levels(batch)) {
keep <- batch==b
if (byrow) {
current <- x[keep,,drop=FALSE]
N <- nrow(current)
} else {
current <- x[,keep,drop=FALSE]
N <- ncol(current)
}
if (!is.null(restrict)) {
cur.restrict <- which(restrict[keep])
if (length(cur.restrict)==0L) {
stop("no cells remaining in a batch after restriction")
}
restricted[[b]] <- cur.restrict
}
output[[b]] <- current
reorder[keep] <- last + seq_len(N)
last <- last + N
}
list(batches=output, reorder=reorder, restricted=restricted)
}
.check_valid_batch <- function(x, batch, byrow=FALSE) {
if (is.null(batch)) {
stop("'batch' must be specified if '...' has only one object")
}
if (byrow) {
if (length(batch)!=nrow(x)) {
stop("'length(batch)' should be equal to number of cells in '...'")
}
} else {
if (length(batch)!=ncol(x)) {
stop("'length(batch)' should be equal to number of cells in '...'")
}
}
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.