#' The ResidualMatrixSeed class
#'
#' This is a seed class that powers the \pkg{DelayedArray} machinery underlying the \linkS4class{ResidualMatrix}.
#'
#' @section Construction:
#' \code{ResidualMatrixSeed(x, design=NULL, keep=NULL)} returns a ResidualMatrixSeed object, given:
#' \itemize{
#' \item \code{x}, a matrix-like object.
#' This can alternatively be a ResidualMatrixSeed, in which case \code{design} is ignored.
#' \item \code{design}, a numeric matrix containing the experimental design,
#' to be used for linear model fitting on each \emph{column} of \code{x}.
#' This defaults to an intercept-only matrix.
#' \item \code{keep}, an integer vector specifying the columns of \code{design} to \emph{not} regress out.
#' By default, all columns of \code{design} are regressed out.
#' \item \code{restrict}, an integer or logical vector specifying the rows of \code{x} to use for model fitting.
#' If \code{NULL}, all rows of \code{x} are used.
#' }
#'
#' @section Methods:
#' ResidualMatrixSeed objects are implemented as \linkS4class{DelayedMatrix} backends.
#' They support standard operations like \code{dim}, \code{dimnames} and \code{extract_array}.
#'
#' Passing a ResidualMatrixSeed object to the \code{\link{DelayedArray}} or \code{\link{ResidualMatrix}} constructors
#' will create a \linkS4class{ResidualMatrix} (which is what most users should be working with, anyway).
#'
#' @examples
#' design <- model.matrix(~gl(5, 50))
#'
#' library(Matrix)
#' y0 <- rsparsematrix(nrow(design), 200, 0.1)
#' s <- ResidualMatrixSeed(y0, design)
#' s
#'
#' ResidualMatrix(s)
#'
#' DelayedArray(s)
#' @author Aaron Lun
#'
#' @aliases
#' ResidualMatrixSeed
#' ResidualMatrixSeed-class
#' dim,ResidualMatrixSeed-method
#' dimnames,ResidualMatrixSeed-method
#' extract_array,ResidualMatrixSeed-method
#' DelayedArray,ResidualMatrixSeed-method
#' show,ResidualMatrixSeed-method
#' @docType class
#' @name ResidualMatrixSeed-class
NULL
#' @export
#' @importFrom Matrix crossprod
ResidualMatrixSeed <- function(x, design=NULL, keep=NULL, restrict=NULL) {
if (missing(x)) {
x <- matrix(0, 0, 0)
} else if (is(x, "ResidualMatrixSeed")) {
return(x)
}
if (is.null(design)) {
design <- matrix(1, nrow(x), 1)
} else if (nrow(design)!=nrow(x)) {
stop("'nrow(x)' and 'nrow(design)' should be equal")
}
QR <- qr(design)
if (QR$rank < ncol(design) && nrow(design)!=0) {
stop("'design' does not appear to be of full rank")
}
Q <- as.matrix(qr.Q(QR))
if (is.null(restrict) && is.null(keep)) {
Qty <- as.matrix(crossprod(Q, x))
} else if (!is.null(restrict)) {
subdesign <- design[restrict,,drop=FALSE]
subQR <- qr(subdesign)
if (subQR$rank < ncol(subdesign) && nrow(subdesign)!=0) {
stop("'design[restrict,]' does not appear to be of full rank")
}
subQ <- as.matrix(qr.Q(subQR))
subQty <- as.matrix(crossprod(subQ, x[restrict,,drop=FALSE]))
subR <- qr.R(subQR)
coefs <- backsolve(subR, subQty)
if (!is.null(keep)) {
# See the logic in the next clause.
coefs[subQR$pivot %in% keep,] <- 0
}
coefs <- coefs[match(QR$pivot, subQR$pivot),,drop=FALSE]
# We want to subtract the fitted values obtained by X %*% coefs,
# where coefs is computed using only the restricted subset of samples.
# This leads use to realize that X %*% coefs is just Q (R %*% coefs).
R <- qr.R(QR)
Qty <- R %*% coefs
} else {
# We want to subtract the fitted values X %*% coef', where kept coefs are set to zero in coef'.
# As X %*% coef' = QR %*% coef' = Q (R %*% coef'), we can use (R %*% coef') as our Qty.
R <- qr.R(QR)
Qty <- as.matrix(crossprod(Q, x))
coefs <- backsolve(R, Qty)
coefs[QR$pivot %in% keep,] <- 0
Qty <- R %*% coefs
}
new("ResidualMatrixSeed", .matrix=x, Q=Q, Qty=Qty, transposed=FALSE)
}
#' @importFrom S4Vectors setValidity2
setValidity2("ResidualMatrixSeed", function(object) {
msg <- character(0)
x <- get_matrix2(object)
Q <- get_Q(object)
if (nrow(x)!=nrow(Q)) {
msg <- c(msg, "'nrow(x)' and 'nrow(Q)' are not the same")
}
if (!is.numeric(Q)) {
msg <- c(msg, "'Q' should be a numeric matrix")
}
Qty <- get_Qty(object)
if (ncol(x)!=ncol(Qty)) {
msg <- c(msg, "'ncol(x)' and 'ncol(Qty)' are not the same")
}
if (ncol(Q)!=nrow(Qty)) {
msg <- c(msg, "'ncol(Q)' and 'nrow(Qty)' are not the same")
}
if (!is.numeric(Q)) {
msg <- c(msg, "'Qty' should be a numeric matrix")
}
if (length(is_transposed(object))!=1L) {
msg <- c(msg, "'transposed' must be a logical scalar")
}
if (length(msg)) {
return(msg)
}
TRUE
})
#' @export
setMethod("show", "ResidualMatrixSeed", function(object) {
cat(sprintf("%i x %i ResidualMatrixSeed object", nrow(object), ncol(object)),
sep="\n")
})
###################################
# Internal getters.
get_matrix2 <- function(x) x@.matrix
get_Q <- function(x) x@Q
get_Qty <- function(x) x@Qty
is_transposed <- function(x) x@transposed
###################################
# DelayedArray support utilities.
#' @export
setMethod("dim", "ResidualMatrixSeed", function(x) {
d <- dim(get_matrix2(x))
if (is_transposed(x)) { d <- rev(d) }
d
})
#' @export
setMethod("dimnames", "ResidualMatrixSeed", function(x) {
d <- dimnames(get_matrix2(x))
if (is_transposed(x)) { d <- rev(d) }
d
})
#' @export
#' @importFrom DelayedArray extract_array
#' @importFrom Matrix t crossprod
setMethod("extract_array", "ResidualMatrixSeed", function(x, index) {
if (was_transposed <- is_transposed(x)) {
x <- transpose_ResidualMatrixSeed(x)
index <- rev(index)
}
x2 <- subset_ResidualMatrixSeed(x, index[[1]], index[[2]])
resid <- get_matrix2(x2) - get_Q(x2) %*% get_Qty(x2)
if (was_transposed) {
resid <- t(resid)
}
as.matrix(resid)
})
###################################
# Additional utilities for efficiency.
subset_ResidualMatrixSeed <- function(x, i, j) {
mat <- get_matrix2(x)
Q <- get_Q(x)
Qty <- get_Qty(x)
if (!is.null(i)) {
if (is.character(i)) {
i <- match(i, rownames(mat))
}
mat <- mat[i,,drop=FALSE]
Q <- Q[i,,drop=FALSE]
}
if (!is.null(j)) {
if (is.character(j)) {
j <- match(j, colnames(mat))
}
mat <- mat[,j,drop=FALSE]
Qty <- Qty[,j,drop=FALSE]
}
initialize(x, .matrix=mat, Q=Q, Qty=Qty)
}
transpose_ResidualMatrixSeed <- function(x) {
initialize(x, transposed=!is_transposed(x))
}
rename_ResidualMatrixSeed <- function(x, value) {
x2 <- get_matrix2(x)
if (is_transposed(x)) {
value <- rev(value)
}
dimnames(x2) <- value
initialize(x, .matrix=x2)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.