Nothing
#' MultiBlock
#'
#' Creates a MultiBlock object from a named list of data blocks.
#' @param Samples A vector of sample names shared across all blocks (optional).
#' When omitted, sample names are taken from the row names of each block.
#' If no row names exist and all blocks have the same number of rows,
#' samples are numbered as integers. Use \code{ignore.names} or
#' \code{ignore.size} for more flexible behaviour.
#' @param Data A named list of matrices or data.frames (one entry per block).
#' @param Variables A named list of variable-name vectors, one per block
#' (optional). When omitted, column names are taken from each block; if
#' absent, variables are numbered as integers.
#' @param Batch A named list of batch vectors, one per block (optional).
#' @param Metadata A named list of metadata data.frames, one per block
#' (optional).
#' @param ignore.names If TRUE, sample names are not checked across blocks.
#' All blocks must have the same number of rows unless \code{ignore.size}
#' is also TRUE.
#' @param ignore.size If TRUE (only meaningful when \code{ignore.names = TRUE}),
#' blocks with different row counts are accepted. The resulting MultiBlock
#' stores a per-block Samples list and is not directly suitable for ComDim
#' (use \code{SplitRW()} first).
#' @return A MultiBlock object.
#' @examples
#' b1 <- matrix(rnorm(500), 10, 50) # 10 samples, 50 variables
#' b2 <- matrix(rnorm(800), 10, 80) # 10 samples, 80 variables
#' # Minimal call: Samples and Variables are filled in automatically.
#' mb <- MultiBlock(Data = list(b1 = b1, b2 = b2))
#'
#' # With explicit sample names (enables cross-block alignment):
#' rownames(b1) <- paste0("s", 1:10)
#' rownames(b2) <- paste0("s", 1:10)
#' mb <- MultiBlock(Data = list(b1 = b1, b2 = b2))
#'
#' # Blocks with different row counts (replicate design):
#' b3 <- matrix(rnorm(800), 30, 80)
#' batch_b3 <- c(rep(1, 10), rep(2, 10), rep(3, 10))
#' mb3 <- MultiBlock(
#' Data = list(b3 = b3), Batch = list(b3 = batch_b3),
#' ignore.names = TRUE, ignore.size = TRUE
#' )
#' @references Puig-Castellví F, Jouan-Rimbaud Bouveresse D, Mazéas L, Chapleur O, Rutledge DN (2021).
#' Rearrangement of incomplete multi-omics datasets combined with ComDim for evaluating replicate
#' cross-platform variability and batch influence.
#' \emph{Chemometrics and Intelligent Laboratory Systems}, 218, 104422.
#' \doi{10.1016/j.chemolab.2021.104422}
#' @export
MultiBlock <- function(Samples = NULL,
Data,
Variables = NULL,
Batch = NULL,
Metadata = NULL,
ignore.names = FALSE,
ignore.size = FALSE) {
if (is.null(Batch)) Batch <- list()
if (is.null(Metadata)) Metadata <- list()
# -------------------------------------------------------------------------
# Expand any MultiBlock objects passed inside Data
# -------------------------------------------------------------------------
if (any(vapply(Data, is, logical(1), "MultiBlock"))) {
expanded <- list()
for (nm in names(Data)) {
item <- Data[[nm]]
if (is(item, "MultiBlock")) {
for (bn in names(item@Data)) {
data_block <- item@Data[[bn]]
# Restore row names from the Samples slot so sample labels survive
# the dimname-strip in the main processing loop.
if (!is.list(item@Samples)) {
rownames(data_block) <- as.character(item@Samples)
} else if (!is.null(item@Samples[[bn]])) {
rownames(data_block) <- as.character(item@Samples[[bn]])
}
expanded[[bn]] <- data_block
}
for (bn in names(item@Batch)) {
if (is.null(Batch[[bn]])) Batch[[bn]] <- item@Batch[[bn]]
}
for (bn in names(item@Metadata)) {
if (is.null(Metadata[[bn]])) Metadata[[bn]] <- item@Metadata[[bn]]
}
} else {
expanded[[nm]] <- item
}
}
Data <- expanded
}
# -------------------------------------------------------------------------
# Validate Data input
# -------------------------------------------------------------------------
if (!is.list(Data) || length(Data) == 0) {
stop("'Data' must be a non-empty named list of matrices or data.frames.")
}
if (is.null(names(Data)) || any(names(Data) == "")) {
stop("All elements of 'Data' must be named.")
}
# -------------------------------------------------------------------------
# Process each block: convert to matrix, capture row/col names
# -------------------------------------------------------------------------
detected_samples <- vector("list", length(Data))
detected_vars <- vector("list", length(Data))
block_nrow <- integer(length(Data))
for (i in seq_along(Data)) {
block <- Data[[i]]
nm <- names(Data)[i]
if (is.data.frame(block)) {
if (any(sapply(block, is.character))) {
stop(sprintf("Block '%s' contains character columns.", nm))
}
if (!is.null(rownames(block))) detected_samples[[i]] <- rownames(block)
detected_vars[[i]] <- if (!is.null(colnames(block))) colnames(block) else seq_len(ncol(block))
Data[[i]] <- as.matrix(block)
dimnames(Data[[i]]) <- NULL
} else if (is.matrix(block)) {
if (!is.null(rownames(block))) detected_samples[[i]] <- rownames(block)
detected_vars[[i]] <- if (!is.null(colnames(block))) colnames(block) else seq_len(ncol(block))
dimnames(Data[[i]]) <- NULL
} else {
stop(sprintf("Block '%s' must be a matrix or a data.frame.", nm))
}
block_nrow[i] <- nrow(Data[[i]])
}
# -------------------------------------------------------------------------
# Build Variables list when not provided by the user
# -------------------------------------------------------------------------
if (is.null(Variables)) {
Variables <- setNames(detected_vars, names(Data))
}
# -------------------------------------------------------------------------
# Build Samples when not provided by the user
# -------------------------------------------------------------------------
if (!is.null(Samples)) {
# User supplied Samples explicitly — use as-is.
# The validator will catch any size mismatches.
} else if (ignore.names) {
# -- ignore.names = TRUE -------------------------------------------------
# Do not use names for cross-block alignment; only check/enforce row counts.
if (!ignore.size && length(unique(block_nrow)) != 1) {
stop(paste(
"Blocks do not have the same number of rows.",
"Provide sample names for all blocks to enable matching,",
"or set 'ignore.size = TRUE'."
))
}
if (length(unique(block_nrow)) == 1) {
# All blocks same size: assign integer indices
Samples <- seq_len(block_nrow[1])
} else {
# ignore.size = TRUE: store a per-block Samples list
Samples <- setNames(
lapply(seq_along(Data), function(i) {
if (!is.null(detected_samples[[i]])) {
detected_samples[[i]]
} else {
seq_len(block_nrow[i])
}
}),
names(Data)
)
warning(paste(
"The resulting MultiBlock is not directly suitable for ComDim because",
"sample numbers are not common across blocks.",
"If data contains replicates, use SplitRW().",
"Otherwise provide sample names and use ignore.names = FALSE."
))
}
} else {
# -- ignore.names = FALSE (default) --------------------------------------
# ignore.size is overridden to FALSE when ignore.names is FALSE
ignore.size <- FALSE
has_names <- !sapply(detected_samples, is.null)
if (!any(has_names)) {
# No block has row names: require equal sizes, assign integers
if (length(unique(block_nrow)) != 1) {
stop(paste(
"Blocks do not have the same number of rows.",
"Provide sample names for all blocks to enable matching,",
"or set 'ignore.names = TRUE'."
))
}
Samples <- seq_len(block_nrow[1])
} else if (any(has_names) && !all(has_names)) {
# Some blocks have row names and others do not: ambiguous
stop(paste(
"Some blocks have row names and others do not.",
"Either provide row names for all blocks, or none."
))
} else {
# All blocks have row names: align on common samples
common <- detected_samples[[1]]
for (i in seq_along(detected_samples)[-1]) {
common <- intersect(common, detected_samples[[i]])
}
if (length(common) == 0) {
stop("There are no common sample names across blocks.")
}
if (length(common) != max(block_nrow)) {
warning("Some samples are not common across blocks. They will be discarded.")
}
# Reorder / subset each block to the common sample set
for (i in seq_along(Data)) {
idx <- match(common, detected_samples[[i]])
Data[[i]] <- Data[[i]][idx, , drop = FALSE]
}
Samples <- common
}
}
# -------------------------------------------------------------------------
# Build and validate the MultiBlock object
# -------------------------------------------------------------------------
MB <- new("MultiBlock",
Samples = Samples,
Data = Data,
Variables = Variables,
Batch = Batch,
Metadata = Metadata
)
if (validObject(MB)) {
return(MB)
} else {
stop("The 'MultiBlock' could not be built.")
}
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.