#' Apply a principal component analysis (PCA) to layers of a GRaster
#'
#' @description This function applies a principal component analysis to layers of a `GRaster`.
#'
#' @param x A `GRaster` with two or more layers.
#'
#' @param scale Logical: If `TRUE` (default), input layers will be rescaled by dividing each layer by its overall population standard deviation. Rasters will always be centered (have their mean subtracted from values). Centering and scaling is recommended when rasters values are in different units.
#'
#' @param scores Logical: If `TRUE`, the `prcomp` object will have the scores attached to it. This can greatly increase the size of the object in memory if the input raster has many cells. It will also take more time. If `FALSE` (default), then skip returning scores.
#'
#' @returns A multi-layer `GRaster` with one layer per principal component axis. The [pcs()] function can be used on the output raster to retrieve a `prcomp` object from the raster, which includes rotations (loadings) and proportions of variance explained.
#'
#' @seealso [terra::princomp()], [terra::prcomp()]
#'
#' @example man/examples/ex_princomp.r
#'
#' @aliases princomp
#' @rdname princomp
#' @exportMethod princomp
methods::setMethod(
f = "princomp",
signature = c(x = "GRaster"),
function(x, scale = TRUE, scores = FALSE) {
if (nlyr(x) < 2L) stop("Input raster must have 2 or more layers.")
.locationRestore(x)
.region(x)
input <- paste(sources(x), collapse = ",")
src <- .makeSourceName("i_pca", "raster")
args <- list(
cmd = "i.pca",
input = input,
output = src,
rescale = c(0, 0),
flags = c(.quiet(), "overwrite"),
intern = TRUE
)
if (scale) args$flags <- c(args$flags, "n")
# execute PCA and get rasters
info <- do.call(rgrass::execGRASS, args = args)
layers <- 1L:nlyr(x)
srcs <- paste0(src, ".", layers)
out <- .makeGRaster(srcs, paste0("PC", layers))
### get PCA information
#######################
n <- nlyr(x)
# standard deviations
sdev <- rep(NA_real_, n)
# rotations
rotation <- matrix(NA_real_, nrow = n, ncol = n, dimnames = list(names(x), names(out)))
for (pc in layers) {
this <- info[pc]
# standard deviation
start <- regexpr(this, pattern = " ")[1L] + 1L
stop <- regexpr(this, pattern = "\\(")[1L] - 1L
var <- substr(this, start, stop)
var <- as.numeric(var)
sd <- sqrt(var)
sdev[pc] <- sd
# rotations
start <- regexpr(this, pattern = "\\(")[1L] + 1L
end <- regexpr(this, pattern = ")")[1L] - 1L
rots <- substr(this, start, end)
rots <- strsplit(rots, ",")[[1L]]
rots <- as.numeric(rots)
rotation[ , pc] <- rots
}
# center
center <- global(x, "mean")
center <- c(center)[[1L]]
names(center) <- names(x)
# scale
if (!scale) {
scale <- FALSE
} else {
scale <- global(x, "sd")
scale <- c(scale)[[1L]]
names(scale) <- names(x)
}
pca <- list(
sdev = sdev,
rotation = rotation,
center = center,
scale = scale
)
# return scores
if (scores) {
sc <- as.points(out, values = TRUE)
sc <- as.data.table(sc)
sc <- as.matrix(sc)
pca$scores <- sc
}
class(pca) <- "prcomp"
attr(out, "pca") <- pca
out
} # EOF
)
#' Retrieve a principal components model from a PCA GRaster
#'
#' @param x A `GRaster` created by [princomp()]
#'
#' @returns An object of class `prcomp`.
#'
#' @seealso [princomp()], [terra::princomp()], module `i.pca` in **GRASS**
#'
#' @example man/examples/ex_princomp.r
#'
#' @aliases pcs
#' @rdname pcs
#' @export
pcs <- function(x) attr(x, "pca")
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.