| 1 |
# PREDICT |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
# CA =========================================================================== |
|
| 6 |
#' @export |
|
| 7 |
#' @rdname predict |
|
| 8 |
#' @aliases predict,CA-method |
|
| 9 |
setMethod( |
|
| 10 |
f = "predict", |
|
| 11 |
signature = c(object = "CA"), |
|
| 12 |
definition = function(object, newdata, margin = 1) {
|
|
| 13 |
## Coerce to matrix |
|
| 14 | 4x |
if (missing(newdata)) {
|
| 15 | ! |
data <- object@data |
| 16 | ! |
data <- data[!object@rows@supplement, !object@columns@supplement, drop = FALSE] |
| 17 |
} else {
|
|
| 18 | 4x |
data <- as.matrix(newdata) |
| 19 |
} |
|
| 20 | ||
| 21 |
## TODO: keep only matching rows/columns |
|
| 22 | ||
| 23 |
## Get standard coordinates |
|
| 24 | 4x |
if (margin == 1) {
|
| 25 | 2x |
data <- data / rowSums(data) |
| 26 | 2x |
std <- object@columns@standard |
| 27 |
} |
|
| 28 | 4x |
if (margin == 2) {
|
| 29 | 2x |
data <- t(data) / colSums(data) |
| 30 | 2x |
std <- object@rows@standard |
| 31 |
} |
|
| 32 | ||
| 33 |
## Compute principal coordinates |
|
| 34 | 4x |
coords <- crossprod(t(data), std) |
| 35 | 4x |
coords <- as.data.frame(coords) |
| 36 | 4x |
colnames(coords) <- paste0("F", seq_along(coords))
|
| 37 | 4x |
return(coords) |
| 38 |
} |
|
| 39 |
) |
|
| 40 | ||
| 41 |
# MCA ========================================================================== |
|
| 42 |
#' @export |
|
| 43 |
#' @rdname predict |
|
| 44 |
#' @aliases predict,MCA-method |
|
| 45 |
setMethod( |
|
| 46 |
f = "predict", |
|
| 47 |
signature = c(object = "MCA"), |
|
| 48 |
definition = function(object, newdata, margin = 1) {
|
|
| 49 |
## Coerce to matrix |
|
| 50 | 2x |
if (missing(newdata)) {
|
| 51 | ! |
data <- object@data |
| 52 | ! |
data <- data[!object@rows@supplement, !object@columns@supplement, drop = FALSE] |
| 53 |
} else {
|
|
| 54 |
## Complete disjunctive table |
|
| 55 | 2x |
data <- cdt(newdata) |
| 56 |
} |
|
| 57 | ||
| 58 | 2x |
methods::callNextMethod(object = object, newdata = data, margin = margin) |
| 59 |
} |
|
| 60 |
) |
|
| 61 | ||
| 62 |
# PCA ========================================================================== |
|
| 63 |
#' @export |
|
| 64 |
#' @rdname predict |
|
| 65 |
#' @aliases predict,PCA-method |
|
| 66 |
setMethod( |
|
| 67 |
f = "predict", |
|
| 68 |
signature = c(object = "PCA"), |
|
| 69 |
definition = function(object, newdata, margin = 1) {
|
|
| 70 |
## Coerce to matrix |
|
| 71 | 2x |
if (missing(newdata)) {
|
| 72 | ! |
data <- object@data |
| 73 | ! |
data <- data[!object@rows@supplement, !object@columns@supplement] |
| 74 |
} else {
|
|
| 75 | 2x |
data <- as.matrix(newdata) |
| 76 |
} |
|
| 77 | ||
| 78 |
## Get standard coordinates |
|
| 79 | 2x |
var_mean <- object@center |
| 80 | 2x |
var_sd <- object@scale |
| 81 | ||
| 82 | 2x |
if (margin == 1) {
|
| 83 | 1x |
std <- object@columns@standard |
| 84 | 1x |
w <- object@columns@weights |
| 85 | ||
| 86 | 1x |
newdata <- (t(newdata) - var_mean) * w / var_sd |
| 87 |
} |
|
| 88 | 2x |
if (margin == 2) {
|
| 89 | 1x |
std <- object@rows@standard |
| 90 | 1x |
w <- object@rows@weights |
| 91 | 1x |
j <- ncol(newdata) |
| 92 | ||
| 93 | 1x |
X <- if (all(var_mean == 0)) rep(0, j) else weighted_mean(newdata, w) |
| 94 | 1x |
newdata <- t(t(newdata) - X) |
| 95 | 1x |
Y <- if (all(var_sd == 1)) rep(1, j) else weighted_sd(newdata, w) |
| 96 | 1x |
newdata <- t(t(newdata) / Y) |
| 97 | 1x |
newdata <- newdata * w |
| 98 |
} |
|
| 99 | ||
| 100 |
## Compute principal coordinates |
|
| 101 | 2x |
coords <- crossprod(newdata, std) |
| 102 | 2x |
coords <- as.data.frame(coords) |
| 103 | 2x |
colnames(coords) <- paste0("F", seq_along(coords))
|
| 104 | 2x |
return(coords) |
| 105 |
} |
|
| 106 |
) |
| 1 |
# CORRESPONDENCE ANALYSIS |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname ca |
|
| 7 |
#' @aliases ca,data.frame-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "ca", |
|
| 10 |
signature = c(object = "data.frame"), |
|
| 11 |
definition = function(object, rank = NULL, sup_row = NULL, sup_col = NULL, |
|
| 12 |
sup_quali = NULL, autodetect = FALSE) {
|
|
| 13 |
## Remove non-numeric variables, if any |
|
| 14 | 10x |
clean <- drop_variable(object, f = is.numeric, negate = TRUE, |
| 15 | 10x |
sup = sup_col, extra = sup_quali, |
| 16 | 10x |
auto = autodetect, what = "qualitative") |
| 17 |
## Compute PCA |
|
| 18 | 10x |
results <- methods::callGeneric( |
| 19 | 10x |
object = clean$data, rank = rank, |
| 20 | 10x |
sup_row = sup_row, sup_col = clean$sup |
| 21 |
) |
|
| 22 | ||
| 23 |
## Add supplementary quantitative variables |
|
| 24 | ! |
if (!is.null(clean$extra)) set_extra(results) <- clean$extra |
| 25 | ||
| 26 | 8x |
results |
| 27 |
} |
|
| 28 |
) |
|
| 29 | ||
| 30 |
#' @export |
|
| 31 |
#' @rdname ca |
|
| 32 |
#' @aliases ca,matrix-method |
|
| 33 |
setMethod( |
|
| 34 |
f = "ca", |
|
| 35 |
signature = c(object = "matrix"), |
|
| 36 |
definition = function(object, rank = NULL, sup_row = NULL, sup_col = NULL) {
|
|
| 37 |
## Fix dimension names |
|
| 38 | 14x |
names_row <- rownames(object) |
| 39 | 14x |
names_col <- colnames(object) |
| 40 | 3x |
if (is.null(names_row)) names_row <- as.character(seq_len(nrow(object))) |
| 41 | 2x |
if (is.null(names_col)) names_col <- as.character(seq_len(ncol(object))) |
| 42 | ||
| 43 |
## Subset |
|
| 44 | 14x |
is_row_sup <- find_variable(sup_row, nrow(object), names = rownames(object)) |
| 45 | 14x |
is_col_sup <- find_variable(sup_col, ncol(object), names = colnames(object)) |
| 46 | 14x |
N <- object[!is_row_sup, !is_col_sup, drop = FALSE] |
| 47 | ||
| 48 |
## Check missing values |
|
| 49 | 14x |
arkhe::assert_missing(N) |
| 50 | ||
| 51 |
## Check dimensions |
|
| 52 | 14x |
arkhe::assert_filled(N) |
| 53 | ||
| 54 |
## Dimension of the solution |
|
| 55 | 14x |
ndim <- min(rank, dim(N) - 1) |
| 56 | 14x |
i <- nrow(N) |
| 57 | 14x |
j <- ncol(N) |
| 58 | ||
| 59 |
## Grand total |
|
| 60 | 14x |
total <- sum(N, na.rm = FALSE) |
| 61 |
## Relative frequencies |
|
| 62 | 14x |
P <- N / total |
| 63 | ||
| 64 |
## Calcul des marges |
|
| 65 | 14x |
w_row <- rowSums(P, na.rm = FALSE) |
| 66 | 14x |
w_col <- colSums(P, na.rm = FALSE) |
| 67 | ||
| 68 |
## /!\ Important: we need to clean the data before processing |
|
| 69 |
## Empty rows/columns must be removed to avoid error in svd() |
|
| 70 | 14x |
if (any(w_row == 0)) |
| 71 | 1x |
stop(tr_("Empty rows detected."), call. = FALSE)
|
| 72 | 13x |
if (any(w_col == 0)) |
| 73 | 1x |
stop(tr_("Empty columns detected."), call. = FALSE)
|
| 74 | ||
| 75 |
## Build matrix |
|
| 76 |
## matrix * vector is faster (!) than: |
|
| 77 |
# matrix %*% t(vector) |
|
| 78 |
# t(t(matrix) * vector) |
|
| 79 | 12x |
s_row <- sqrt(w_row) |
| 80 | 12x |
s_col <- sqrt(w_col) |
| 81 | 12x |
W_row1 <- matrix(s_row, nrow = i, ncol = j, byrow = FALSE) |
| 82 | 12x |
W_col1 <- matrix(s_col, nrow = i, ncol = j, byrow = TRUE) |
| 83 | 12x |
W_row2 <- matrix(s_row, nrow = i, ncol = ndim, byrow = FALSE) |
| 84 | 12x |
W_col2 <- matrix(s_col, nrow = j, ncol = ndim, byrow = FALSE) |
| 85 | ||
| 86 |
## Calcul des écarts à l'indépendance |
|
| 87 | 12x |
M <- P - tcrossprod(w_row, w_col) |
| 88 | ||
| 89 |
## Matrix of standardized residuals |
|
| 90 | 12x |
S <- M / W_row1 / W_col1 |
| 91 | ||
| 92 |
## Singular Value Decomposition |
|
| 93 | 12x |
D <- svd2(S, ndim) |
| 94 | 12x |
sv <- D$d # Singular values |
| 95 | ||
| 96 |
## Standard coordinates |
|
| 97 | 12x |
U <- D$u / W_row2 |
| 98 | 12x |
V <- D$v / W_col2 |
| 99 | ||
| 100 | 12x |
sv_U <- matrix(sv, nrow = i, ncol = ndim, byrow = TRUE) |
| 101 | 12x |
sv_V <- matrix(sv, nrow = j, ncol = ndim, byrow = TRUE) |
| 102 | ||
| 103 |
## Principal coordinates |
|
| 104 | 12x |
coord_row <- U * sv_U |
| 105 | 12x |
coord_col <- V * sv_V |
| 106 | ||
| 107 |
## Contributions |
|
| 108 | 12x |
contrib_row <- ((coord_row * W_row2) / sv_U)^2 * 100 |
| 109 | 12x |
contrib_col <- ((coord_col * W_col2) / sv_V)^2 * 100 |
| 110 | ||
| 111 |
## Squared distance to centroide |
|
| 112 | 12x |
dist_row <- rowSums(S^2) / w_row |
| 113 | 12x |
dist_col <- colSums(S^2) / w_col |
| 114 | ||
| 115 |
## Supplementary points |
|
| 116 | 12x |
if (any(is_row_sup)) {
|
| 117 | 4x |
extra_row <- object[is_row_sup, !is_col_sup, drop = FALSE] |
| 118 | 4x |
row_sup <- t(extra_row / rowSums(extra_row)) |
| 119 | ||
| 120 |
## Coordinates |
|
| 121 | 4x |
coord_row_sup <- crossprod(row_sup, V) |
| 122 | 4x |
coord_row <- rbind(coord_row, coord_row_sup) |
| 123 | ||
| 124 |
## Distances |
|
| 125 | 4x |
dist_row_sup <- colSums((row_sup - w_col)^2 / w_col) |
| 126 | 4x |
dist_row <- c(dist_row, dist_row_sup) |
| 127 |
} |
|
| 128 | 12x |
if (any(is_col_sup)) {
|
| 129 | 3x |
extra_col <- object[!is_row_sup, is_col_sup, drop = FALSE] |
| 130 | 3x |
col_sup <- t(t(extra_col) / colSums(extra_col)) |
| 131 | ||
| 132 |
## Coordinates |
|
| 133 | 3x |
coord_col_sup <- crossprod(col_sup, U) |
| 134 | 3x |
coord_col <- rbind(coord_col, coord_col_sup) |
| 135 | ||
| 136 |
## Distances |
|
| 137 | 3x |
dist_col_sup <- colSums((col_sup - w_row)^2 / w_row) |
| 138 | 3x |
dist_col <- c(dist_col, dist_col_sup) |
| 139 |
} |
|
| 140 | ||
| 141 |
## Squared cosine |
|
| 142 | 12x |
cos_row <- coord_row^2 / dist_row |
| 143 | 12x |
cos_col <- coord_col^2 / dist_col |
| 144 | ||
| 145 | 12x |
.CA( |
| 146 | 12x |
data = object, |
| 147 | 12x |
dimension = as.integer(ndim), |
| 148 | 12x |
singular_values = sv, |
| 149 | 12x |
rows = build_results( |
| 150 | 12x |
names = names_row, |
| 151 | 12x |
principal = coord_row, |
| 152 | 12x |
standard = U, |
| 153 | 12x |
contributions = contrib_row, |
| 154 | 12x |
distances = dist_row, |
| 155 | 12x |
cosine = cos_row, |
| 156 | 12x |
weights = w_row, |
| 157 | 12x |
supplement = is_row_sup |
| 158 |
), |
|
| 159 | 12x |
columns = build_results( |
| 160 | 12x |
names = names_col, |
| 161 | 12x |
principal = coord_col, |
| 162 | 12x |
standard = V, |
| 163 | 12x |
contributions = contrib_col, |
| 164 | 12x |
distances = dist_col, |
| 165 | 12x |
cosine = cos_col, |
| 166 | 12x |
weights = w_col, |
| 167 | 12x |
supplement = is_col_sup |
| 168 |
) |
|
| 169 |
) |
|
| 170 |
} |
|
| 171 |
) |
| 1 |
# PLOT COORDINATES |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @method plot PCOA |
|
| 7 |
plot.PCOA <- function(x, ..., axes = c(1, 2), labels = FALSE, |
|
| 8 |
extra_quali = NULL, extra_quanti = NULL, |
|
| 9 |
ellipse = NULL, hull = FALSE, |
|
| 10 |
color = NULL, fill = FALSE, symbol = FALSE, size = c(1, 6), |
|
| 11 |
xlim = NULL, ylim = NULL, main = NULL, sub = NULL, |
|
| 12 |
ann = graphics::par("ann"), frame.plot = TRUE,
|
|
| 13 |
panel.first = NULL, panel.last = NULL, |
|
| 14 |
legend = list(x = "topleft")) {
|
|
| 15 |
## Set axes |
|
| 16 | 5x |
arkhe::assert_type(axes, "numeric") |
| 17 | 5x |
arkhe::assert_length(axes, 2) |
| 18 | 5x |
assign("axes", value = axes, envir = the)
|
| 19 | ||
| 20 |
## Prepare data |
|
| 21 | 5x |
coord <- get_coordinates(x) |
| 22 | 5x |
coord$x <- coord[[axes[[1L]]]] |
| 23 | 5x |
coord$y <- coord[[axes[[2L]]]] |
| 24 | 5x |
n <- NROW(coord) |
| 25 | ||
| 26 |
## Set graphical parameters |
|
| 27 |
## (recycle if of length one) |
|
| 28 | 5x |
dots <- list(...) |
| 29 | 5x |
col <- recycle(dots$col %||% graphics::par("col"), n)
|
| 30 | 5x |
bg <- recycle(dots$bg %||% graphics::par("bg"), n)
|
| 31 | 5x |
pch <- recycle(dots$pch %||% 16, n) |
| 32 | 5x |
cex <- recycle(dots$cex %||% graphics::par("cex"), n)
|
| 33 | ||
| 34 |
## Highlight quantitative information |
|
| 35 | 5x |
if (length(extra_quanti) > 0) {
|
| 36 | 1x |
arkhe::assert_type(extra_quanti, "numeric") |
| 37 | 1x |
arkhe::assert_length(extra_quanti, n) |
| 38 |
## Continuous scales |
|
| 39 |
## (ignored if col, bg and cex are set by user) |
|
| 40 | 1x |
if (is.null(dots$col) && !isFALSE(color)) |
| 41 | 1x |
col <- khroma::palette_color_continuous(colors = color)(extra_quanti) |
| 42 | 1x |
if (is.null(dots$bg) && !isFALSE(fill)) |
| 43 | ! |
bg <- khroma::palette_color_continuous(colors = fill)(extra_quanti) |
| 44 | 1x |
if (is.null(dots$cex) && !isFALSE(size)) |
| 45 | 1x |
cex <- khroma::palette_size_sequential(range = size)(extra_quanti) |
| 46 |
} |
|
| 47 |
## Highlight qualitative information |
|
| 48 | 5x |
if (length(extra_quali) > 0) {
|
| 49 | 4x |
arkhe::assert_length(extra_quali, n) |
| 50 |
## Discrete scales |
|
| 51 |
## (ignored if col, bg and pch are set by user) |
|
| 52 | 4x |
if (is.null(dots$col) && !isFALSE(color)) |
| 53 | 4x |
col <- khroma::palette_color_discrete(colors = color)(extra_quali) |
| 54 | 4x |
if (is.null(dots$bg) && !isFALSE(fill)) |
| 55 | ! |
bg <- khroma::palette_color_discrete(colors = fill)(extra_quali) |
| 56 | 4x |
if (is.null(dots$pch) && !isFALSE(symbol)) |
| 57 | ! |
pch <- khroma::palette_shape(symbols = symbol)(extra_quali) |
| 58 |
} |
|
| 59 | ||
| 60 |
## Save and restore graphical parameters |
|
| 61 |
## pty: square plotting region, independent of device size |
|
| 62 | 5x |
old_par <- graphics::par(pty = "s", no.readonly = TRUE) |
| 63 | 5x |
on.exit(graphics::par(old_par), add = TRUE) |
| 64 | ||
| 65 |
## Open new window |
|
| 66 | 5x |
grDevices::dev.hold() |
| 67 | 5x |
on.exit(grDevices::dev.flush(), add = TRUE) |
| 68 | 5x |
graphics::plot.new() |
| 69 | ||
| 70 |
## Set plotting coordinates |
|
| 71 | 5x |
xlim <- xlim %||% range(coord$x, na.rm = TRUE, finite = TRUE) |
| 72 | 5x |
ylim <- ylim %||% range(coord$y, na.rm = TRUE, finite = TRUE) |
| 73 | 5x |
graphics::plot.window(xlim = xlim, ylim = ylim, asp = 1) |
| 74 | ||
| 75 |
## Evaluate pre-plot expressions |
|
| 76 | 5x |
panel.first |
| 77 | ||
| 78 |
## Plot |
|
| 79 | 5x |
graphics::abline(h = 0, lty = "dashed", lwd = 1, col = graphics::par("fg"))
|
| 80 | 5x |
graphics::abline(v = 0, lty = "dashed", lwd = 1, col = graphics::par("fg"))
|
| 81 | 5x |
graphics::points(x = coord$x, y = coord$y, |
| 82 | 5x |
col = col, bg = bg, pch = pch, cex = cex) |
| 83 | ||
| 84 |
## Labels |
|
| 85 | 5x |
if (isTRUE(labels)) {
|
| 86 | ! |
label( |
| 87 | ! |
x = coord$x, |
| 88 | ! |
y = coord$y, |
| 89 | ! |
labels = rownames(coord), |
| 90 | ! |
type = "shadow", |
| 91 | ! |
col = col, |
| 92 | ! |
cex = cex, |
| 93 | ! |
xpd = TRUE |
| 94 |
) |
|
| 95 |
} |
|
| 96 | ||
| 97 | 5x |
if (length(extra_quali) > 0) {
|
| 98 |
## Add ellipse |
|
| 99 | 4x |
if (is.list(ellipse) && length(ellipse) > 0) {
|
| 100 | 2x |
args_ell <- list(x = x, group = extra_quali, axes = axes, |
| 101 | 2x |
color = color, fill = FALSE, symbol = FALSE) |
| 102 | 2x |
ellipse <- modifyList(args_ell, val = ellipse) |
| 103 | 2x |
do.call(viz_ellipses, ellipse) |
| 104 |
} |
|
| 105 |
## Add convex hull |
|
| 106 | 4x |
if (isTRUE(hull)) {
|
| 107 | 1x |
args_hull <- list(x = x, group = extra_quali, axes = axes, |
| 108 | 1x |
color = color, fill = FALSE, symbol = FALSE) |
| 109 | 1x |
do.call(viz_hull, args_hull) |
| 110 |
} |
|
| 111 |
} |
|
| 112 | ||
| 113 |
## Evaluate post-plot and pre-axis expressions |
|
| 114 | 5x |
panel.last |
| 115 | ||
| 116 |
## Construct axis (axes) |
|
| 117 | 5x |
if (TRUE) {
|
| 118 | 5x |
graphics::axis(side = 1, las = 1) |
| 119 | 5x |
graphics::axis(side = 2, las = 1) |
| 120 |
} |
|
| 121 | ||
| 122 |
## Plot frame |
|
| 123 | 5x |
if (frame.plot) {
|
| 124 | 5x |
graphics::box() |
| 125 |
} |
|
| 126 | ||
| 127 |
## Add annotation |
|
| 128 | 5x |
if (ann) {
|
| 129 | 5x |
graphics::title( |
| 130 | 5x |
main = main, sub = sub, |
| 131 | 5x |
xlab = colnames(coord)[axes[[1]]], |
| 132 | 5x |
ylab = colnames(coord)[axes[[2]]] |
| 133 |
) |
|
| 134 |
} |
|
| 135 | ||
| 136 |
## Legend |
|
| 137 | 5x |
coord <- data.frame( |
| 138 | 5x |
extra_quanti = if (length(extra_quanti) > 0) extra_quanti else rep(NA, n), |
| 139 | 5x |
extra_quali = if (length(extra_quali) > 0) extra_quali else rep(NA, n), |
| 140 | 5x |
cex = cex, col = col, bg = bg, pch = pch, lty = rep(NA, n) |
| 141 |
) |
|
| 142 | 5x |
viz_legend(coord, legend, points = TRUE, lines = FALSE) |
| 143 | ||
| 144 | 5x |
invisible(x) |
| 145 |
} |
|
| 146 | ||
| 147 |
#' @export |
|
| 148 |
#' @rdname plot |
|
| 149 |
#' @aliases plot,PCOA,missing-method |
|
| 150 |
setMethod("plot", c(x = "PCOA", y = "missing"), plot.PCOA)
|
| 1 |
# PRINCIPAL COMPONENTS ANALYSIS |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname pca |
|
| 7 |
#' @aliases pca,data.frame-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "pca", |
|
| 10 |
signature = c(object = "data.frame"), |
|
| 11 |
definition = function(object, center = TRUE, scale = TRUE, rank = NULL, |
|
| 12 |
sup_row = NULL, sup_col = NULL, sup_quali = NULL, |
|
| 13 |
weight_row = NULL, weight_col = NULL, autodetect = FALSE) {
|
|
| 14 |
## Remove non-numeric variables, if any |
|
| 15 | 10x |
clean <- drop_variable(object, f = is.numeric, negate = TRUE, |
| 16 | 10x |
sup = sup_col, extra = sup_quali, |
| 17 | 10x |
auto = autodetect, what = "qualitative") |
| 18 | ||
| 19 |
## Compute PCA |
|
| 20 | 10x |
results <- methods::callGeneric( |
| 21 | 10x |
object = clean$data, center = center, scale = scale, |
| 22 | 10x |
rank = rank, sup_row = sup_row, sup_col = clean$sup, |
| 23 | 10x |
weight_row = weight_row, weight_col = weight_col |
| 24 |
) |
|
| 25 | ||
| 26 |
## Add supplementary quantitative variables |
|
| 27 | 4x |
if (!is.null(clean$extra)) set_extra(results) <- clean$extra |
| 28 | ||
| 29 | 9x |
results |
| 30 |
} |
|
| 31 |
) |
|
| 32 | ||
| 33 |
#' @export |
|
| 34 |
#' @rdname pca |
|
| 35 |
#' @aliases pca,matrix-method |
|
| 36 |
setMethod( |
|
| 37 |
f = "pca", |
|
| 38 |
signature = c(object = "matrix"), |
|
| 39 |
definition = function(object, center = TRUE, scale = TRUE, rank = NULL, |
|
| 40 |
sup_row = NULL, sup_col = NULL, |
|
| 41 |
weight_row = NULL, weight_col = NULL) {
|
|
| 42 |
# Fix dimension names |
|
| 43 | 12x |
names_row <- rownames(object) |
| 44 | 12x |
names_col <- colnames(object) |
| 45 | 7x |
if (is.null(names_row)) names_row <- as.character(seq_len(nrow(object))) |
| 46 | 2x |
if (is.null(names_col)) names_col <- as.character(seq_len(ncol(object))) |
| 47 | ||
| 48 |
# Subset |
|
| 49 | 12x |
is_row_sup <- find_variable(sup_row, nrow(object), names = rownames(object)) |
| 50 | 12x |
is_col_sup <- find_variable(sup_col, ncol(object), names = colnames(object)) |
| 51 | 12x |
N <- object[!is_row_sup, !is_col_sup, drop = FALSE] |
| 52 | ||
| 53 |
## Check missing values |
|
| 54 | 12x |
arkhe::assert_missing(N) |
| 55 | ||
| 56 |
## Check dimensions |
|
| 57 | 11x |
arkhe::assert_filled(N) |
| 58 | ||
| 59 |
# Dimension of the solution |
|
| 60 | 11x |
ndim <- min(rank, dim(N) - 1) |
| 61 | 11x |
i <- nrow(N) |
| 62 | 11x |
j <- ncol(N) |
| 63 | ||
| 64 |
# Weights |
|
| 65 | 11x |
w_row <- if (is.null(weight_row)) rep(1, nrow(N)) else weight_row |
| 66 | 11x |
w_col <- if (is.null(weight_col)) rep(1, ncol(N)) else weight_col |
| 67 | 11x |
w_row <- w_row / sum(w_row) |
| 68 | ||
| 69 |
# Build matrix |
|
| 70 | 11x |
s_row <- sqrt(w_row) |
| 71 | 11x |
s_col <- sqrt(w_col) |
| 72 | 11x |
W_row1 <- matrix(s_row, nrow = i, ncol = j, byrow = FALSE) |
| 73 | 11x |
W_col1 <- matrix(s_col, nrow = i, ncol = j, byrow = TRUE) |
| 74 | 11x |
W_row2 <- matrix(s_row, nrow = i, ncol = ndim, byrow = FALSE) |
| 75 | 11x |
W_col2 <- matrix(s_col, nrow = j, ncol = ndim, byrow = FALSE) |
| 76 | ||
| 77 |
# Center data |
|
| 78 | 11x |
if (center) {
|
| 79 | 10x |
var_mean <- weighted_mean(N, w_row) |
| 80 |
} else {
|
|
| 81 | 1x |
var_mean <- rep(0, j) |
| 82 |
} |
|
| 83 | 11x |
ctr <- matrix(var_mean, nrow = i, ncol = j, byrow = TRUE) |
| 84 | 11x |
P <- N - ctr |
| 85 | ||
| 86 |
# Scale data |
|
| 87 | 11x |
if (scale) {
|
| 88 | 6x |
var_sd <- weighted_sd(P, w_row) |
| 89 |
} else {
|
|
| 90 | 5x |
var_sd <- rep(1, j) |
| 91 |
} |
|
| 92 | 11x |
std <- matrix(var_sd, nrow = i, ncol = j, byrow = TRUE) |
| 93 | 11x |
M <- P / std |
| 94 | ||
| 95 |
# Matrix of standardized residuals |
|
| 96 | 11x |
S <- M * W_col1 * W_row1 |
| 97 | ||
| 98 |
# Singular Value Decomposition |
|
| 99 | 11x |
D <- svd2(S, ndim) |
| 100 | 11x |
sv <- D$d # Singular values |
| 101 | ||
| 102 |
# Standard coordinates |
|
| 103 | 11x |
U <- D$u / W_row2 |
| 104 | 11x |
V <- D$v / W_col2 |
| 105 | ||
| 106 | 11x |
sv_U <- matrix(sv, nrow = i, ncol = ndim, byrow = TRUE) |
| 107 | 11x |
sv_V <- matrix(sv, nrow = j, ncol = ndim, byrow = TRUE) |
| 108 | ||
| 109 |
# Principal coordinates |
|
| 110 | 11x |
coord_row <- U * sv_U |
| 111 | 11x |
coord_col <- V * sv_V |
| 112 | ||
| 113 |
# Contributions |
|
| 114 | 11x |
contrib_row <- ((coord_row * W_row2) / sv_U)^2 * 100 |
| 115 | 11x |
contrib_col <- ((coord_col * W_col2) / sv_V)^2 * 100 |
| 116 | ||
| 117 |
# Squared distance to centroide |
|
| 118 | 11x |
dist_row <- rowSums((M * W_col1)^2) |
| 119 | 11x |
dist_col <- colSums((M * W_row1)^2) |
| 120 | ||
| 121 |
# Supplementary points |
|
| 122 | 11x |
if (any(is_row_sup)) {
|
| 123 | 5x |
extra_row <- object[is_row_sup, !is_col_sup, drop = FALSE] |
| 124 | 5x |
ind_sup <- (t(extra_row) - var_mean) * w_col / var_sd |
| 125 | ||
| 126 |
# Coordinates |
|
| 127 | 5x |
coord_row_sup <- crossprod(ind_sup, V) |
| 128 | 5x |
coord_row <- rbind(coord_row, coord_row_sup) |
| 129 | ||
| 130 |
# Distances |
|
| 131 | 5x |
dist_row_sup <- colSums(ind_sup^2 * w_col) |
| 132 | 5x |
dist_row <- c(dist_row, dist_row_sup) |
| 133 |
} |
|
| 134 | 11x |
if (any(is_col_sup)) {
|
| 135 | 4x |
extra_col <- object[!is_row_sup, is_col_sup, drop = FALSE] |
| 136 |
# Center and scale |
|
| 137 | 4x |
if (center) {
|
| 138 | 4x |
extra_col <- t(t(extra_col) - weighted_mean(extra_col, w_row)) |
| 139 |
} |
|
| 140 | 4x |
if (scale) {
|
| 141 | 3x |
extra_col <- t(t(extra_col) / weighted_sd(extra_col, w_row)) |
| 142 |
} |
|
| 143 | 4x |
var_sup <- extra_col * w_row |
| 144 | ||
| 145 |
# Coordinates |
|
| 146 | 4x |
coord_col_sup <- crossprod(var_sup, U) |
| 147 | 4x |
coord_col <- rbind(coord_col, coord_col_sup) |
| 148 | ||
| 149 |
# Distances |
|
| 150 | 4x |
dist_col_sup <- colSums(extra_col^2 * w_row) |
| 151 | 4x |
dist_col <- c(dist_col, dist_col_sup) |
| 152 |
} |
|
| 153 | ||
| 154 |
# Squared cosine |
|
| 155 | 11x |
cos_row <- coord_row^2 / dist_row |
| 156 | 11x |
cos_col <- coord_col^2 / dist_col |
| 157 | ||
| 158 |
# names(sv) <- paste0("F", dim_keep)
|
|
| 159 | 11x |
.PCA( |
| 160 | 11x |
data = object, |
| 161 | 11x |
dimension = as.integer(ndim), |
| 162 | 11x |
singular_values = sv, |
| 163 | 11x |
rows = build_results( |
| 164 | 11x |
names = names_row, |
| 165 | 11x |
principal = coord_row, |
| 166 | 11x |
standard = U, |
| 167 | 11x |
contributions = contrib_row, |
| 168 | 11x |
distances = sqrt(dist_row), |
| 169 | 11x |
cosine = cos_row, |
| 170 | 11x |
weights = w_row, |
| 171 | 11x |
supplement = is_row_sup |
| 172 |
), |
|
| 173 | 11x |
columns = build_results( |
| 174 | 11x |
names = names_col, |
| 175 | 11x |
principal = coord_col, |
| 176 | 11x |
standard = V, |
| 177 | 11x |
contributions = contrib_col, |
| 178 | 11x |
distances = sqrt(dist_col), |
| 179 | 11x |
cosine = cos_col, |
| 180 | 11x |
weights = w_col, |
| 181 | 11x |
supplement = is_col_sup |
| 182 |
), |
|
| 183 | 11x |
center = var_mean, |
| 184 | 11x |
scale = var_sd |
| 185 |
) |
|
| 186 |
} |
|
| 187 |
) |
| 1 |
# REPELLING LABELS |
|
| 2 | ||
| 3 |
# Text ========================================================================= |
|
| 4 |
#' Non-Overlapping Text Labels |
|
| 5 |
#' |
|
| 6 |
#' Optimize the location of text labels to minimize overplotting text. |
|
| 7 |
#' @param x,y A [`numeric`] vector giving the x and y coordinates of a set of |
|
| 8 |
#' points. If `y` is `NULL`, an attempt is made to interpret `x` in a suitable |
|
| 9 |
#' way (see [grDevices::xy.coords()]). |
|
| 10 |
#' @param labels A [`character`] vector or [`expression`] specifying the text |
|
| 11 |
#' to be written. |
|
| 12 |
#' @param type A [`character`] string specifying the shape of the field. |
|
| 13 |
#' It must be one of "`text`", "`shadow`" or "`box`". Any unambiguous substring |
|
| 14 |
#' can be given. |
|
| 15 |
#' @param ... Further arguments to be passed to [graphics::text()], |
|
| 16 |
#' particularly, character expansion, `cex` and color, `col`. |
|
| 17 |
#' @return |
|
| 18 |
#' `label()` is called it for its side-effects: it results in a graphic |
|
| 19 |
#' being displayed. |
|
| 20 |
#' @seealso [graphics::text()] |
|
| 21 |
#' @source |
|
| 22 |
#' This function is modeled after [car::pointLabel()] (originally from the |
|
| 23 |
#' \pkg{maptools} package).
|
|
| 24 |
#' @author N. Frerebeau |
|
| 25 |
#' @family annotations |
|
| 26 |
#' @keywords internal |
|
| 27 |
#' @export |
|
| 28 |
label <- function(x, y = NULL, labels = seq_along(x$x), |
|
| 29 |
type = c("text", "shadow", "box"), ...) {
|
|
| 30 |
## Validation |
|
| 31 | ! |
type <- match.arg(type, several.ok = FALSE) |
| 32 | ! |
x <- grDevices::xy.coords(x = x, y = y) |
| 33 | ||
| 34 | ! |
labels <- grDevices::as.graphicsAnnot(labels) |
| 35 | ! |
if (length(labels) < length(x$x)) labels <- rep(labels, length(x$x)) |
| 36 | ||
| 37 |
## Compute label positions |
|
| 38 | ! |
labs <- compute_labels(x = x$x, y = x$y, labels = labels) |
| 39 | ||
| 40 |
## Draw labels |
|
| 41 | ! |
fun <- switch( |
| 42 | ! |
type, |
| 43 | ! |
text = graphics::text, |
| 44 | ! |
shadow = text_shadow, |
| 45 | ! |
box = text_box |
| 46 |
) |
|
| 47 | ! |
fun(labs, labels = labels, ...) |
| 48 | ||
| 49 | ! |
invisible(labs) |
| 50 |
} |
|
| 51 | ||
| 52 |
# Adapted from car::pointLabel() |
|
| 53 |
compute_labels <- function(x, y, labels, ..., iter = 50, |
|
| 54 |
cex = graphics::par("cex"),
|
|
| 55 |
font = NULL, vfont = NULL) {
|
|
| 56 |
## Coordinates |
|
| 57 | ! |
bound <- graphics::par("usr")
|
| 58 | ! |
ratio <- graphics::par("pin")[1] / graphics::par("pin")[2] # x/y ratio
|
| 59 | ||
| 60 | ! |
to_unity <- function(x, y) {
|
| 61 | ! |
list(x = (x - bound[1]) / (bound[2] - bound[1]) * ratio, |
| 62 | ! |
y = (y - bound[3]) / (bound[4] - bound[3]) / ratio) |
| 63 |
} |
|
| 64 | ! |
to_usr <- function(x, y) {
|
| 65 | ! |
list(x = bound[1] + x / ratio * (bound[2] - bound[1]), |
| 66 | ! |
y = bound[3] + y * ratio * (bound[4] - bound[3])) |
| 67 |
} |
|
| 68 | ||
| 69 | ! |
xy <- to_unity(x = x, y = y) |
| 70 | ! |
x <- xy$x |
| 71 | ! |
y <- xy$y |
| 72 | ! |
n <- length(x) |
| 73 | ||
| 74 |
## 8 positions: corners and side mid-points of the rectangle |
|
| 75 |
## Position 7 (top right) is the most preferred |
|
| 76 | ! |
width <- graphics::strwidth(labels, units = "figure", cex = cex, |
| 77 | ! |
font = font, vfont = vfont) |
| 78 | ! |
height <- graphics::strheight(labels, units = "figure", cex = cex, |
| 79 | ! |
font = font, vfont = vfont) |
| 80 | ! |
width <- (width + 0.02) * ratio |
| 81 | ! |
height <- (height + 0.02) / ratio |
| 82 | ||
| 83 | ! |
makeoff <- function(pos) {
|
| 84 | ! |
c(-1, -1, -1, 0, 0, 1, 1, 1)[pos] * (width / 2) + |
| 85 | ! |
1i * c(-1, 0, 1, -1, 1, -1, 0, 1)[pos] * (height / 2) |
| 86 |
} |
|
| 87 | ||
| 88 |
## Find intersection area of two rectangles |
|
| 89 | ! |
overlap <- function(xy1, off1, xy2, off2) {
|
| 90 | ! |
w <- pmin(Re(xy1 + off1 / 2), Re(xy2 + off2 / 2)) - |
| 91 | ! |
pmax(Re(xy1 - off1 / 2), Re(xy2 - off2 / 2)) |
| 92 | ! |
h <- pmin(Im(xy1 + off1 / 2), Im(xy2 + off2 / 2)) - |
| 93 | ! |
pmax(Im(xy1 - off1 / 2), Im(xy2 - off2 / 2)) |
| 94 | ! |
w[w <= 0] <- 0 |
| 95 | ! |
h[h <= 0] <- 0 |
| 96 | ! |
w * h |
| 97 |
} |
|
| 98 | ||
| 99 | ! |
objective <- function(gene) {
|
| 100 | ! |
offset <- makeoff(gene) |
| 101 | ||
| 102 | ! |
if (!is.null(rectidx1)) {
|
| 103 | ! |
area <- sum(overlap(xy[rectidx1] + offset[rectidx1], rectv[rectidx1], |
| 104 | ! |
xy[rectidx2] + offset[rectidx2], rectv[rectidx2])) |
| 105 |
} else {
|
|
| 106 | ! |
area <- 0 |
| 107 |
} |
|
| 108 | ||
| 109 |
## Penalize labels which go outside the image area |
|
| 110 |
## Count points outside of the image |
|
| 111 | ! |
a <- Re(xy + offset - rectv / 2) < 0 | Re(xy + offset + rectv / 2) > ratio |
| 112 | ! |
b <- Im(xy + offset - rectv / 2) < 0 | Im(xy + offset + rectv / 2) > 1 / ratio |
| 113 | ! |
outside <- sum(a | b) |
| 114 | ! |
res <- 1000 * area + outside |
| 115 | ! |
res |
| 116 |
} |
|
| 117 | ||
| 118 |
# Make a list of label rectangles in their reference positions, |
|
| 119 |
# centered over the map feature; the real labels are displaced |
|
| 120 |
# from these positions so as not to overlap |
|
| 121 |
# Note that some labels can be bigger than others |
|
| 122 | ! |
xy <- x + 1i * y |
| 123 | ! |
rectv <- width + 1i * height |
| 124 | ||
| 125 | ! |
rectidx1 <- rectidx2 <- array(0, (length(x)^2 - length(x)) / 2) |
| 126 | ! |
k <- 0 |
| 127 | ! |
for (i in seq_along(x)) |
| 128 | ! |
for (j in seq_len(i - 1)) {
|
| 129 | ! |
k <- k + 1 |
| 130 | ! |
rectidx1[k] <- i |
| 131 | ! |
rectidx2[k] <- j |
| 132 |
} |
|
| 133 | ! |
maylap <- overlap(xy[rectidx1], 2 * rectv[rectidx1], |
| 134 | ! |
xy[rectidx2], 2 * rectv[rectidx2]) > 0 |
| 135 | ! |
rectidx1 <- rectidx1[maylap] |
| 136 | ! |
rectidx2 <- rectidx2[maylap] |
| 137 | ||
| 138 |
## Simulated annealing |
|
| 139 |
## Initial state |
|
| 140 | ! |
gene <- rep(8, n) |
| 141 | ! |
score <- objective(gene) |
| 142 |
## Initial "best" solution |
|
| 143 | ! |
bestgene <- gene |
| 144 | ! |
bestscore <- score |
| 145 | ! |
iter <- seq_len(iter) |
| 146 | ! |
temp <- 2.5 |
| 147 | ! |
for (i in iter) {
|
| 148 | ! |
k <- 1 # Energy evaluation count |
| 149 | ! |
for (j in iter) {
|
| 150 | ! |
newgene <- gene |
| 151 | ! |
newgene[sample(n, 1)] <- sample(8, 1) |
| 152 | ! |
newscore <- objective(newgene) |
| 153 | ! |
if (newscore <= score || stats::runif(1) < exp((score - newscore) / temp)) {
|
| 154 |
## keep the new set if it has the same or better score or |
|
| 155 |
## if it's worse randomly based on the annealing criteria |
|
| 156 | ! |
k <- k + 1 |
| 157 | ! |
score <- newscore |
| 158 | ! |
gene <- newgene |
| 159 |
} |
|
| 160 | ! |
if (score <= bestscore) {
|
| 161 | ! |
bestscore <- score |
| 162 | ! |
bestgene <- gene |
| 163 |
} |
|
| 164 | ! |
if (bestscore == 0 || k == 10) break |
| 165 |
} |
|
| 166 | ! |
if (bestscore == 0) break |
| 167 | ! |
temp <- 0.9 * temp |
| 168 |
} |
|
| 169 | ||
| 170 | ! |
nx <- Re(xy + makeoff(bestgene)) |
| 171 | ! |
ny <- Im(xy + makeoff(bestgene)) |
| 172 | ||
| 173 | ! |
xy <- to_usr(x = nx, y = ny) |
| 174 | ! |
xy$labels <- labels |
| 175 | ! |
xy |
| 176 |
} |
|
| 177 | ||
| 178 |
#' Shadow Text |
|
| 179 |
#' |
|
| 180 |
#' @param x,y A [`numeric`] vector. If `y` is `NULL`, an attempt is made to |
|
| 181 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 182 |
#' @param labels A [`character`] vector specifying the text to be written. |
|
| 183 |
#' @param width Thickness of the shadow, as a fraction of the plotting size. |
|
| 184 |
#' @param theta Angles for plotting the background. |
|
| 185 |
#' @param cex A [`numeric`] character expansion factor. |
|
| 186 |
#' @param col The color to be used for the text. |
|
| 187 |
#' @param bg The color to be used for the shadow. |
|
| 188 |
#' @param font,vfont The font to be used (see [graphics::text()]). |
|
| 189 |
#' @param ... Further parameters to be passed to [graphics::text()]. |
|
| 190 |
#' @return |
|
| 191 |
#' `text_shadow()` is called it for its side-effects: it results in a graphic |
|
| 192 |
#' being displayed. |
|
| 193 |
#' @author N. Frerebeau |
|
| 194 |
#' @family geometries |
|
| 195 |
#' @keywords internal |
|
| 196 |
#' @noRd |
|
| 197 |
text_shadow <- function(x, y = NULL, labels = seq_along(x$x), |
|
| 198 |
width = 1/10, theta = seq(0, 2 * pi, length.out = 50), |
|
| 199 |
cex = graphics::par("cex"), col = graphics::par("fg"),
|
|
| 200 |
bg = graphics::par("bg"), font = NULL, vfont = NULL, ...) {
|
|
| 201 | ||
| 202 | ! |
x <- grDevices::xy.coords(x = x, y = y) |
| 203 | ||
| 204 | ! |
xo <- width * graphics::strwidth("M", units = "user", cex = cex, font = font, vfont = vfont)
|
| 205 | ! |
yo <- width * graphics::strheight("X", units = "user", cex = cex, font = font, vfont = vfont)
|
| 206 | ||
| 207 | ! |
for (i in theta) {
|
| 208 | ! |
graphics::text(x = x$x + cos(i) * xo, y = x$y + sin(i) * yo, labels = labels, |
| 209 | ! |
col = bg, cex = cex, font = font, vfont = vfont, ...) |
| 210 |
} |
|
| 211 | ||
| 212 | ! |
graphics::text(x = x$x, y = x$y, labels = labels, col = col, cex = cex, |
| 213 | ! |
font = font, vfont = vfont, ...) |
| 214 | ||
| 215 | ! |
invisible(NULL) |
| 216 |
} |
|
| 217 | ||
| 218 |
#' Text with Halo Underneath |
|
| 219 |
#' |
|
| 220 |
#' @param x,y A [`numeric`] vector. If `y` is `NULL`, an attempt is made to |
|
| 221 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 222 |
#' @param labels A [`character`] vector specifying the text to be written. |
|
| 223 |
#' @param padding A length-one [`numeric`] vector giving the amount of padding |
|
| 224 |
#' around label. |
|
| 225 |
#' @param rounding A length-one [`numeric`] vector giving the rounding of the |
|
| 226 |
#' angles (see [rounded()]). |
|
| 227 |
#' @param vertices A length-on [`integer`] vector specifying the number of |
|
| 228 |
#' vertices to draw (see [rounded()]). |
|
| 229 |
#' @param cex A numeric character expansion factor. |
|
| 230 |
#' @param col The color to be used for the text. |
|
| 231 |
#' @param bg The color to be used for the background. |
|
| 232 |
#' @param font,vfont The font to be used (see [graphics::text()]). |
|
| 233 |
#' @param ... Further parameters to be passed to [graphics::text()] (see details). |
|
| 234 |
#' @details |
|
| 235 |
#' Specifying `pos` and `offset` will currently change the position of the |
|
| 236 |
#' text, but not of the field. |
|
| 237 |
#' @return |
|
| 238 |
#' `text_box()` is called it for its side-effects: it results in a graphic |
|
| 239 |
#' being displayed. |
|
| 240 |
#' @author N. Frerebeau |
|
| 241 |
#' @family geometries |
|
| 242 |
#' @keywords internal |
|
| 243 |
#' @noRd |
|
| 244 |
text_box <- function(x, y = NULL, labels = seq_along(x$x), padding = 1/3, |
|
| 245 |
rounding = 0.2, vertices = 100, |
|
| 246 |
cex = graphics::par("cex"), col = graphics::par("fg"),
|
|
| 247 |
bg = graphics::par("bg"), font = NULL, vfont = NULL, ...) {
|
|
| 248 | ||
| 249 | ! |
x <- grDevices::xy.coords(x = x, y = y) |
| 250 | ! |
srt <- list(...)$srt %||% graphics::par("srt")
|
| 251 | ||
| 252 | ! |
em <- graphics::strwidth("M", units = "user", cex = cex, font = font, vfont = vfont)
|
| 253 | ! |
ex <- graphics::strheight("X", units = "user", cex = cex, font = font, vfont = vfont)
|
| 254 | ||
| 255 | ! |
xo <- padding * em |
| 256 | ! |
yo <- padding * ex |
| 257 | ||
| 258 | ! |
width <- graphics::strwidth(labels, units = "user", cex = cex, font = font, vfont = vfont) |
| 259 | ! |
height <- graphics::strheight(labels, units = "user", cex = cex, font = font, vfont = vfont) |
| 260 | ||
| 261 | ! |
.mapply( |
| 262 | ! |
FUN = function(x, y, w, h, r, n, col, border, rotate) {
|
| 263 | ! |
rounded( |
| 264 | ! |
x0 = x - w - xo, |
| 265 | ! |
y0 = y - h - yo, |
| 266 | ! |
x1 = x + w + xo, |
| 267 | ! |
y1 = y + h + yo, |
| 268 | ! |
r = r, |
| 269 | ! |
n = n, |
| 270 | ! |
col = col, |
| 271 | ! |
border = border, |
| 272 | ! |
rotate = rotate, |
| 273 | ! |
aspect = TRUE |
| 274 |
) |
|
| 275 |
}, |
|
| 276 | ! |
dots = list(x = x$x, y = x$y, w = width * 0.5, h = height * 0.5, |
| 277 | ! |
col = bg, border = col, rotate = srt), |
| 278 | ! |
MoreArgs = list(r = rounding, n = vertices) |
| 279 |
) |
|
| 280 | ! |
graphics::text(x = x$x, y = x$y, labels = labels, col = col, cex = cex, |
| 281 | ! |
font = font, vfont = vfont, ...) |
| 282 | ||
| 283 | ! |
invisible(NULL) |
| 284 |
} |
|
| 285 | ||
| 286 |
# Shapes ======================================================================= |
|
| 287 |
#' Circle |
|
| 288 |
#' |
|
| 289 |
#' Draws a circle. |
|
| 290 |
#' @param x,y A length-one [`numeric`] vector giving the coordinates of the |
|
| 291 |
#' center of the circle. |
|
| 292 |
#' @param radius A length-one [`numeric`] vector giving the radius of the |
|
| 293 |
#' circle. |
|
| 294 |
#' @param n A length-on [`integer`] vector specifying the number of vertices to |
|
| 295 |
#' draw the circle. |
|
| 296 |
#' @param ... Further parameters to be passed to [graphics::polygon()]. |
|
| 297 |
#' @return |
|
| 298 |
#' `circle()` is called it for its side-effects: it results in a graphic |
|
| 299 |
#' being displayed. |
|
| 300 |
#' @author N. Frerebeau |
|
| 301 |
#' @family shapes |
|
| 302 |
#' @keywords internal |
|
| 303 |
#' @noRd |
|
| 304 |
circle <- function(x, y, radius, ..., n = 100) {
|
|
| 305 | 7x |
angle.inc <- 2 * pi / n |
| 306 | 7x |
angles <- seq(0, 2 * pi - angle.inc, by = angle.inc) |
| 307 | ||
| 308 | 7x |
xv <- cos(angles) * radius + x |
| 309 | 7x |
yv <- sin(angles) * radius + y |
| 310 | 7x |
graphics::polygon(xv, yv, ...) |
| 311 |
} |
|
| 312 | ||
| 313 |
#' Rounded Rectangle |
|
| 314 |
#' |
|
| 315 |
#' Draws a rectangular box with rounded left and right edges. |
|
| 316 |
#' @param x0,y0 A length-one [`numeric`] vector giving the coordinates of the |
|
| 317 |
#' bottom left angle. |
|
| 318 |
#' @param x1,y1 A length-one [`numeric`] vector giving the coordinates of the |
|
| 319 |
#' top right angle. |
|
| 320 |
#' @param r A length-one [`numeric`] vector giving the rounding of the edges. |
|
| 321 |
#' @param n A length-on [`integer`] vector specifying the number of vertices to |
|
| 322 |
#' draw. |
|
| 323 |
#' @param rotate A [`numeric`] vector giving the angle of rotation, in degrees. |
|
| 324 |
#' @param aspect A [`logical`] scalar: should the aspect ratio be kept during |
|
| 325 |
#' rotation? |
|
| 326 |
#' @param ... Further parameters to be passed to [graphics::polygon()]. |
|
| 327 |
#' @return |
|
| 328 |
#' `rounded()` is called it for its side-effects: it results in a graphic |
|
| 329 |
#' being displayed. |
|
| 330 |
#' @author N. Frerebeau |
|
| 331 |
#' @family shapes |
|
| 332 |
#' @keywords internal |
|
| 333 |
#' @noRd |
|
| 334 |
rounded <- function(x0, y0, x1, y1, ..., r = 0.2, n = 100, |
|
| 335 |
rotate = NULL, aspect = FALSE) {
|
|
| 336 | ||
| 337 | ! |
XD <- YD <- min(c(x1 - x0, y1 - y0)) |
| 338 | ! |
xi <- r * XD |
| 339 | ! |
yi <- r * YD |
| 340 | ||
| 341 |
## Elliptic corners function |
|
| 342 | ! |
elx <- function(from, to) xi * cos(seq(from, to, length.out = n / 4)) |
| 343 | ! |
ely <- function(from, to) yi * sin(seq(from, to, length.out = n / 4)) |
| 344 | ||
| 345 |
## Coordinates |
|
| 346 | ! |
x <- c(x1 - xi + elx(0, pi / 2), |
| 347 | ! |
x0 + xi + elx(pi / 2, pi), |
| 348 | ! |
x0 + xi + elx(pi, 3 * pi / 2), |
| 349 | ! |
x1 - xi + elx(3 * pi / 2, 2 * pi)) |
| 350 | ! |
y <- c(y1 - yi + ely(0, pi / 2), |
| 351 | ! |
y1 - yi + ely(pi / 2, pi), |
| 352 | ! |
y0 + yi + ely(pi, 3 * pi / 2), |
| 353 | ! |
y0 + yi + ely(3 * pi / 2, 2 * pi)) |
| 354 | ||
| 355 |
## Rotate |
|
| 356 | ! |
xy <- list(x = x, y = y) |
| 357 | ! |
if (!is.null(rotate)) xy <- rotate(xy$x, xy$y, angle = rotate, aspect = aspect) |
| 358 | ||
| 359 | ! |
graphics::polygon(x = xy$x, y = xy$y, ...) |
| 360 |
} |
|
| 361 | ||
| 362 |
# Helpers ====================================================================== |
|
| 363 |
#' Rotation in Euclidean Space |
|
| 364 |
#' |
|
| 365 |
#' Rotates points in the `xy` plane counterclockwise. |
|
| 366 |
#' @param x,y A [`numeric`] vector. If `y` is `NULL`, an attempt is made to |
|
| 367 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 368 |
#' @param angle A [`numeric`] vector giving the angle of rotation, in degrees. |
|
| 369 |
#' @param center A length-two [`numeric`] vector giving the coordinates of the |
|
| 370 |
#' rotation point. If `NULL`, defaults to centroid. |
|
| 371 |
#' @param aspect A [`logical`] scalar: should aspect ratio be kept? |
|
| 372 |
#' @return |
|
| 373 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 374 |
#' @example inst/examples/ex-rotate.R |
|
| 375 |
#' @keywords internal |
|
| 376 |
#' @noRd |
|
| 377 |
rotate <- function(x, y = NULL, angle = 0, center = NULL, aspect = FALSE) {
|
|
| 378 | ||
| 379 | ! |
xy <- grDevices::xy.coords(x = x, y = y) |
| 380 | ! |
if (is.null(center)) center <- c(mean(xy$x), mean(xy$y)) |
| 381 | ||
| 382 | ! |
theta <- angle / 180 * pi |
| 383 | ! |
cos_theta <- cos(theta) |
| 384 | ! |
sin_theta <- sin(theta) |
| 385 | ||
| 386 | ! |
dx <- xy$x - center[[1L]] |
| 387 | ! |
dy <- xy$y - center[[2L]] |
| 388 | ||
| 389 | ! |
ex <- center[[1L]] + cos_theta * dx - sin_theta * dy |
| 390 | ! |
ey <- center[[2L]] + sin_theta * dx + cos_theta * dy |
| 391 | ||
| 392 | ! |
if (aspect) {
|
| 393 | ! |
usr <- graphics::par("usr")
|
| 394 | ! |
pin <- graphics::par("pin")
|
| 395 | ! |
sy <- usr[[4L]] - usr[[3L]] |
| 396 | ! |
sx <- usr[[2L]] - usr[[1L]] |
| 397 | ! |
ey <- center[[2L]] + (ey - center[[2L]]) * sy / sx * pin[[1L]] / pin[[2L]] |
| 398 |
} |
|
| 399 | ||
| 400 | ! |
list(x = ex, y = ey) |
| 401 |
} |
| 1 |
# MUTATORS |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
# Non exported ================================================================= |
|
| 6 |
is_centered <- function(x) {
|
|
| 7 | 1x |
!all(x@center == 0) |
| 8 |
} |
|
| 9 |
is_scaled <- function(x) {
|
|
| 10 | 22x |
!all(x@scale == 1) |
| 11 |
} |
|
| 12 | ||
| 13 |
get_masses <- function(x, margin = 1) {
|
|
| 14 | 63x |
margin <- margin[[1L]] |
| 15 | 35x |
if (margin == 1) mass <- x@rows@weights |
| 16 | 28x |
if (margin == 2) mass <- x@columns@weights |
| 17 | 63x |
mass |
| 18 |
} |
|
| 19 | ||
| 20 |
get_order <- function(x, margin = 1) {
|
|
| 21 | 64x |
margin <- margin[[1L]] |
| 22 | 39x |
if (margin == 1) ord <- x@rows@order |
| 23 | 25x |
if (margin == 2) ord <- x@columns@order |
| 24 | 64x |
ord |
| 25 |
} |
|
| 26 | ||
| 27 |
## Supplementary variables ----------------------------------------------------- |
|
| 28 |
is_supplementary <- function(x, margin = 1) {
|
|
| 29 | ! |
margin <- margin[[1L]] |
| 30 | ! |
if (margin == 1) supp <- x@rows@supplement |
| 31 | ! |
if (margin == 2) supp <- x@columns@supplement |
| 32 | ! |
supp |
| 33 |
} |
|
| 34 | ||
| 35 |
has_supplementary <- function(x, margin = 1) {
|
|
| 36 | ! |
any(is_supplementary(x, margin = margin)) |
| 37 |
} |
|
| 38 | ||
| 39 |
get_extra <- function(x) {
|
|
| 40 | 37x |
as.data.frame(x@extra) |
| 41 |
} |
|
| 42 | ||
| 43 |
has_extra <- function(x) {
|
|
| 44 | ! |
all(lengths(x@extra) > 0) |
| 45 |
} |
|
| 46 | ||
| 47 |
`set_extra<-` <- function(x, value) {
|
|
| 48 |
## /!\ Reorder, see build_results() /!\ |
|
| 49 | 4x |
value <- lapply( |
| 50 | 4x |
X = value, |
| 51 | 4x |
FUN = function(val, i) { val[i] },
|
| 52 | 4x |
i = get_order(x, margin = 1) |
| 53 |
) |
|
| 54 | 4x |
x@extra <- value |
| 55 | 4x |
methods::validObject(x) |
| 56 | 4x |
x |
| 57 |
} |
|
| 58 | ||
| 59 |
# Groups ======================================================================= |
|
| 60 |
get_groups <- function(x, margin = 1) {
|
|
| 61 | ! |
margin <- margin[[1L]] |
| 62 | ! |
if (margin == 1) grp <- x@rows@groups |
| 63 | ! |
if (margin == 2) grp <- x@columns@groups |
| 64 | ! |
grp |
| 65 |
} |
|
| 66 | ||
| 67 |
`set_groups<-` <- function(x, margin = 1, value) {
|
|
| 68 | ! |
if (is.null(value)) value <- character(0) |
| 69 | ! |
margin <- margin[[1L]] |
| 70 | ! |
if (margin == 1) x@rows@groups <- value |
| 71 | ! |
if (margin == 2) x@columns@groups <- value |
| 72 | ! |
methods::validObject(x) |
| 73 | ! |
x |
| 74 |
} |
|
| 75 | ||
| 76 |
has_groups <- function(x, margin = 1) {
|
|
| 77 | 12x |
margin <- margin[[1L]] |
| 78 | 6x |
if (margin == 1) grp <- x@rows@groups |
| 79 | 6x |
if (margin == 2) grp <- x@columns@groups |
| 80 | 12x |
length(grp) > 0 |
| 81 |
} |
|
| 82 | ||
| 83 |
# Dimensions =================================================================== |
|
| 84 |
#' @export |
|
| 85 |
#' @method dim MultivariateAnalysis |
|
| 86 |
dim.MultivariateAnalysis <- function(x) {
|
|
| 87 | 2x |
x@dimension |
| 88 |
} |
|
| 89 | ||
| 90 |
#' @export |
|
| 91 |
#' @rdname dimnames |
|
| 92 |
#' @aliases dim,MultivariateAnalysis-method |
|
| 93 |
setMethod("dim", "MultivariateAnalysis", dim.MultivariateAnalysis)
|
|
| 94 | ||
| 95 |
#' @export |
|
| 96 |
#' @method rownames MultivariateAnalysis |
|
| 97 |
rownames.MultivariateAnalysis <- function(x, do.NULL = TRUE, prefix = "row") {
|
|
| 98 | 3x |
dn <- dimnames(x) |
| 99 | 3x |
if (!is.null(dn[[1L]])) |
| 100 | 3x |
dn[[1L]] |
| 101 |
else {
|
|
| 102 | ! |
nr <- NROW(x@rows@principal) |
| 103 | ! |
if (do.NULL) |
| 104 | ! |
NULL |
| 105 | ! |
else if (nr > 0L) |
| 106 | ! |
paste0(prefix, seq_len(nr)) |
| 107 | ! |
else character() |
| 108 |
} |
|
| 109 |
} |
|
| 110 | ||
| 111 |
#' @export |
|
| 112 |
#' @rdname dimnames |
|
| 113 |
#' @aliases rownames,MultivariateAnalysis-method |
|
| 114 |
setMethod("rownames", "MultivariateAnalysis", rownames.MultivariateAnalysis)
|
|
| 115 | ||
| 116 |
#' @export |
|
| 117 |
#' @method colnames MultivariateAnalysis |
|
| 118 |
colnames.MultivariateAnalysis <- function(x, do.NULL = TRUE, prefix = "col") {
|
|
| 119 | 2x |
dn <- dimnames(x) |
| 120 | 2x |
if (!is.null(dn[[2L]])) |
| 121 | 2x |
dn[[2L]] |
| 122 |
else {
|
|
| 123 | ! |
nc <- NROW(x@columns@principal) |
| 124 | ! |
if (do.NULL) |
| 125 | ! |
NULL |
| 126 | ! |
else if (nc > 0L) |
| 127 | ! |
paste0(prefix, seq_len(nc)) |
| 128 | ! |
else character() |
| 129 |
} |
|
| 130 |
} |
|
| 131 | ||
| 132 |
#' @export |
|
| 133 |
#' @rdname dimnames |
|
| 134 |
#' @aliases colnames,MultivariateAnalysis-method |
|
| 135 |
setMethod("colnames", "MultivariateAnalysis", colnames.MultivariateAnalysis)
|
|
| 136 | ||
| 137 |
#' @export |
|
| 138 |
#' @method dimnames MultivariateAnalysis |
|
| 139 |
dimnames.MultivariateAnalysis <- function(x) {
|
|
| 140 | 7x |
list(x@rows@names, x@columns@names) |
| 141 |
} |
|
| 142 | ||
| 143 |
#' @export |
|
| 144 |
#' @rdname dimnames |
|
| 145 |
#' @aliases dimnames,MultivariateAnalysis-method |
|
| 146 |
setMethod("dimnames", "MultivariateAnalysis", dimnames.MultivariateAnalysis)
|
| 1 |
# MULTIPLE CORRESPONDENCE ANALYSIS |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname mca |
|
| 7 |
#' @aliases mca,data.frame-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "mca", |
|
| 10 |
signature = c(object = "data.frame"), |
|
| 11 |
definition = function(object, rank = NULL, sup_row = NULL, sup_col = NULL, |
|
| 12 |
sup_quanti = NULL, autodetect = FALSE) {
|
|
| 13 |
## Remove numeric variables, if any |
|
| 14 | 2x |
clean <- drop_variable(object, f = is.numeric, negate = FALSE, |
| 15 | 2x |
sup = sup_col, extra = sup_quanti, auto = autodetect, |
| 16 | 2x |
what = "quantitative") |
| 17 | ||
| 18 |
## Compute MCA |
|
| 19 | 2x |
results <- methods::callGeneric(object = clean$data, rank = rank, |
| 20 | 2x |
sup_row = sup_row, sup_col = clean$sup) |
| 21 | ||
| 22 |
## Add supplementary quantitative variables |
|
| 23 | ! |
if (!is.null(clean$extra)) set_extra(results) <- clean$extra |
| 24 | ||
| 25 | 2x |
results |
| 26 |
} |
|
| 27 |
) |
|
| 28 | ||
| 29 |
#' @export |
|
| 30 |
#' @rdname mca |
|
| 31 |
#' @aliases mca,matrix-method |
|
| 32 |
setMethod( |
|
| 33 |
f = "mca", |
|
| 34 |
signature = c(object = "matrix"), |
|
| 35 |
definition = function(object, rank = NULL, sup_row = NULL, sup_col = NULL) {
|
|
| 36 |
## Subset |
|
| 37 | 2x |
is_row_sup <- find_variable(sup_row, nrow(object), names = rownames(object)) |
| 38 | 2x |
is_col_sup <- find_variable(sup_col, ncol(object), names = colnames(object)) |
| 39 | 2x |
N <- object[, !is_col_sup, drop = FALSE] |
| 40 | ||
| 41 |
## Complete disjunctive table |
|
| 42 | 2x |
Z <- cdt(N) |
| 43 | ||
| 44 |
## Check missing values |
|
| 45 | 2x |
arkhe::assert_missing(Z) |
| 46 | ||
| 47 |
## Get supplementary columns |
|
| 48 | 2x |
Z_tot <- Z |
| 49 | 2x |
sup_col <- NULL |
| 50 | 2x |
if (any(is_col_sup)) {
|
| 51 | ! |
Z_sup <- cdt(object[, is_col_sup, drop = FALSE]) |
| 52 | ! |
Z_tot <- cbind(Z, Z_sup) |
| 53 | ! |
sup_col <- seq_len(ncol(Z_sup)) + ncol(Z) |
| 54 |
} |
|
| 55 | ||
| 56 |
## Compute |
|
| 57 | 2x |
ndim <- min(rank, ncol(Z_tot) - sum(!is_col_sup)) |
| 58 | 2x |
results <- ca(Z_tot, rank = ndim, sup_row = sup_row, sup_col = sup_col) |
| 59 | ||
| 60 | 2x |
.MCA(results) |
| 61 |
} |
|
| 62 |
) |
| 1 |
# PLOT ELLIPSE |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname viz_ellipses |
|
| 7 |
#' @aliases viz_ellipses,numeric,numeric-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "viz_ellipses", |
|
| 10 |
signature = c(x = "numeric", y = "numeric"), |
|
| 11 |
definition = function(x, y, ..., group = NULL, |
|
| 12 |
type = c("tolerance", "confidence"), level = 0.95,
|
|
| 13 |
color = NULL, fill = FALSE, symbol = FALSE) {
|
|
| 14 | ! |
type <- match.arg(type, several.ok = FALSE) |
| 15 | ! |
fun <- switch( |
| 16 | ! |
type, |
| 17 | ! |
tolerance = wrap_tolerance, |
| 18 | ! |
confidence = wrap_confidence |
| 19 |
) |
|
| 20 | ! |
ell <- fun(x, y, group = group, level = level) |
| 21 | ! |
.viz_ellipses(ell, color = color, fill = fill, symbol = symbol, ...) |
| 22 | ||
| 23 | ! |
invisible(list(x = x, y = y)) |
| 24 |
} |
|
| 25 |
) |
|
| 26 | ||
| 27 |
#' @export |
|
| 28 |
#' @rdname viz_ellipses |
|
| 29 |
#' @aliases viz_ellipses,MultivariateAnalysis,missing-method |
|
| 30 |
setMethod( |
|
| 31 |
f = "viz_ellipses", |
|
| 32 |
signature = c(x = "MultivariateAnalysis", y = "missing"), |
|
| 33 |
definition = function(x, ..., group = NULL, |
|
| 34 |
type = c("tolerance", "confidence"), level = 0.95,
|
|
| 35 |
color = NULL, fill = FALSE, symbol = FALSE) {
|
|
| 36 | ! |
type <- match.arg(type, several.ok = FALSE) |
| 37 | ! |
fun <- switch( |
| 38 | ! |
type, |
| 39 | ! |
tolerance = wrap_tolerance, |
| 40 | ! |
confidence = wrap_confidence |
| 41 |
) |
|
| 42 | ! |
ell <- fun(x, margin = get_margin(), axes = get_axes(), |
| 43 | ! |
group = group, level = level, principal = get_principal()) |
| 44 | ! |
.viz_ellipses(ell, color = color, fill = fill, symbol = symbol, ...) |
| 45 | ||
| 46 | ! |
invisible(x) |
| 47 |
} |
|
| 48 |
) |
|
| 49 | ||
| 50 |
#' @export |
|
| 51 |
#' @rdname viz_ellipses |
|
| 52 |
#' @aliases viz_ellipses,PCOA,missing-method |
|
| 53 |
setMethod( |
|
| 54 |
f = "viz_ellipses", |
|
| 55 |
signature = c(x = "PCOA", y = "missing"), |
|
| 56 |
definition = function(x, ..., group = NULL, |
|
| 57 |
type = c("tolerance", "confidence"), level = 0.95,
|
|
| 58 |
color = NULL, fill = FALSE, symbol = FALSE) {
|
|
| 59 | 2x |
type <- match.arg(type, several.ok = FALSE) |
| 60 | 2x |
fun <- switch( |
| 61 | 2x |
type, |
| 62 | 2x |
tolerance = wrap_tolerance, |
| 63 | 2x |
confidence = wrap_confidence |
| 64 |
) |
|
| 65 | 2x |
ell <- fun(x, axes = get_axes(), group = group, level = level) |
| 66 | 2x |
.viz_ellipses(ell, color = color, fill = fill, symbol = symbol, ...) |
| 67 | ||
| 68 | 2x |
invisible(x) |
| 69 |
} |
|
| 70 |
) |
|
| 71 | ||
| 72 | ||
| 73 |
#' @param x A `list` of `matrix` returned by [wrap_ellipse()]. |
|
| 74 |
#' @noRd |
|
| 75 |
.viz_ellipses <- function(x, ..., color = NULL, fill = FALSE, symbol = FALSE) {
|
|
| 76 | 2x |
n <- length(x) |
| 77 | ||
| 78 |
## Recycle graphical parameters if of length one |
|
| 79 | 2x |
dots <- list(...) |
| 80 | 2x |
col <- recycle(dots$border %||% graphics::par("fg"), n)
|
| 81 | 2x |
bg <- recycle(dots$col %||% NA, n) |
| 82 | 2x |
lty <- recycle(dots$lty %||% graphics::par("lty"), n)
|
| 83 | 2x |
lwd <- recycle(dots$lwd %||% graphics::par("lwd"), n)
|
| 84 | ||
| 85 | 2x |
if (n > 1) {
|
| 86 |
## Discrete scales |
|
| 87 | 2x |
extra_quali <- names(x) |
| 88 | 2x |
if (is.null(dots$border) && !isFALSE(color)) |
| 89 | 2x |
col <- khroma::palette_color_discrete(colors = color)(extra_quali) |
| 90 | 2x |
if (is.null(dots$col) && !isFALSE(fill)) |
| 91 | ! |
bg <- khroma::palette_color_discrete(colors = fill)(extra_quali) |
| 92 | 2x |
if (is.null(dots$lty) && !isFALSE(symbol)) |
| 93 | ! |
lty <- khroma::palette_line(types = symbol)(extra_quali) |
| 94 |
} |
|
| 95 | ||
| 96 | 2x |
for (i in seq_along(x)) {
|
| 97 | 6x |
lvl <- x[[i]] |
| 98 | 6x |
for (j in seq_along(lvl)) {
|
| 99 | 6x |
graphics::polygon( |
| 100 | 6x |
x = lvl[[j]], |
| 101 | 6x |
border = col[i], |
| 102 | 6x |
col = bg[i], |
| 103 | 6x |
lty = lty[i], |
| 104 | 6x |
lwd = lwd[i] |
| 105 |
) |
|
| 106 |
} |
|
| 107 |
} |
|
| 108 | ||
| 109 | 2x |
invisible(x) |
| 110 |
} |
|
| 111 | ||
| 112 |
# Tolerance ==================================================================== |
|
| 113 |
#' @export |
|
| 114 |
#' @rdname viz_tolerance |
|
| 115 |
#' @aliases viz_tolerance,numeric,numeric-method |
|
| 116 |
setMethod( |
|
| 117 |
f = "viz_tolerance", |
|
| 118 |
signature = c(x = "numeric", y = "numeric"), |
|
| 119 |
definition = function(x, y, ..., group = NULL, level = 0.95, |
|
| 120 |
color = NULL, fill = FALSE, symbol = FALSE) {
|
|
| 121 | ! |
viz_ellipses(x, y, group = group, type = "tolerance", level = level, |
| 122 | ! |
color = color, fill = fill, symbol = symbol, ...) |
| 123 | ! |
invisible(list(x = x, y = y)) |
| 124 |
} |
|
| 125 |
) |
|
| 126 | ||
| 127 |
#' @export |
|
| 128 |
#' @rdname viz_tolerance |
|
| 129 |
#' @aliases viz_tolerance,MultivariateAnalysis,missing-method |
|
| 130 |
setMethod( |
|
| 131 |
f = "viz_tolerance", |
|
| 132 |
signature = c(x = "MultivariateAnalysis", y = "missing"), |
|
| 133 |
definition = function(x, ..., group = NULL, level = 0.95, |
|
| 134 |
color = NULL, fill = FALSE, symbol = FALSE) {
|
|
| 135 | ! |
viz_ellipses(x, group = group, type = "tolerance", level = level, |
| 136 | ! |
color = color, fill = fill, symbol = symbol, ...) |
| 137 | ! |
invisible(x) |
| 138 |
} |
|
| 139 |
) |
|
| 140 | ||
| 141 |
#' @export |
|
| 142 |
#' @rdname viz_tolerance |
|
| 143 |
#' @aliases viz_tolerance,MultivariateBootstrap,missing-method |
|
| 144 |
setMethod( |
|
| 145 |
f = "viz_tolerance", |
|
| 146 |
signature = c(x = "MultivariateBootstrap", y = "missing"), |
|
| 147 |
definition = function(x, ..., level = 0.95, |
|
| 148 |
color = FALSE, fill = FALSE, symbol = FALSE) {
|
|
| 149 | ! |
viz_ellipses(x, group = NULL, type = "tolerance", level = level, |
| 150 | ! |
color = color, fill = fill, symbol = symbol, ...) |
| 151 | ! |
invisible(x) |
| 152 |
} |
|
| 153 |
) |
|
| 154 | ||
| 155 |
#' @export |
|
| 156 |
#' @rdname viz_tolerance |
|
| 157 |
#' @aliases viz_tolerance,PCOA,missing-method |
|
| 158 |
setMethod( |
|
| 159 |
f = "viz_tolerance", |
|
| 160 |
signature = c(x = "PCOA", y = "missing"), |
|
| 161 |
definition = function(x, ..., group = NULL, level = 0.95, |
|
| 162 |
color = NULL, fill = FALSE, symbol = FALSE) {
|
|
| 163 | ! |
viz_ellipses(x, group = group, type = "tolerance", level = level, |
| 164 | ! |
color = color, fill = fill, symbol = symbol, ...) |
| 165 | ! |
invisible(x) |
| 166 |
} |
|
| 167 |
) |
|
| 168 | ||
| 169 |
# Confidence =================================================================== |
|
| 170 |
#' @export |
|
| 171 |
#' @rdname viz_confidence |
|
| 172 |
#' @aliases viz_confidence,numeric,numeric-method |
|
| 173 |
setMethod( |
|
| 174 |
f = "viz_confidence", |
|
| 175 |
signature = c(x = "numeric", y = "numeric"), |
|
| 176 |
definition = function(x, y, ..., group = NULL, level = 0.95, |
|
| 177 |
color = NULL, fill = FALSE, symbol = FALSE) {
|
|
| 178 | ! |
viz_ellipses(x, y, group = group, type = "confidence", level = level, |
| 179 | ! |
color = color, fill = fill, symbol = symbol, ...) |
| 180 | ! |
invisible(list(x = x, y = y)) |
| 181 |
} |
|
| 182 |
) |
|
| 183 | ||
| 184 |
#' @export |
|
| 185 |
#' @rdname viz_confidence |
|
| 186 |
#' @aliases viz_confidence,MultivariateAnalysis,missing-method |
|
| 187 |
setMethod( |
|
| 188 |
f = "viz_confidence", |
|
| 189 |
signature = c(x = "MultivariateAnalysis", y = "missing"), |
|
| 190 |
definition = function(x, ..., group = NULL, level = 0.95, |
|
| 191 |
color = NULL, fill = FALSE, symbol = FALSE) {
|
|
| 192 | ! |
viz_ellipses(x, group = group, type = "confidence", level = level, |
| 193 | ! |
color = color, fill = fill, symbol = symbol, ...) |
| 194 | ! |
invisible(x) |
| 195 |
} |
|
| 196 |
) |
|
| 197 | ||
| 198 |
#' @export |
|
| 199 |
#' @rdname viz_confidence |
|
| 200 |
#' @aliases viz_confidence,MultivariateBootstrap,missing-method |
|
| 201 |
setMethod( |
|
| 202 |
f = "viz_confidence", |
|
| 203 |
signature = c(x = "MultivariateBootstrap", y = "missing"), |
|
| 204 |
definition = function(x, ..., level = 0.95, |
|
| 205 |
color = FALSE, fill = FALSE, symbol = FALSE) {
|
|
| 206 | ! |
viz_ellipses(x, group = NULL, type = "confidence", level = level, |
| 207 | ! |
color = color, fill = fill, symbol = symbol, ...) |
| 208 | ! |
invisible(x) |
| 209 |
} |
|
| 210 |
) |
|
| 211 | ||
| 212 |
#' @export |
|
| 213 |
#' @rdname viz_confidence |
|
| 214 |
#' @aliases viz_confidence,PCOA,missing-method |
|
| 215 |
setMethod( |
|
| 216 |
f = "viz_confidence", |
|
| 217 |
signature = c(x = "PCOA", y = "missing"), |
|
| 218 |
definition = function(x, ..., axes = c(1, 2), group = NULL, level = 0.95, |
|
| 219 |
color = NULL, fill = FALSE, symbol = FALSE) {
|
|
| 220 | ! |
viz_ellipses(x, group = group, type = "confidence", level = level, |
| 221 | ! |
color = color, fill = fill, symbol = symbol, ...) |
| 222 | ! |
invisible(x) |
| 223 |
} |
|
| 224 |
) |
| 1 |
# SHOW |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
setMethod( |
|
| 6 |
f = "show", |
|
| 7 |
signature = "CA", |
|
| 8 |
definition = function(object) {
|
|
| 9 | 1x |
cat( |
| 10 | 1x |
tr_("Correspondence Analysis (CA):"),
|
| 11 | 1x |
utils::capture.output(describe(object)), |
| 12 | 1x |
sep = "\n" |
| 13 |
) |
|
| 14 | 1x |
invisible(object) |
| 15 |
} |
|
| 16 |
) |
|
| 17 | ||
| 18 |
setMethod( |
|
| 19 |
f = "show", |
|
| 20 |
signature = "MCA", |
|
| 21 |
definition = function(object) {
|
|
| 22 | 1x |
cat( |
| 23 | 1x |
tr_("Multiple Correspondence Analysis (MCA):"),
|
| 24 | 1x |
utils::capture.output(describe(object)), |
| 25 | 1x |
sep = "\n" |
| 26 |
) |
|
| 27 | 1x |
invisible(object) |
| 28 |
} |
|
| 29 |
) |
|
| 30 | ||
| 31 |
setMethod( |
|
| 32 |
f = "show", |
|
| 33 |
signature = "PCA", |
|
| 34 |
definition = function(object) {
|
|
| 35 | 1x |
cat( |
| 36 | 1x |
tr_("Principal Components Analysis (PCA):"),
|
| 37 | 1x |
utils::capture.output(describe(object)), |
| 38 | 1x |
sep = "\n" |
| 39 |
) |
|
| 40 | 1x |
invisible(object) |
| 41 |
} |
|
| 42 |
) |
|
| 43 | ||
| 44 |
setMethod( |
|
| 45 |
f = "show", |
|
| 46 |
signature = "PCOA", |
|
| 47 |
definition = function(object) {
|
|
| 48 | ! |
cat( |
| 49 | ! |
tr_("Principal Coordinate Analysis (PCoA):"),
|
| 50 | ! |
sprintf(tr_("* Method: %s."), object@method),
|
| 51 | ! |
sep = "\n" |
| 52 |
) |
|
| 53 | ! |
invisible(object) |
| 54 |
} |
|
| 55 |
) |
|
| 56 | ||
| 57 |
setMethod( |
|
| 58 |
f = "show", |
|
| 59 |
signature = "MultivariateSummary", |
|
| 60 |
definition = function(object) {
|
|
| 61 |
## Get options |
|
| 62 | 6x |
n_dig <- getOption("dimensio.digits")
|
| 63 | 6x |
n_max <- getOption("dimensio.max.print")
|
| 64 | ||
| 65 | 6x |
if (methods::is(object, "SummaryCA")) {
|
| 66 | 3x |
active <- c(tr_("Active rows"), tr_("Active columns"))
|
| 67 | 3x |
suppl <- c(tr_("Supplementary rows"), tr_("Supplementary columns"))
|
| 68 | 3x |
title <- tr_("Correspondence Analysis (CA)")
|
| 69 |
} |
|
| 70 | 6x |
if (methods::is(object, "SummaryPCA")) {
|
| 71 | 3x |
active <- c(tr_("Active individuals"), tr_("Active variables"))
|
| 72 | 3x |
suppl <- c(tr_("Supplementary individuals"), tr_("Supplementary variables"))
|
| 73 | 3x |
title <- tr_("Principal Components Analysis (PCA)")
|
| 74 |
} |
|
| 75 | ||
| 76 |
## Get data |
|
| 77 | 6x |
eig <- round(object@eigenvalues, digits = n_dig) |
| 78 | 6x |
res <- round(object@results, digits = n_dig) |
| 79 | ||
| 80 |
## Prepare data |
|
| 81 | 6x |
is_sup <- object@supplement |
| 82 | 6x |
eigen <- c(paste0("\n## ", tr_("Eigenvalues")), "",
|
| 83 | 6x |
utils::capture.output(format_table(eig))) |
| 84 | ||
| 85 |
## Supplementary points |
|
| 86 | 6x |
sum_sup <- extra_sup <- NULL |
| 87 | 6x |
if (any(is_sup)) {
|
| 88 | 4x |
res_sup <- res[is_sup, ] |
| 89 | 4x |
n_sup <- nrow(res_sup) |
| 90 | 4x |
if (n_sup > n_max) {
|
| 91 | ! |
res_sup <- res_sup[seq_len(n_max), ] |
| 92 | ! |
extra_sup <- sprintf("(%s more)", n_sup - n_max)
|
| 93 |
} |
|
| 94 | 4x |
is_na <- apply(X = res_sup, MARGIN = 2, FUN = anyNA) |
| 95 | 4x |
res_sup <- res_sup[, !is_na] |
| 96 | 4x |
sum_sup <- c(paste0("\n## ", suppl[[object@margin]]), "",
|
| 97 | 4x |
utils::capture.output(format_table(res_sup))) |
| 98 |
} |
|
| 99 | ||
| 100 |
## Active points |
|
| 101 | 6x |
sum_act <- extra_act <- NULL |
| 102 | 6x |
if (any(!is_sup)) {
|
| 103 | 4x |
res_act <- res[!is_sup, ] |
| 104 | 4x |
n_act <- nrow(res_act) |
| 105 | 4x |
if (n_act > n_max) {
|
| 106 | 4x |
res_act <- res_act[seq_len(n_max), ] |
| 107 | 4x |
extra_act <- sprintf("(%s more)", n_act - n_max)
|
| 108 |
} |
|
| 109 | 4x |
sum_act <- c(paste0("\n## ", active[[object@margin]]), "",
|
| 110 | 4x |
utils::capture.output(format_table(res_act))) |
| 111 |
} |
|
| 112 | ||
| 113 |
|
|
| 114 | 6x |
header <- paste0("# ", title)
|
| 115 | 6x |
cat(header, eigen, sum_act, extra_act, sum_sup, extra_sup, sep = "\n") |
| 116 | 6x |
invisible(object) |
| 117 |
} |
|
| 118 |
) |
|
| 119 | ||
| 120 | ||
| 121 |
format_table <- function(x) {
|
|
| 122 | 14x |
val <- rbind(colnames(x), format_head(colnames(x), left = FALSE), x) |
| 123 | 14x |
val <- apply(X = val, MARGIN = 2, FUN = format_col, left = FALSE) |
| 124 | 14x |
row_names <- c("", format_head(rownames(x))[which.max(nchar(rownames(x)))], rownames(x))
|
| 125 | 14x |
val <- cbind(format_col(row_names), val) |
| 126 | 14x |
val <- apply(X = val, MARGIN = 1, FUN = format_row) |
| 127 | 14x |
cat(val, sep = "\n") |
| 128 |
} |
|
| 129 | ||
| 130 |
vec_rep <- function(x, times) {
|
|
| 131 | 28x |
force(x) |
| 132 | 28x |
vapply( |
| 133 | 28x |
X = times, |
| 134 | 28x |
FUN = function(i) paste0(rep(x, i), collapse = ""), |
| 135 | 28x |
FUN.VALUE = character(1) |
| 136 |
) |
|
| 137 |
} |
|
| 138 |
format_head <- function(x, left = TRUE) {
|
|
| 139 | 28x |
n <- nchar(x) - 1 |
| 140 | 28x |
d <- vec_rep("-", n)
|
| 141 | 14x |
if (left) paste0(":", d) else paste0(d, ":")
|
| 142 |
} |
|
| 143 |
format_col <- function(x, left = TRUE) {
|
|
| 144 | 80x |
n <- max(nchar(x)) |
| 145 | 80x |
d <- vapply( |
| 146 | 80x |
X = n - nchar(x), |
| 147 | 80x |
FUN = function(i) ifelse(i == 0, "", paste0(rep(" ", i), collapse = "")),
|
| 148 | 80x |
FUN.VALUE = character(1) |
| 149 |
) |
|
| 150 | 14x |
if (left) paste0(x, d) else paste0(d, x) |
| 151 |
} |
|
| 152 |
format_row <- function(x) {
|
|
| 153 | 116x |
paste0("| ", paste0(x, collapse = " | "), " |")
|
| 154 |
} |
| 1 |
# SCREEPLOT |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
# Screeplot ==================================================================== |
|
| 6 |
#' @export |
|
| 7 |
#' @method screeplot MultivariateAnalysis |
|
| 8 |
screeplot.MultivariateAnalysis <- function(x, ..., eigenvalues = FALSE, cumulative = FALSE, |
|
| 9 |
labels = TRUE, limit = 10, |
|
| 10 |
col = "grey90", border = "grey10", |
|
| 11 |
col.cumulative = "red", lty.cumulative = "solid", |
|
| 12 |
lwd.cumulative = 2) {
|
|
| 13 |
## TODO |
|
| 14 | 8x |
horiz <- FALSE |
| 15 | ||
| 16 |
## Save and restore graphical parameters |
|
| 17 | 8x |
old_par <- graphics::par(mar = c(5, 4, 4, 2 + 2 * cumulative) + 0.1, |
| 18 | 8x |
no.readonly = TRUE) |
| 19 | 8x |
on.exit(graphics::par(old_par), add = TRUE) |
| 20 | ||
| 21 |
## Prepare data |
|
| 22 | 8x |
data <- get_eigenvalues(x) |
| 23 | 8x |
data$x <- seq_len(nrow(data)) |
| 24 | 8x |
data$z <- data[[3L]] |
| 25 | ||
| 26 |
## Subset |
|
| 27 | 8x |
if (!is.null(limit)) {
|
| 28 | 8x |
limit <- min(nrow(data), limit) |
| 29 | 8x |
data <- data[seq_len(limit), , drop = FALSE] |
| 30 |
} |
|
| 31 | ||
| 32 | 8x |
if (eigenvalues) {
|
| 33 | 4x |
data$y <- data[[1L]] |
| 34 | 4x |
data$labels <- round(data$y, digits = 1) |
| 35 | 4x |
ylab <- tr_("Eigenvalues")
|
| 36 |
} else {
|
|
| 37 | 4x |
data$y <- data[[2L]] |
| 38 | 4x |
data$labels <- paste0(round(data$y, digits = 1), "%") |
| 39 | 4x |
if (methods::is(x, "CA")) {
|
| 40 | 2x |
ylab <- tr_("Proportion of inertia (%)")
|
| 41 |
} else {
|
|
| 42 | 2x |
ylab <- tr_("Explained variance (%)")
|
| 43 |
} |
|
| 44 |
} |
|
| 45 | ||
| 46 | 8x |
k <- max(data$y) / max(data$z) |
| 47 | 8x |
data$k <- data$z * k |
| 48 | ||
| 49 |
## Bar plot |
|
| 50 | 8x |
mid <- graphics::barplot( |
| 51 | 8x |
height = data$y, |
| 52 | 8x |
names.arg = data$x, |
| 53 | 8x |
horiz = horiz, |
| 54 | 8x |
xlab = if (horiz) ylab else NULL, |
| 55 | 8x |
ylab = if (horiz) NULL else ylab, |
| 56 | 8x |
ylim = c(0, max(data$k)) * 1.05, |
| 57 | 8x |
col = col, |
| 58 | 8x |
border = border, |
| 59 | 8x |
las = 1, |
| 60 |
... |
|
| 61 |
) |
|
| 62 | ||
| 63 | 8x |
if (labels) {
|
| 64 | 8x |
graphics::text( |
| 65 | 8x |
x = mid, |
| 66 | 8x |
y = data$y, |
| 67 | 8x |
labels = data$labels, |
| 68 | 8x |
pos = 3 |
| 69 |
) |
|
| 70 |
} |
|
| 71 | ||
| 72 | 8x |
if (cumulative && !horiz) {
|
| 73 | 4x |
if (methods::is(x, "CA")) {
|
| 74 | 2x |
ylab2 <- tr_("Cumulative inertia (%)")
|
| 75 |
} else {
|
|
| 76 | 2x |
ylab2 <- tr_("Cumulative variance (%)")
|
| 77 |
} |
|
| 78 | 4x |
tick_labels <- seq(from = 0, to = 100, by = 20) |
| 79 | 4x |
tick_at <- tick_labels * k |
| 80 | 4x |
graphics::lines( |
| 81 | 4x |
x = mid, |
| 82 | 4x |
y = data$k, |
| 83 | 4x |
type = "b", |
| 84 | 4x |
pch = 16, |
| 85 | 4x |
lty = lty.cumulative, |
| 86 | 4x |
lwd = lwd.cumulative, |
| 87 | 4x |
col = col.cumulative |
| 88 |
) |
|
| 89 | 4x |
graphics::axis(side = 4, at = tick_at, labels = tick_labels, |
| 90 | 4x |
col = col.cumulative, col.ticks = col.cumulative, |
| 91 | 4x |
col.axis = col.cumulative, las = 1) |
| 92 | 4x |
graphics::mtext( |
| 93 | 4x |
text = ylab2, |
| 94 | 4x |
side = 4, line = 3, col = col.cumulative |
| 95 |
) |
|
| 96 |
} |
|
| 97 | ||
| 98 | 8x |
invisible(x) |
| 99 |
} |
|
| 100 | ||
| 101 |
#' @export |
|
| 102 |
#' @rdname screeplot |
|
| 103 |
#' @aliases screeplot,MultivariateAnalysis-method |
|
| 104 |
setMethod("screeplot", c(x = "MultivariateAnalysis"), screeplot.MultivariateAnalysis)
|
|
| 105 | ||
| 106 |
#' @export |
|
| 107 |
#' @method screeplot PCOA |
|
| 108 |
screeplot.PCOA <- function(x, ..., labels = FALSE, limit = NULL, |
|
| 109 |
col = "grey90", border = "grey10") {
|
|
| 110 |
## TODO |
|
| 111 | 1x |
horiz <- FALSE |
| 112 | ||
| 113 |
## Prepare data |
|
| 114 | 1x |
data <- get_eigenvalues(x) |
| 115 | 1x |
data$x <- seq_len(nrow(data)) |
| 116 | 1x |
data$y <- data[[1L]] |
| 117 | 1x |
data$labels <- round(data$y, digits = 1) |
| 118 | ||
| 119 |
## Subset |
|
| 120 | 1x |
if (!is.null(limit)) {
|
| 121 | ! |
limit <- min(nrow(data), limit) |
| 122 | ! |
data <- data[seq_len(limit), , drop = FALSE] |
| 123 |
} |
|
| 124 | ||
| 125 |
## Bar plot |
|
| 126 | 1x |
ylab <- tr_("Eigenvalues")
|
| 127 | 1x |
mid <- graphics::barplot( |
| 128 | 1x |
height = data$y, |
| 129 | 1x |
names.arg = data$x, |
| 130 | 1x |
horiz = horiz, |
| 131 | 1x |
xlab = if (horiz) ylab else NULL, |
| 132 | 1x |
ylab = if (horiz) NULL else ylab, |
| 133 | 1x |
ylim = c(0, max(data$y)) * 1.05, |
| 134 | 1x |
col = col, |
| 135 | 1x |
border = border, |
| 136 | 1x |
las = 1, |
| 137 |
... |
|
| 138 |
) |
|
| 139 | ||
| 140 | 1x |
if (labels) {
|
| 141 | ! |
graphics::text( |
| 142 | ! |
x = mid, |
| 143 | ! |
y = data$y, |
| 144 | ! |
labels = data$labels, |
| 145 | ! |
pos = 3 |
| 146 |
) |
|
| 147 |
} |
|
| 148 | ||
| 149 | 1x |
invisible(x) |
| 150 |
} |
|
| 151 | ||
| 152 |
#' @export |
|
| 153 |
#' @rdname screeplot |
|
| 154 |
#' @aliases screeplot,PCOA-method |
|
| 155 |
setMethod("screeplot", c(x = "PCOA"), screeplot.PCOA)
|
| 1 |
# CONVEX HULL |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname viz_hull |
|
| 7 |
#' @aliases wrap_hull,numeric,numeric-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "wrap_hull", |
|
| 10 |
signature = c(x = "numeric", y = "numeric"), |
|
| 11 |
definition = function(x, y, group = NULL) {
|
|
| 12 |
## Validation |
|
| 13 | 4x |
n <- length(x) |
| 14 | 4x |
arkhe::assert_length(y, n) |
| 15 | ||
| 16 |
## Add groups, if any |
|
| 17 | ! |
if (is.null(group)) group <- rep("", n)
|
| 18 | 4x |
group <- as.character(group) |
| 19 | 4x |
arkhe::assert_length(group, n) |
| 20 | ||
| 21 |
## Clean |
|
| 22 | 4x |
ok <- !is.na(x) & !is.na(y) & !is.na(group) |
| 23 | 4x |
x <- x[ok] |
| 24 | 4x |
y <- y[ok] |
| 25 | 4x |
group <- group[ok] |
| 26 | ||
| 27 |
## Compute convex hulls |
|
| 28 | 4x |
index <- split(seq_along(group), f = group) |
| 29 | 4x |
lapply( |
| 30 | 4x |
X = index, |
| 31 | 4x |
FUN = function(i) {
|
| 32 | 12x |
xi <- x[i] |
| 33 | 12x |
yi <- y[i] |
| 34 | ! |
if (length(xi) < 3) return(NULL) |
| 35 | ||
| 36 | 12x |
i <- grDevices::chull(xi, yi) |
| 37 | 12x |
cbind(xi, yi)[c(i, i[1]), , drop = FALSE] |
| 38 |
} |
|
| 39 |
) |
|
| 40 |
} |
|
| 41 |
) |
|
| 42 | ||
| 43 |
#' @export |
|
| 44 |
#' @rdname viz_hull |
|
| 45 |
#' @aliases wrap_hull,MultivariateAnalysis,missing-method |
|
| 46 |
setMethod( |
|
| 47 |
f = "wrap_hull", |
|
| 48 |
signature = c(x = "MultivariateAnalysis", y = "missing"), |
|
| 49 |
definition = function(x, margin = 1, axes = c(1, 2), group = NULL, |
|
| 50 |
principal = TRUE) {
|
|
| 51 |
## Validation |
|
| 52 | 3x |
arkhe::assert_scalar(margin, "numeric") |
| 53 | 3x |
arkhe::assert_type(axes, "numeric") |
| 54 | 3x |
arkhe::assert_length(axes, 2) |
| 55 | ||
| 56 |
## Get coordinates |
|
| 57 | 3x |
data <- get_coordinates(x, margin = margin, principal = principal) |
| 58 | 3x |
data <- data[, axes] |
| 59 | ||
| 60 |
## Add groups, if any |
|
| 61 | 3x |
if (length(group) > 1) {
|
| 62 | 3x |
group <- group[get_order(x, margin = margin)] |
| 63 | ! |
} else if (length(group) == 1) {
|
| 64 | ! |
group <- get_extra(x)[[group]] |
| 65 | ! |
} else if (has_groups(x, margin = margin)) {
|
| 66 | ! |
group <- get_groups(x, margin = margin) |
| 67 |
} |
|
| 68 | ||
| 69 |
## Compute convex hulls |
|
| 70 | 3x |
methods::callGeneric(x = data[, 1], y = data[, 2], group = group) |
| 71 |
} |
|
| 72 |
) |
|
| 73 | ||
| 74 |
#' @export |
|
| 75 |
#' @rdname viz_hull |
|
| 76 |
#' @aliases wrap_hull,PCOA,missing-method |
|
| 77 |
setMethod( |
|
| 78 |
f = "wrap_hull", |
|
| 79 |
signature = c(x = "PCOA", y = "missing"), |
|
| 80 |
definition = function(x, axes = c(1, 2), group = NULL) {
|
|
| 81 |
## Validation |
|
| 82 | 1x |
arkhe::assert_type(axes, "numeric") |
| 83 | 1x |
arkhe::assert_length(axes, 2) |
| 84 | ||
| 85 |
## Get coordinates |
|
| 86 | 1x |
data <- get_coordinates(x) |
| 87 | 1x |
data <- data[, axes] |
| 88 | ||
| 89 |
## Compute convex hulls |
|
| 90 | 1x |
methods::callGeneric(x = data[, 1], y = data[, 2], group = group) |
| 91 |
} |
|
| 92 |
) |
| 1 |
# BOOTSTRAP |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
# CA =========================================================================== |
|
| 6 |
#' @export |
|
| 7 |
#' @rdname bootstrap |
|
| 8 |
#' @aliases bootstrap,CA-method |
|
| 9 |
setMethod( |
|
| 10 |
f = "bootstrap", |
|
| 11 |
signature = c(object = "CA"), |
|
| 12 |
definition = function(object, n = 30) {
|
|
| 13 |
## Data replication |
|
| 14 | ! |
n <- as.integer(n) |
| 15 | ! |
arkhe::assert_scalar(n, "integer") |
| 16 | ||
| 17 | ! |
data <- object@data |
| 18 | ! |
data <- data[!object@rows@supplement, !object@columns@supplement] |
| 19 | ! |
repl <- stats::rmultinom(n = n, size = sum(data), prob = data) |
| 20 | ||
| 21 | ! |
i <- nrow(data) |
| 22 | ! |
j <- ncol(data) |
| 23 | ||
| 24 | ! |
k_n <- seq_len(n) |
| 25 | ! |
k_i <- seq_len(i) |
| 26 | ! |
k_j <- seq_len(j) |
| 27 | ||
| 28 | ! |
new_row <- matrix(data = NA_integer_, nrow = i * n, ncol = j) |
| 29 | ! |
new_col <- matrix(data = NA_integer_, nrow = i, ncol = j * n) |
| 30 | ! |
for (p in k_n) {
|
| 31 | ! |
m_i <- k_i + i * (p - 1) |
| 32 | ! |
m_j <- k_j + j * (p - 1) |
| 33 | ! |
new_row[m_i, ] <- repl[, p] |
| 34 | ! |
new_col[, m_j] <- repl[, p] |
| 35 |
} |
|
| 36 | ||
| 37 | ! |
res_row <- ca(rbind(data, new_row), sup_row = 1:(i * n) + i) |
| 38 | ! |
res_col <- ca(cbind(data, new_col), sup_col = 1:(j * n) + j) |
| 39 | ||
| 40 |
## Set names |
|
| 41 | ! |
names_row <- rep_len(object@rows@names, i * (n + 1)) |
| 42 | ! |
names_col <- rep_len(object@columns@names, j * (n + 1)) |
| 43 | ! |
res_row@rows@names <- make.unique(names_row, sep = "_") |
| 44 | ! |
res_col@columns@names <- make.unique(names_col, sep = "_") |
| 45 | ||
| 46 |
## Set groups |
|
| 47 | ! |
res_row@rows@groups <- names_row |
| 48 | ! |
res_col@columns@groups <- names_col |
| 49 | ||
| 50 | ! |
.BootstrapCA( |
| 51 | ! |
object, |
| 52 | ! |
rows = res_row@rows, |
| 53 | ! |
columns = res_col@columns, |
| 54 | ! |
replications = n |
| 55 |
) |
|
| 56 |
} |
|
| 57 |
) |
|
| 58 | ||
| 59 |
# PCA ========================================================================== |
|
| 60 |
#' @export |
|
| 61 |
#' @rdname bootstrap |
|
| 62 |
#' @aliases bootstrap,PCA-method |
|
| 63 |
setMethod( |
|
| 64 |
f = "bootstrap", |
|
| 65 |
signature = c(object = "PCA"), |
|
| 66 |
definition = function(object, n = 30) {
|
|
| 67 |
## Get data |
|
| 68 | ! |
n <- as.integer(n) |
| 69 | ! |
arkhe::assert_scalar(n, "integer") |
| 70 | ||
| 71 | ! |
data <- object@data |
| 72 | ! |
data <- data[!object@rows@supplement, !object@columns@supplement] |
| 73 | ! |
U <- object@rows@standard |
| 74 | ! |
w <- object@rows@weights |
| 75 | ! |
i <- nrow(data) |
| 76 | ! |
j <- ncol(data) |
| 77 | ||
| 78 | ! |
k_n <- seq_len(n) |
| 79 | ! |
k_i <- seq_len(i) |
| 80 | ! |
k_j <- seq_len(j) |
| 81 | ||
| 82 |
## Data replication |
|
| 83 | ! |
new_coord <- matrix(data = NA_integer_, nrow = j * n, ncol = ncol(U)) |
| 84 | ! |
new_dist <- vector(mode = "numeric", length = j * n) |
| 85 | ! |
for (p in k_n) {
|
| 86 | ! |
m_j <- k_j + j * (p - 1) |
| 87 | ! |
z <- sample(i, size = i, replace = TRUE) |
| 88 | ! |
w_i <- w[z] |
| 89 | ! |
new_data <- data[z, ] |
| 90 | ||
| 91 |
## Principal coordinates |
|
| 92 |
# Center and scale |
|
| 93 | ! |
if (is_centered(object)) {
|
| 94 | ! |
new_data <- t(t(new_data) - weighted_mean(new_data, w_i)) |
| 95 |
} |
|
| 96 | ! |
if (is_scaled(object)) {
|
| 97 | ! |
new_data <- t(t(new_data) / weighted_sd(new_data, w_i)) |
| 98 |
} |
|
| 99 | ! |
var_sup <- new_data * w_i |
| 100 | ! |
new_coord[m_j, ] <- crossprod(var_sup, U[z, ]) |
| 101 | ||
| 102 |
## Squared distance to centroide |
|
| 103 | ! |
new_dist[m_j] <- colSums(new_data^2 * w_i) |
| 104 |
} |
|
| 105 | ||
| 106 |
## Squared cosine |
|
| 107 | ! |
new_cos <- new_coord^2 / new_dist |
| 108 | ||
| 109 |
## Set names |
|
| 110 | ! |
names_col <- rep_len(object@columns@names, j * (n + 1)) |
| 111 | ||
| 112 | ! |
new_col <- build_results( |
| 113 | ! |
names = make.unique(names_col, sep = "_"), |
| 114 | ! |
principal = rbind(object@columns@principal, new_coord), |
| 115 | ! |
standard = object@columns@standard, |
| 116 | ! |
contributions = object@columns@contributions, |
| 117 | ! |
distances = c(object@columns@distances, new_dist), |
| 118 | ! |
cosine = rbind(object@columns@cosine, new_cos), |
| 119 | ! |
weights = object@columns@weights, |
| 120 | ! |
supplement = c(object@columns@supplement, !logical(j * n)), |
| 121 | ! |
groups = names_col |
| 122 |
) |
|
| 123 | ||
| 124 | ! |
.BootstrapPCA( |
| 125 | ! |
object, |
| 126 | ! |
columns = new_col, |
| 127 | ! |
replications = n |
| 128 |
) |
|
| 129 |
} |
|
| 130 |
) |
| 1 |
# GENERIC METHODS |
|
| 2 |
#' @include AllClasses.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
# S4 dispatch to base S3 generic =============================================== |
|
| 6 |
setGeneric("rownames")
|
|
| 7 |
setGeneric("colnames")
|
|
| 8 |
setGeneric("dimnames")
|
|
| 9 |
setGeneric("loadings")
|
|
| 10 |
setGeneric("biplot")
|
|
| 11 | ||
| 12 |
# Import S4 generics =========================================================== |
|
| 13 |
#' @importMethodsFrom arkhe bootstrap |
|
| 14 |
#' @importMethodsFrom arkhe describe |
|
| 15 |
NULL |
|
| 16 | ||
| 17 |
# Extract ====================================================================== |
|
| 18 |
## Dimnames -------------------------------------------------------------------- |
|
| 19 |
#' Dimnames of an Object |
|
| 20 |
#' |
|
| 21 |
#' Retrieve or set the dimnames of an object. |
|
| 22 |
#' @param x An object from which to retrieve the row or column names |
|
| 23 |
#' (a [`CA-class`] or [`PCA-class`] object). |
|
| 24 |
#' @param do.NULL A [`logical`] scalar. If `FALSE` and names are `NULL`, names |
|
| 25 |
#' are created. |
|
| 26 |
#' @param prefix A [`character`] string specifying the prefix for created names. |
|
| 27 |
#' @author N. Frerebeau |
|
| 28 |
#' @docType methods |
|
| 29 |
#' @family mutators |
|
| 30 |
#' @name dimnames |
|
| 31 |
#' @rdname dimnames |
|
| 32 |
NULL |
|
| 33 | ||
| 34 |
## Subset ---------------------------------------------------------------------- |
|
| 35 |
#' Extract Parts of an Object |
|
| 36 |
#' |
|
| 37 |
#' Operators acting on objects to extract parts. |
|
| 38 |
#' @param x An object from which to extract element(s) or in which to replace |
|
| 39 |
#' element(s). |
|
| 40 |
#' @param i A [`character`] string specifying elements to extract. |
|
| 41 |
#' Any unambiguous substring can be given (see details). |
|
| 42 |
#' @details |
|
| 43 |
#' If `i` is "`data`", returns a list with the following elements: |
|
| 44 |
#' \describe{
|
|
| 45 |
#' \item{`data`}{A [`numeric`] matrix of raw data.}
|
|
| 46 |
#' \item{`mean`}{A [`numeric`] vector giving the variables means (`PCA`).}
|
|
| 47 |
#' \item{`sd`}{A [`numeric`] vector giving the variables standard deviations
|
|
| 48 |
#' (`PCA`).} |
|
| 49 |
#' } |
|
| 50 |
#' |
|
| 51 |
#' If `i` is "`rows`", returns a list with the following elements: |
|
| 52 |
#' \describe{
|
|
| 53 |
#' \item{`coord`}{A [`numeric`] matrix of rows/individuals coordinates.}
|
|
| 54 |
#' \item{`cos2`}{A [`numeric`] matrix of rows/individuals squared cosine.}
|
|
| 55 |
#' \item{`masses`}{A [`numeric`] vector giving the rows masses/individual
|
|
| 56 |
#' weights.} |
|
| 57 |
#' \item{`sup`}{A [`logical`] vector specifying whether a point is a
|
|
| 58 |
#' supplementary observation or not.} |
|
| 59 |
#' } |
|
| 60 |
#' |
|
| 61 |
#' If `i` is "`columns`", returns a list with the following elements: |
|
| 62 |
#' \describe{
|
|
| 63 |
#' \item{`coord`}{A [`numeric`] matrix of columns/variables coordinates.}
|
|
| 64 |
#' \item{\code{cor}}{A [`numeric`] matrix of correlation between variables and
|
|
| 65 |
#' the dimensions (`PCA`).} |
|
| 66 |
#' \item{`cos2`}{A [`numeric`] matrix of columns/variables squared cosine.}
|
|
| 67 |
#' \item{`masses`}{A [`numeric`] vector giving the columns masses/variable
|
|
| 68 |
#' weights.} |
|
| 69 |
#' \item{`sup`}{A [`logical`] vector specifying whether a point is a
|
|
| 70 |
#' supplementary observation or not.} |
|
| 71 |
#' } |
|
| 72 |
#' |
|
| 73 |
#' If `i` is "`eigenvalues`", returns a [`numeric`] vector of eigenvalues. |
|
| 74 |
#' @return |
|
| 75 |
#' A [`list`]. |
|
| 76 |
#' @example inst/examples/ex-subset.R |
|
| 77 |
#' @author N. Frerebeau |
|
| 78 |
#' @docType methods |
|
| 79 |
#' @family mutators |
|
| 80 |
#' @name subset |
|
| 81 |
#' @rdname subset |
|
| 82 |
NULL |
|
| 83 | ||
| 84 |
# CA =========================================================================== |
|
| 85 |
#' Correspondence Analysis |
|
| 86 |
#' |
|
| 87 |
#' Computes a simple correspondence analysis based on the singular value |
|
| 88 |
#' decomposition. |
|
| 89 |
#' @param object A \eqn{m \times p}{m x p} `numeric` [`matrix`] or a
|
|
| 90 |
#' [`data.frame`]. |
|
| 91 |
#' @param rank An [`integer`] value specifying the maximal number of |
|
| 92 |
#' components to be kept in the results. If `NULL` (the default), |
|
| 93 |
#' \eqn{min(m, p) - 1} components will be returned.
|
|
| 94 |
#' @param sup_row A `vector` specifying the indices of the supplementary rows. |
|
| 95 |
#' @param sup_col A `vector` specifying the indices of the supplementary columns. |
|
| 96 |
#' @param sup_quali A `vector` specifying the indices of the supplementary |
|
| 97 |
#' qualitative columns. |
|
| 98 |
#' @param autodetect A [`logical`] scalar: should non-numeric variables be |
|
| 99 |
#' automatically removed? |
|
| 100 |
#' @param ... Currently not used. |
|
| 101 |
#' @return |
|
| 102 |
#' A [`CA-class`] object. |
|
| 103 |
#' @example inst/examples/ex-ca.R |
|
| 104 |
#' @seealso [svd()] |
|
| 105 |
#' @references |
|
| 106 |
#' Greenacre, M. J. *Theory and Applications of Correspondence Analysis*. |
|
| 107 |
#' London: Academic Press, 1984. |
|
| 108 |
#' |
|
| 109 |
#' Greenacre, M. J. *Correspondence Analysis in Practice*. Seconde edition. |
|
| 110 |
#' Interdisciplinary Statistics Series. Boca Raton: Chapman & Hall/CRC, 2007. |
|
| 111 |
#' |
|
| 112 |
#' Lebart, L., Piron, M. and Morineau, A. *Statistique exploratoire |
|
| 113 |
#' multidimensionnelle: visualisation et inférence en fouille de données*. |
|
| 114 |
#' Paris: Dunod, 2006. |
|
| 115 |
#' @author N. Frerebeau |
|
| 116 |
#' @docType methods |
|
| 117 |
#' @family multivariate analysis |
|
| 118 |
#' @aliases ca-method |
|
| 119 |
setGeneric( |
|
| 120 |
name = "ca", |
|
| 121 |
def = function(object, ...) standardGeneric("ca"),
|
|
| 122 |
valueClass = "CA" |
|
| 123 |
) |
|
| 124 | ||
| 125 |
# MCA ========================================================================== |
|
| 126 |
#' Multiple Correspondence Analysis |
|
| 127 |
#' |
|
| 128 |
#' Computes a multiple correspondence analysis. |
|
| 129 |
#' @param object A \eqn{m \times p}{m x p} `numeric` [`matrix`] or a
|
|
| 130 |
#' [`data.frame`]. |
|
| 131 |
#' @param rank An [`integer`] value specifying the maximal number of |
|
| 132 |
#' components to be kept in the results. If `NULL` (the default), |
|
| 133 |
#' \eqn{min(m, p) - 1} components will be returned.
|
|
| 134 |
#' @param sup_row A `vector` specifying the indices of the supplementary rows. |
|
| 135 |
#' @param sup_col A `vector` specifying the indices of the supplementary |
|
| 136 |
#' categorical columns. |
|
| 137 |
#' @param sup_quanti A `vector` specifying the indices of the supplementary |
|
| 138 |
#' quantitative columns. |
|
| 139 |
#' @param autodetect A [`logical`] scalar: should numeric variables be |
|
| 140 |
#' automatically removed (except `sup_quanti`)? |
|
| 141 |
#' @param ... Currently not used. |
|
| 142 |
#' @return |
|
| 143 |
#' A [`MCA-class`] object. |
|
| 144 |
# @example inst/examples/ex-mca.R |
|
| 145 |
#' @seealso [svd()], [cdt()] |
|
| 146 |
#' @references |
|
| 147 |
#' Lebart, L., Piron, M. and Morineau, A. *Statistique exploratoire |
|
| 148 |
#' multidimensionnelle: visualisation et inférence en fouille de données*. |
|
| 149 |
#' Paris: Dunod, 2006. |
|
| 150 |
#' @author N. Frerebeau |
|
| 151 |
#' @docType methods |
|
| 152 |
#' @family multivariate analysis |
|
| 153 |
#' @aliases mca-method |
|
| 154 |
setGeneric( |
|
| 155 |
name = "mca", |
|
| 156 |
def = function(object, ...) standardGeneric("mca"),
|
|
| 157 |
valueClass = "MCA" |
|
| 158 |
) |
|
| 159 | ||
| 160 |
# PCA ========================================================================== |
|
| 161 |
#' Principal Components Analysis |
|
| 162 |
#' |
|
| 163 |
#' Computes a principal components analysis based on the singular value |
|
| 164 |
#' decomposition. |
|
| 165 |
#' @param object A \eqn{m \times p}{m x p} `numeric` [`matrix`] or a
|
|
| 166 |
#' [`data.frame`]. |
|
| 167 |
#' @param center A [`logical`] scalar: should the variables be shifted to be |
|
| 168 |
#' zero centered? |
|
| 169 |
#' @param scale A [`logical`] scalar: should the variables be scaled to unit |
|
| 170 |
#' variance? |
|
| 171 |
#' @param rank An [`integer`] value specifying the maximal number of components |
|
| 172 |
#' to be kept in the results. If `NULL` (the default), \eqn{p - 1} components
|
|
| 173 |
#' will be returned. |
|
| 174 |
#' @param sup_row A `vector` specifying the indices of the supplementary rows. |
|
| 175 |
#' @param sup_col A `vector` specifying the indices of the supplementary columns. |
|
| 176 |
#' @param sup_quali A `vector` specifying the indices of the supplementary |
|
| 177 |
#' qualitative columns. |
|
| 178 |
#' @param weight_row A [`numeric`] vector specifying the active row (individual) |
|
| 179 |
#' weights. If `NULL` (the default), uniform weights are used. Row weights are |
|
| 180 |
#' internally normalized to sum 1 |
|
| 181 |
#' @param weight_col A [`numeric`] vector specifying the active column |
|
| 182 |
#' (variable) weights. If `NULL` (the default), uniform weights (1) are |
|
| 183 |
#' used. |
|
| 184 |
#' @param autodetect A [`logical`] scalar: should non-numeric variables be |
|
| 185 |
#' automatically removed (except `sup_quali`)? |
|
| 186 |
#' @param ... Currently not used. |
|
| 187 |
#' @return |
|
| 188 |
#' A [`PCA-class`] object. |
|
| 189 |
#' @example inst/examples/ex-pca.R |
|
| 190 |
#' @seealso [svd()] |
|
| 191 |
#' @references |
|
| 192 |
#' Lebart, L., Piron, M. and Morineau, A. *Statistique exploratoire |
|
| 193 |
#' multidimensionnelle: visualisation et inférence en fouille de données*. |
|
| 194 |
#' Paris: Dunod, 2006. |
|
| 195 |
#' @author N. Frerebeau |
|
| 196 |
#' @docType methods |
|
| 197 |
#' @family multivariate analysis |
|
| 198 |
#' @aliases pca-method |
|
| 199 |
setGeneric( |
|
| 200 |
name = "pca", |
|
| 201 |
def = function(object, ...) standardGeneric("pca"),
|
|
| 202 |
valueClass = "PCA" |
|
| 203 |
) |
|
| 204 | ||
| 205 |
# PCoA ========================================================================= |
|
| 206 |
#' Principal Coordinates Analysis |
|
| 207 |
#' |
|
| 208 |
#' Computes classical (metric) multidimensional scaling. |
|
| 209 |
#' @param object A [distance structure][stats::dist()]. |
|
| 210 |
#' @param rank An [`integer`] value specifying the maximal number dimension of |
|
| 211 |
#' the space which the data are to be represented in. |
|
| 212 |
#' @param ... Currently not used. |
|
| 213 |
#' @return |
|
| 214 |
#' A [`PCOA-class`] object. |
|
| 215 |
#' @references |
|
| 216 |
#' Gower, J. C. (1966). Some Distance Properties of Latent Root and Vector |
|
| 217 |
#' Methods Used in Multivariate Analysis. *Biometrika*, 53(3‑4): 325-338. |
|
| 218 |
#' \doi{10.1093/biomet/53.3-4.325}.
|
|
| 219 |
#' @example inst/examples/ex-pcoa.R |
|
| 220 |
#' @seealso [stats::cmdscale()] |
|
| 221 |
#' @author N. Frerebeau |
|
| 222 |
#' @docType methods |
|
| 223 |
#' @family multivariate analysis |
|
| 224 |
#' @aliases pcoa-method |
|
| 225 |
setGeneric( |
|
| 226 |
name = "pcoa", |
|
| 227 |
def = function(object, ...) standardGeneric("pcoa"),
|
|
| 228 |
valueClass = "PCOA" |
|
| 229 |
) |
|
| 230 | ||
| 231 |
# Predict ====================================================================== |
|
| 232 |
#' Predict New Coordinates |
|
| 233 |
#' |
|
| 234 |
#' Predict the projection of new individuals/rows or variables/columns. |
|
| 235 |
#' @param object A [`CA-class`] or [`PCA-class`] object. |
|
| 236 |
#' @param newdata An object of supplementary points coercible to a |
|
| 237 |
#' [`matrix`] for which to compute principal coordinates. |
|
| 238 |
#' @param margin A length-one [`numeric`] vector giving the subscript which the |
|
| 239 |
#' data will be predicted: `1` indicates individuals/rows (the default), `2` |
|
| 240 |
#' indicates variables/columns. |
|
| 241 |
#' @return |
|
| 242 |
#' A [`data.frame`] of coordinates. |
|
| 243 |
#' @example inst/examples/ex-predict.R |
|
| 244 |
#' @author N. Frerebeau |
|
| 245 |
#' @docType methods |
|
| 246 |
#' @family multivariate analysis |
|
| 247 |
#' @name predict |
|
| 248 |
#' @rdname predict |
|
| 249 |
NULL |
|
| 250 | ||
| 251 |
# Bootstrap ==================================================================== |
|
| 252 |
#' Partial Bootstrap Analysis |
|
| 253 |
#' |
|
| 254 |
#' Checks analysis with partial bootstrap resampling. |
|
| 255 |
#' @param object A [`CA-class`] or [`PCA-class`] object. |
|
| 256 |
#' @param n A non-negative [`integer`] giving the number of bootstrap |
|
| 257 |
#' replications. |
|
| 258 |
#' @return |
|
| 259 |
#' Returns a [`BootstrapCA-class`] or a [`BootstrapPCA-class`] object. |
|
| 260 |
#' @example inst/examples/ex-bootstrap.R |
|
| 261 |
#' @references |
|
| 262 |
#' Greenacre, Michael J. *Theory and Applications of Correspondence |
|
| 263 |
#' Analysis*. London: Academic Press, 1984. |
|
| 264 |
#' |
|
| 265 |
#' Lebart, L., Piron, M. and Morineau, A. *Statistique exploratoire |
|
| 266 |
#' multidimensionnelle: visualisation et inférence en fouille de données*. |
|
| 267 |
#' Paris: Dunod, 2006. |
|
| 268 |
#' |
|
| 269 |
#' Lockyear, K. (2013). Applying Bootstrapped Correspondence Analysis to |
|
| 270 |
#' Archaeological Data. *Journal of Archaeological Science*, 40(12): 4744-4753. |
|
| 271 |
#' \doi{10.1016/j.jas.2012.08.035}.
|
|
| 272 |
#' |
|
| 273 |
#' Ringrose, T. J. (1992). Bootstrapping and Correspondence Analysis in |
|
| 274 |
#' Archaeology. *Journal of Archaeological Science*, 19(6): 615-629. |
|
| 275 |
#' \doi{10.1016/0305-4403(92)90032-X}.
|
|
| 276 |
#' @author N. Frerebeau |
|
| 277 |
#' @docType methods |
|
| 278 |
#' @family resampling methods |
|
| 279 |
#' @name bootstrap |
|
| 280 |
#' @rdname bootstrap |
|
| 281 |
NULL |
|
| 282 | ||
| 283 |
# Results ====================================================================== |
|
| 284 |
#' Export Results |
|
| 285 |
#' |
|
| 286 |
#' Creates a Zip archive of all results in CSV format. |
|
| 287 |
#' @param object A [`CA-class`], [`MCA-class`] or [`PCA-class`] object. |
|
| 288 |
#' @param file A [`character`] string specifying the pathname of the zip file. |
|
| 289 |
#' @param flags A [`character`] string of flags (see [utils::zip()]). |
|
| 290 |
#' @param ... Currently not used. |
|
| 291 |
#' @example inst/examples/ex-export.R |
|
| 292 |
#' @seealso [utils::write.csv()], [utils::zip()] |
|
| 293 |
#' @author N. Frerebeau |
|
| 294 |
#' @docType methods |
|
| 295 |
#' @family getters |
|
| 296 |
#' @aliases export-method |
|
| 297 |
setGeneric( |
|
| 298 |
name = "export", |
|
| 299 | ! |
def = function(object, ...) standardGeneric("export")
|
| 300 |
) |
|
| 301 | ||
| 302 |
### Data ----------------------------------------------------------------------- |
|
| 303 |
#' Get Original Data |
|
| 304 |
#' |
|
| 305 |
#' @param x An object from which to get element(s) (a [`CA-class`], |
|
| 306 |
#' [`MCA-class`] or [`PCA-class`] object). |
|
| 307 |
#' @param ... Currently not used. |
|
| 308 |
#' @return |
|
| 309 |
#' Returns a [`data.frame`] of original data. |
|
| 310 |
#' @author N. Frerebeau |
|
| 311 |
#' @docType methods |
|
| 312 |
#' @family getters |
|
| 313 |
#' @aliases get_data-method |
|
| 314 |
setGeneric( |
|
| 315 |
name = "get_data", |
|
| 316 |
def = function(x, ...) standardGeneric("get_data"),
|
|
| 317 |
valueClass = "data.frame" |
|
| 318 |
) |
|
| 319 | ||
| 320 |
## Coordinates ----------------------------------------------------------------- |
|
| 321 |
#' Get Coordinates |
|
| 322 |
#' |
|
| 323 |
#' @param x An object from which to get element(s) (a [`CA-class`], |
|
| 324 |
#' [`MCA-class`] or [`PCA-class`] object). |
|
| 325 |
#' @param margin A length-one [`numeric`] vector giving the subscript which the |
|
| 326 |
#' data will be returned: `1` indicates individuals/rows (the default), `2` |
|
| 327 |
#' indicates variables/columns. |
|
| 328 |
#' @param principal A [`logical`] scalar: should principal coordinates be |
|
| 329 |
#' returned? If `FALSE`, standard coordinates are returned. |
|
| 330 |
#' @param sup_name A [`character`] string specifying the name of the column to |
|
| 331 |
#' create for supplementary points attribution (see below). |
|
| 332 |
#' @param ... Currently not used. |
|
| 333 |
#' @return |
|
| 334 |
#' * `get_coordinates()` returns a [`data.frame`] of coordinates. An extra |
|
| 335 |
#' column (named after `sup_name`) is added specifying whether an observation |
|
| 336 |
#' is a supplementary point or not. |
|
| 337 |
#' * `get_replications()` returns an [`array`] of coordinates. |
|
| 338 |
#' @example inst/examples/ex-coordinates.R |
|
| 339 |
#' @author N. Frerebeau |
|
| 340 |
#' @docType methods |
|
| 341 |
#' @family getters |
|
| 342 |
#' @aliases get_coordinates-method |
|
| 343 |
setGeneric( |
|
| 344 |
name = "get_coordinates", |
|
| 345 |
def = function(x, ...) standardGeneric("get_coordinates"),
|
|
| 346 |
valueClass = "data.frame" |
|
| 347 |
) |
|
| 348 | ||
| 349 |
#' @rdname get_coordinates |
|
| 350 |
#' @aliases get_replications-method |
|
| 351 |
setGeneric( |
|
| 352 |
name = "get_replications", |
|
| 353 |
def = function(x, ...) standardGeneric("get_replications"),
|
|
| 354 |
valueClass = "array" |
|
| 355 |
) |
|
| 356 | ||
| 357 |
## Eigenvalues ----------------------------------------------------------------- |
|
| 358 |
#' Get Eigenvalues |
|
| 359 |
#' |
|
| 360 |
#' @param x An object from which to get element(s) (a [`CA-class`], |
|
| 361 |
#' [`MCA-class`] or [`PCA-class`] object). |
|
| 362 |
#' @param margin A length-one [`numeric`] vector giving the subscript which the |
|
| 363 |
#' data will be returned: `1` indicates individuals/rows (the default), `2` |
|
| 364 |
#' indicates variables/columns. |
|
| 365 |
#' @param digits An [`integer`] indicating the number of decimal places to be |
|
| 366 |
#' used. |
|
| 367 |
#' @param ... Currently not used. |
|
| 368 |
#' @return |
|
| 369 |
#' * `get_eigenvalues()` returns a [`data.frame`] with the following columns: |
|
| 370 |
#' `eigenvalues`, `variance` (percentage of variance) and `cumulative` |
|
| 371 |
#' (cumulative percentage of variance). |
|
| 372 |
#' * `get_variance()` returns a [`numeric`] vector giving the amount of |
|
| 373 |
#' variance explained by each (principal) component. |
|
| 374 |
#' * `get_distance()`returns a [`numeric`] vector of squared distance to the |
|
| 375 |
#' centroid. |
|
| 376 |
#' * `get_inertia()` returns a [`numeric`] vector giving the inertia (weighted |
|
| 377 |
#' squared distance to the centroid). |
|
| 378 |
#' @author N. Frerebeau |
|
| 379 |
#' @docType methods |
|
| 380 |
#' @family getters |
|
| 381 |
#' @aliases get_eigenvalues-method |
|
| 382 |
setGeneric( |
|
| 383 |
name = "get_eigenvalues", |
|
| 384 |
def = function(x) standardGeneric("get_eigenvalues"),
|
|
| 385 |
valueClass = "data.frame" |
|
| 386 |
) |
|
| 387 | ||
| 388 |
#' @rdname get_eigenvalues |
|
| 389 |
#' @aliases get_variance-method |
|
| 390 |
setGeneric( |
|
| 391 |
name = "get_variance", |
|
| 392 |
def = function(x, ...) standardGeneric("get_variance"),
|
|
| 393 |
valueClass = "numeric" |
|
| 394 |
) |
|
| 395 | ||
| 396 |
#' @rdname get_eigenvalues |
|
| 397 |
#' @aliases get_distances-method |
|
| 398 |
setGeneric( |
|
| 399 |
name = "get_distances", |
|
| 400 |
def = function(x, ...) standardGeneric("get_distances"),
|
|
| 401 |
valueClass = "numeric" |
|
| 402 |
) |
|
| 403 | ||
| 404 |
#' @rdname get_eigenvalues |
|
| 405 |
#' @aliases get_inertia-method |
|
| 406 |
setGeneric( |
|
| 407 |
name = "get_inertia", |
|
| 408 |
def = function(x, ...) standardGeneric("get_inertia"),
|
|
| 409 |
valueClass = "numeric" |
|
| 410 |
) |
|
| 411 | ||
| 412 |
## Contributions --------------------------------------------------------------- |
|
| 413 |
#' Get Contributions |
|
| 414 |
#' |
|
| 415 |
#' @param x An object from which to get element(s) (a [`CA-class`], |
|
| 416 |
#' [`MCA-class`] or [`PCA-class`] object). |
|
| 417 |
#' @param margin A length-one [`numeric`] vector giving the subscript which the |
|
| 418 |
#' data will be returned: `1` indicates individuals/rows (the default), `2` |
|
| 419 |
#' indicates variables/columns. |
|
| 420 |
#' @param sup_name A [`character`] string specifying the name of the column to |
|
| 421 |
#' create for supplementary points attribution (see below). |
|
| 422 |
#' @param ... Currently not used. |
|
| 423 |
#' @return |
|
| 424 |
#' * `get_contributions()` returns a [`data.frame`] of contributions to the |
|
| 425 |
#' definition of the principal dimensions. |
|
| 426 |
#' * `get_correlations()` returns a [`data.frame`] of correlations between |
|
| 427 |
#' variables and dimensions. An extra column (named after `sup_name`) |
|
| 428 |
#' is added specifying whether an observation is a supplementary point or |
|
| 429 |
#' not. |
|
| 430 |
#' * `get_cos2()` returns a [`data.frame`] of \eqn{cos^2}{cos2} values (i.e.
|
|
| 431 |
#' quality of the representation of the points on the factor map). An extra |
|
| 432 |
#' column (named after `sup_name`) is added specifying whether an observation |
|
| 433 |
#' is a supplementary point or not. |
|
| 434 |
#' @author N. Frerebeau |
|
| 435 |
#' @docType methods |
|
| 436 |
#' @family getters |
|
| 437 |
#' @aliases get_contributions-method |
|
| 438 |
setGeneric( |
|
| 439 |
name = "get_contributions", |
|
| 440 |
def = function(x, ...) standardGeneric("get_contributions"),
|
|
| 441 |
valueClass = "data.frame" |
|
| 442 |
) |
|
| 443 | ||
| 444 |
#' @rdname get_contributions |
|
| 445 |
#' @aliases get_correlations-method |
|
| 446 |
setGeneric( |
|
| 447 |
name = "get_correlations", |
|
| 448 |
def = function(x, ...) standardGeneric("get_correlations"),
|
|
| 449 |
valueClass = "data.frame" |
|
| 450 |
) |
|
| 451 | ||
| 452 |
#' @rdname get_contributions |
|
| 453 |
#' @aliases get_cos2-method |
|
| 454 |
setGeneric( |
|
| 455 |
name = "get_cos2", |
|
| 456 |
def = function(x, ...) standardGeneric("get_cos2"),
|
|
| 457 |
valueClass = "data.frame" |
|
| 458 |
) |
|
| 459 | ||
| 460 |
# Plot ========================================================================= |
|
| 461 |
#' Plot Coordinates |
|
| 462 |
#' |
|
| 463 |
#' @param x An \R object. |
|
| 464 |
#' @param ... Further [graphical parameters][graphics::par]. |
|
| 465 |
#' @inheritParams viz_points |
|
| 466 |
#' @author N. Frerebeau |
|
| 467 |
#' @docType methods |
|
| 468 |
#' @family plot methods |
|
| 469 |
#' @name plot |
|
| 470 |
#' @rdname plot |
|
| 471 |
NULL |
|
| 472 | ||
| 473 |
## Biplot ---------------------------------------------------------------------- |
|
| 474 |
#' Biplot |
|
| 475 |
#' |
|
| 476 |
#' @param x A [`CA-class`], [`MCA-class`] or [`PCA-class`] object. |
|
| 477 |
#' @param axes A length-two [`numeric`] vector giving the dimensions to be |
|
| 478 |
#' plotted. |
|
| 479 |
#' @param type A [`character`] string specifying the biplot to be plotted |
|
| 480 |
#' (see below). It must be one of "`rows`", "`columns`", "`contribution`" (CA), |
|
| 481 |
#' "`form`" or "`covariance`" (PCA). Any unambiguous substring can be given. |
|
| 482 |
#' @param labels A [`character`] vector specifying whether |
|
| 483 |
#' "`rows`"/"`individuals`" and/or "`columns`"/"`variables`" names must be |
|
| 484 |
#' drawn. Any unambiguous substring can be given. |
|
| 485 |
#' @param col.rows,col.columns A length-two `vector` of color specification for |
|
| 486 |
#' the active and supplementary rows/columns. |
|
| 487 |
#' @param pch.rows,pch.columns A length-two `vector` of symbol specification for |
|
| 488 |
#' the active and supplementary rows/columns. |
|
| 489 |
#' @param lty.columns A length-two `vector` of line type specification for |
|
| 490 |
#' the active and supplementary columns. |
|
| 491 |
#' @param size A length-two [`numeric`] vector giving range of possible sizes |
|
| 492 |
#' (greater than 0). Only used if `type` is "`contribution`" (CA). |
|
| 493 |
#' @param xlim A length-two [`numeric`] vector giving the x limits of the plot. |
|
| 494 |
#' The default value, `NULL`, indicates that the range of the |
|
| 495 |
#' [finite][is.finite()] values to be plotted should be used. |
|
| 496 |
#' @param ylim A length-two [`numeric`] vector giving the y limits of the plot. |
|
| 497 |
#' The default value, `NULL`, indicates that the range of the |
|
| 498 |
#' [finite][is.finite()] values to be plotted should be used. |
|
| 499 |
#' @param main A [`character`] string giving a main title for the plot. |
|
| 500 |
#' @param sub A [`character`] string giving a subtitle for the plot. |
|
| 501 |
#' @param legend A [`list`] of additional arguments to be passed to |
|
| 502 |
#' [graphics::legend()]; names of the list are used as argument names. |
|
| 503 |
#' If `NULL`, no legend is displayed. |
|
| 504 |
#' @inheritParams prepare_plot |
|
| 505 |
#' @param ... Currently not used. |
|
| 506 |
#' @details |
|
| 507 |
#' A biplot is the simultaneous representation of rows and columns of a |
|
| 508 |
#' rectangular dataset. It is the generalization of a scatterplot to the case |
|
| 509 |
#' of mutlivariate data: it allows to visualize as much information as possible |
|
| 510 |
#' in a single graph (Greenacre 2010). |
|
| 511 |
#' |
|
| 512 |
#' Biplots have the drawbacks of their advantages: they can quickly become |
|
| 513 |
#' difficult to read as they display a lot of information at once. It may then |
|
| 514 |
#' be preferable to visualize the results for individuals and variables |
|
| 515 |
#' separately. |
|
| 516 |
#' @section PCA Biplots: |
|
| 517 |
#' \describe{
|
|
| 518 |
#' \item{`form` (row-metric-preserving)}{The form biplot favors the
|
|
| 519 |
#' representation of the individuals: the distance between the individuals |
|
| 520 |
#' approximates the Euclidean distance between rows. In the form biplot the |
|
| 521 |
#' length of a vector approximates the quality of the representation of the |
|
| 522 |
#' variable.} |
|
| 523 |
#' \item{`covariance` (column-metric-preserving)}{The covariance biplot favors
|
|
| 524 |
#' the representation of the variables: the length of a vector approximates |
|
| 525 |
#' the standard deviation of the variable and the cosine of the angle formed |
|
| 526 |
#' by two vectors approximates the correlation between the two variables. In |
|
| 527 |
#' the covariance biplot the distance between the individuals approximates the |
|
| 528 |
#' Mahalanobis distance between rows.} |
|
| 529 |
#' } |
|
| 530 |
#' @section CA Biplots: |
|
| 531 |
#' \describe{
|
|
| 532 |
#' \item{`symetric` (symetric biplot)}{Represents the row and column profiles
|
|
| 533 |
#' simultaneously in a common space: rows and columns are in standard |
|
| 534 |
#' coordinates. Note that the the inter-distance between any row and column |
|
| 535 |
#' items is not meaningful (i.e. the proximity between rows and columns cannot |
|
| 536 |
#' be directly interpreted).} |
|
| 537 |
#' \item{`rows` (asymetric biplot)}{Row principal biplot (row-metric-preserving)
|
|
| 538 |
#' with rows in principal coordinates and columns in standard coordinates.} |
|
| 539 |
#' \item{`columns` (asymetric biplot)}{Column principal biplot
|
|
| 540 |
#' (column-metric-preserving) with rows in standard coordinates and columns in |
|
| 541 |
#' principal coordinates.} |
|
| 542 |
#' \item{`contribution` (asymetric biplot)}{Contribution biplot with rows in
|
|
| 543 |
#' principal coordinates and columns in standard coordinates multiplied by the |
|
| 544 |
#' square roots of their masses.} |
|
| 545 |
#' } |
|
| 546 |
#' @return |
|
| 547 |
#' `biplot()` is called for its side-effects: it results in a graphic being |
|
| 548 |
#' displayed. Invisibly returns `x`. |
|
| 549 |
#' @example inst/examples/ex-biplot.R |
|
| 550 |
#' @references |
|
| 551 |
#' Aitchison, J. and Greenacre, M. J. (2002). Biplots of Compositional Data. |
|
| 552 |
#' *Journal of the Royal Statistical Society: Series C (Applied Statistics)*, |
|
| 553 |
#' 51(4): 375-92. \doi{10.1111/1467-9876.00275}.
|
|
| 554 |
#' |
|
| 555 |
#' Greenacre, M. J. (2010). *Biplots in Practice*. Bilbao: Fundación BBVA. |
|
| 556 |
#' @author N. Frerebeau |
|
| 557 |
#' @docType methods |
|
| 558 |
#' @family plot methods |
|
| 559 |
#' @name biplot |
|
| 560 |
#' @rdname biplot |
|
| 561 |
NULL |
|
| 562 | ||
| 563 |
## Coordinates ----------------------------------------------------------------- |
|
| 564 |
#' Visualize Individuals Factor Map |
|
| 565 |
#' |
|
| 566 |
#' Plots row/individual principal coordinates. |
|
| 567 |
#' @inheritParams viz_points |
|
| 568 |
#' @param ... Further [graphical parameters][graphics::par]. |
|
| 569 |
#' @return |
|
| 570 |
#' `viz_*()` is called for its side-effects: it results in a graphic |
|
| 571 |
#' being displayed. Invisibly returns `x`. |
|
| 572 |
#' @example inst/examples/ex-plot.R |
|
| 573 |
#' @author N. Frerebeau |
|
| 574 |
#' @docType methods |
|
| 575 |
#' @family plot methods |
|
| 576 |
#' @aliases viz_individuals-method |
|
| 577 |
setGeneric( |
|
| 578 |
name = "viz_individuals", |
|
| 579 | 12x |
def = function(x, ...) standardGeneric("viz_individuals")
|
| 580 |
) |
|
| 581 | ||
| 582 |
#' @rdname viz_individuals |
|
| 583 |
#' @aliases viz_rows-method |
|
| 584 |
setGeneric( |
|
| 585 |
name = "viz_rows", |
|
| 586 | 4x |
def = function(x, ...) standardGeneric("viz_rows")
|
| 587 |
) |
|
| 588 | ||
| 589 |
#' Visualize Variables Factor Map |
|
| 590 |
#' |
|
| 591 |
#' Plots column/variable principal coordinates. |
|
| 592 |
#' @inheritParams viz_points |
|
| 593 |
#' @param ... Further [graphical parameters][graphics::par]. |
|
| 594 |
#' @return |
|
| 595 |
#' `viz_*()` is called for its side-effects: it results in a graphic |
|
| 596 |
#' being displayed. Invisibly returns `x`. |
|
| 597 |
#' @example inst/examples/ex-plot.R |
|
| 598 |
#' @author N. Frerebeau |
|
| 599 |
#' @docType methods |
|
| 600 |
#' @family plot methods |
|
| 601 |
#' @aliases viz_variables-method |
|
| 602 |
setGeneric( |
|
| 603 |
name = "viz_variables", |
|
| 604 | 7x |
def = function(x, ...) standardGeneric("viz_variables")
|
| 605 |
) |
|
| 606 | ||
| 607 |
#' @rdname viz_variables |
|
| 608 |
#' @aliases viz_columns-method |
|
| 609 |
setGeneric( |
|
| 610 |
name = "viz_columns", |
|
| 611 | 5x |
def = function(x, ...) standardGeneric("viz_columns")
|
| 612 |
) |
|
| 613 | ||
| 614 |
## Eigenvalues ----------------------------------------------------------------- |
|
| 615 |
#' Scree Plot |
|
| 616 |
#' |
|
| 617 |
#' Plot eigenvalues (scree plot) or variances histogram. |
|
| 618 |
#' @param x A [`CA-class`], [`MCA-class`] or [`PCA-class`] object. |
|
| 619 |
#' @param eigenvalues A [`logical`] scalar: should the eigenvalues be plotted |
|
| 620 |
#' instead of variance/inertia? |
|
| 621 |
#' @param cumulative A [`logical`] scalar: should the cumulative percentages of |
|
| 622 |
#' variance be plotted? |
|
| 623 |
#' @param labels A [`logical`] scalar: should text labels be drawn on top of |
|
| 624 |
#' bars? |
|
| 625 |
#' @param limit An [`integer`] specifying the number of top elements to be |
|
| 626 |
#' displayed. |
|
| 627 |
#' @param col,border A [`character`] string specifying the bars infilling and |
|
| 628 |
#' border colors. |
|
| 629 |
#' @param col.cumulative A specification for the line color. |
|
| 630 |
#' @param lty.cumulative A specification for the line type. |
|
| 631 |
#' @param lwd.cumulative A specification for the line width. |
|
| 632 |
#' @param ... Extra parameters to be passed to [graphics::barplot()]. |
|
| 633 |
#' @return |
|
| 634 |
#' `screeplot()` is called for its side-effects: it results in a graphic |
|
| 635 |
#' being displayed. Invisibly returns `x`. |
|
| 636 |
#' @example inst/examples/ex-screeplot.R |
|
| 637 |
#' @author N. Frerebeau |
|
| 638 |
#' @docType methods |
|
| 639 |
#' @family plot methods |
|
| 640 |
#' @aliases screeplot-method |
|
| 641 |
#' @name screeplot |
|
| 642 |
#' @rdname screeplot |
|
| 643 |
NULL |
|
| 644 | ||
| 645 |
## Contributions --------------------------------------------------------------- |
|
| 646 |
#' Visualize Contributions and cos2 |
|
| 647 |
#' |
|
| 648 |
#' Plots contributions histogram and \eqn{cos^2}{cos2} scatterplot.
|
|
| 649 |
#' @param x A [`CA-class`], [`MCA-class`] or [`PCA-class`] object. |
|
| 650 |
#' @param margin A length-one [`numeric`] vector giving the subscript which the |
|
| 651 |
#' data will be returned: `1` indicates individuals/rows (the default), `2` |
|
| 652 |
#' indicates variables/columns. |
|
| 653 |
#' @param axes A [`numeric`] vector giving the dimensions to be plotted. |
|
| 654 |
#' @param active A [`logical`] scalar: should the active observations be |
|
| 655 |
#' plotted? |
|
| 656 |
#' @param sup A [`logical`] scalar: should the supplementary observations be |
|
| 657 |
#' plotted? |
|
| 658 |
#' @param sort A [`logical`] scalar: should the data be sorted? |
|
| 659 |
#' @param decreasing A [`logical`] scalar: should the sort order be decreasing? |
|
| 660 |
#' Only used if `sort` is `TRUE`. |
|
| 661 |
#' @param limit An [`integer`] specifying the number of top elements to be |
|
| 662 |
#' displayed. |
|
| 663 |
#' @param horiz A [`logical`] scalar: should the bars be drawn horizontally |
|
| 664 |
#' with the first at the bottom? |
|
| 665 |
#' @param col,border A [`character`] string specifying the bars infilling and |
|
| 666 |
#' border colors. |
|
| 667 |
#' @param ... Extra parameters to be passed to [graphics::barplot()]. |
|
| 668 |
#' @details |
|
| 669 |
#' The red dashed line indicates the expected average contribution (variables |
|
| 670 |
#' with a contribution larger than this cutoff can be considered as important |
|
| 671 |
#' in contributing to the component). |
|
| 672 |
#' @return |
|
| 673 |
#' `viz_contributions()` and `viz_cos2()` are called for their side-effects: |
|
| 674 |
#' they result in a graphic being displayed. Invisibly return `x`. |
|
| 675 |
#' @example inst/examples/ex-contributions.R |
|
| 676 |
#' @author N. Frerebeau |
|
| 677 |
#' @docType methods |
|
| 678 |
#' @family plot methods |
|
| 679 |
#' @aliases viz_contributions-method |
|
| 680 |
setGeneric( |
|
| 681 |
name = "viz_contributions", |
|
| 682 | 4x |
def = function(x, ...) standardGeneric("viz_contributions")
|
| 683 |
) |
|
| 684 | ||
| 685 |
#' @rdname viz_contributions |
|
| 686 |
#' @aliases viz_cos2-method |
|
| 687 |
setGeneric( |
|
| 688 |
name = "viz_cos2", |
|
| 689 | 2x |
def = function(x, ...) standardGeneric("viz_cos2")
|
| 690 |
) |
|
| 691 | ||
| 692 |
# Envelopes ==================================================================== |
|
| 693 |
#' Convex Hulls |
|
| 694 |
#' |
|
| 695 |
#' Plots convex hull of a set of observations. |
|
| 696 |
#' @param x,y A [`numeric`] vector. If `y` is missing, `x` must be an object |
|
| 697 |
#' from which to wrap observations (a [`CA-class`], [`MCA-class`] or |
|
| 698 |
#' [`PCA-class`] object). |
|
| 699 |
#' @param margin A length-one [`numeric`] vector giving the subscript which the |
|
| 700 |
#' data will be returned: `1` indicates individuals/rows (the default), `2` |
|
| 701 |
#' indicates variables/columns. |
|
| 702 |
#' @param axes A length-two [`numeric`] vector giving the dimensions |
|
| 703 |
#' for which to compute results. |
|
| 704 |
#' @param group A vector specifying the group an observation belongs to. |
|
| 705 |
#' @param principal A [`logical`] scalar: should principal coordinates be |
|
| 706 |
#' used? If `FALSE`, standard coordinates are returned. |
|
| 707 |
#' @param color The colors for borders (will be mapped to `group`). |
|
| 708 |
#' Ignored if set to `FALSE`. If `NULL`, the default color scheme will be used. |
|
| 709 |
#' @param fill The background colors (will be mapped to `group`). |
|
| 710 |
#' Ignored if set to `FALSE`. |
|
| 711 |
#' @param symbol A vector of symbols (will be mapped to `group`). |
|
| 712 |
#' Ignored if set to `FALSE`. |
|
| 713 |
#' @param ... Further [graphical parameters][graphics::par] to be passed to |
|
| 714 |
#' [graphics::polygon()]. |
|
| 715 |
#' @return |
|
| 716 |
#' `wrap_hull()` returns a [`data.frame`] of envelope `x` and `y` coordinates. |
|
| 717 |
#' An extra column named `group` is added specifying the group an observation |
|
| 718 |
#' belongs to. |
|
| 719 |
#' |
|
| 720 |
#' `viz_hull()`is called for its side-effects: it results in a graphic being |
|
| 721 |
#' displayed. Invisibly returns `x`. |
|
| 722 |
#' @example inst/examples/ex-hull.R |
|
| 723 |
#' @author N. Frerebeau |
|
| 724 |
#' @docType methods |
|
| 725 |
#' @family envelopes |
|
| 726 |
#' @aliases viz_hull-method |
|
| 727 |
setGeneric( |
|
| 728 |
name = "viz_hull", |
|
| 729 | 4x |
def = function(x, y, ...) standardGeneric("viz_hull")
|
| 730 |
) |
|
| 731 | ||
| 732 |
#' @rdname viz_hull |
|
| 733 |
#' @aliases wrap_hull-method |
|
| 734 |
setGeneric( |
|
| 735 |
name = "wrap_hull", |
|
| 736 | 8x |
def = function(x, y, ...) standardGeneric("wrap_hull")
|
| 737 |
) |
|
| 738 | ||
| 739 |
#' Ellipses |
|
| 740 |
#' |
|
| 741 |
#' Plots ellipses. |
|
| 742 |
#' @inheritParams viz_hull |
|
| 743 |
#' @param level A [`numeric`] vector specifying the confidence/tolerance level. |
|
| 744 |
#' @param type A [`character`] string specifying the ellipse to draw. |
|
| 745 |
#' It must be one of "`tolerance`" or "`confidence`"). |
|
| 746 |
#' Any unambiguous substring can be given. |
|
| 747 |
#' @return |
|
| 748 |
#' `viz_ellipses()`is called for its side-effects: it results in a graphic |
|
| 749 |
#' being displayed. Invisibly returns `x`. |
|
| 750 |
#' @example inst/examples/ex-ellipses.R |
|
| 751 |
#' @author N. Frerebeau |
|
| 752 |
#' @docType methods |
|
| 753 |
#' @family envelopes |
|
| 754 |
#' @aliases viz_ellipses-method |
|
| 755 |
setGeneric( |
|
| 756 |
name = "viz_ellipses", |
|
| 757 | 2x |
def = function(x, y, ...) standardGeneric("viz_ellipses")
|
| 758 |
) |
|
| 759 | ||
| 760 |
#' Confidence Ellipses |
|
| 761 |
#' |
|
| 762 |
#' Plots confidence ellipses. |
|
| 763 |
#' @inheritParams viz_hull |
|
| 764 |
#' @inheritParams viz_ellipses |
|
| 765 |
#' @return |
|
| 766 |
#' `wrap_confidence()` returns a [`data.frame`] of envelope `x` and `y` |
|
| 767 |
#' coordinates. An extra column named `group` is added specifying the group an |
|
| 768 |
#' observation belongs to. |
|
| 769 |
#' |
|
| 770 |
#' `viz_confidence()`is called for its side-effects: it results in a graphic |
|
| 771 |
#' being displayed. Invisibly returns `x`. |
|
| 772 |
#' @example inst/examples/ex-confidence.R |
|
| 773 |
#' @author N. Frerebeau |
|
| 774 |
#' @docType methods |
|
| 775 |
#' @family envelopes |
|
| 776 |
#' @aliases viz_confidence-method |
|
| 777 |
setGeneric( |
|
| 778 |
name = "viz_confidence", |
|
| 779 | ! |
def = function(x, y, ...) standardGeneric("viz_confidence")
|
| 780 |
) |
|
| 781 | ||
| 782 |
#' @rdname viz_confidence |
|
| 783 |
#' @aliases wrap_confidence-method |
|
| 784 |
setGeneric( |
|
| 785 |
name = "wrap_confidence", |
|
| 786 | 2x |
def = function(x, y, ...) standardGeneric("wrap_confidence")
|
| 787 |
) |
|
| 788 | ||
| 789 |
#' Tolerance Ellipses |
|
| 790 |
#' |
|
| 791 |
#' Plots tolerance ellipses. |
|
| 792 |
#' @inheritParams viz_hull |
|
| 793 |
#' @inheritParams viz_ellipses |
|
| 794 |
#' @return |
|
| 795 |
#' `wrap_tolerance()` returns a [`data.frame`] of envelope `x` and `y` |
|
| 796 |
#' coordinates. An extra column named `group` is added specifying the group an |
|
| 797 |
#' observation belongs to. |
|
| 798 |
#' |
|
| 799 |
#' `viz_tolerance()`is called for its side-effects: it results in a graphic |
|
| 800 |
#' being displayed. Invisibly returns `x`. |
|
| 801 |
#' @example inst/examples/ex-tolerance.R |
|
| 802 |
#' @author N. Frerebeau |
|
| 803 |
#' @docType methods |
|
| 804 |
#' @family envelopes |
|
| 805 |
#' @aliases viz_tolerance-method |
|
| 806 |
setGeneric( |
|
| 807 |
name = "viz_tolerance", |
|
| 808 | ! |
def = function(x, y, ...) standardGeneric("viz_tolerance")
|
| 809 |
) |
|
| 810 | ||
| 811 |
#' @rdname viz_tolerance |
|
| 812 |
#' @aliases wrap_tolerance-method |
|
| 813 |
setGeneric( |
|
| 814 |
name = "wrap_tolerance", |
|
| 815 | 2x |
def = function(x, y, ...) standardGeneric("wrap_tolerance")
|
| 816 |
) |
|
| 817 | ||
| 818 |
# Summarize ==================================================================== |
|
| 819 |
#' Object Summaries |
|
| 820 |
#' |
|
| 821 |
#' Provides a summary of the results of a multivariate data analysis. |
|
| 822 |
#' @param object A [`CA-class`], [`MCA-class`] or [`PCA-class`] object. |
|
| 823 |
#' @param axes A length-two [`numeric`] vector giving the dimensions to be |
|
| 824 |
#' summarized. |
|
| 825 |
#' @param margin A length-one [`numeric`] vector giving the subscript which the |
|
| 826 |
#' data will be summarized: `1` indicates individuals/rows (the default), `2` |
|
| 827 |
#' indicates variables/columns. |
|
| 828 |
#' @param rank An [`integer`] value specifying the maximal number of components |
|
| 829 |
#' to be kept in the results. Deprecated, use `axes` instead. |
|
| 830 |
#' @param active A [`logical`] scalar: should the active observations be |
|
| 831 |
#' summarized? |
|
| 832 |
#' @param sup A [`logical`] scalar: should the supplementary observations be |
|
| 833 |
#' summarized? |
|
| 834 |
#' @param x A [`MultivariateSummary-class`] object. |
|
| 835 |
#' @param row.names A [`character`] vector giving the row names for the data |
|
| 836 |
#' frame, or `NULL`. |
|
| 837 |
#' @param optional A [`logical`] scalar: should the names of the variables in |
|
| 838 |
#' the data frame be checked? If `FALSE` then the names of the variables in the |
|
| 839 |
#' data frame are checked to ensure that they are syntactically valid variable |
|
| 840 |
#' names and are not duplicated. |
|
| 841 |
#' @param ... Currently not used. |
|
| 842 |
#' @example inst/examples/ex-summary.R |
|
| 843 |
#' @author N. Frerebeau |
|
| 844 |
#' @docType methods |
|
| 845 |
#' @family summary |
|
| 846 |
#' @name summary |
|
| 847 |
#' @rdname summary |
|
| 848 |
NULL |
|
| 849 | ||
| 850 |
#' Object Description |
|
| 851 |
#' |
|
| 852 |
#' @param x A [`CA-class`], [`MCA-class`] or [`PCA-class`] object. |
|
| 853 |
#' @param ... Further parameters to be passed to [cat()]. |
|
| 854 |
#' @return |
|
| 855 |
#' `describe()` is called for its side-effects. Invisibly returns `x`. |
|
| 856 |
#' @example inst/examples/ex-summary.R |
|
| 857 |
#' @author N. Frerebeau |
|
| 858 |
#' @family summary |
|
| 859 |
#' @docType methods |
|
| 860 |
#' @rdname describe |
|
| 861 |
#' @name describe |
|
| 862 |
NULL |
|
| 863 | ||
| 864 |
#' Tidy Coordinates |
|
| 865 |
#' |
|
| 866 |
#' @param x A [`CA-class`], [`MCA-class`] or [`PCA-class`] object. |
|
| 867 |
#' @param margin A length-one [`numeric`] vector giving the subscript |
|
| 868 |
#' which the data will be returned: `1` indicates individuals/rows (the |
|
| 869 |
#' default), `2` indicates variables/columns. |
|
| 870 |
#' @param axes A length-two [`numeric`] vector giving the dimensions |
|
| 871 |
#' for which to compute results. |
|
| 872 |
#' @param principal A [`logical`] scalar: should principal coordinates be |
|
| 873 |
#' returned? If `FALSE`, standard coordinates are returned. |
|
| 874 |
#' @param ... Currently not used. |
|
| 875 |
#' @return |
|
| 876 |
#' `tidy()` returns a long [`data.frame`] with the following columns: |
|
| 877 |
#' \describe{
|
|
| 878 |
#' \item{`label`}{Row/column names of the original data.}
|
|
| 879 |
#' \item{`component`}{Component.}
|
|
| 880 |
#' \item{`supplementary`}{Whether an observation is active or
|
|
| 881 |
#' supplementary.} |
|
| 882 |
#' \item{`coordinate`}{Coordinates.}
|
|
| 883 |
#' \item{`contribution`}{Contributions to the definition of the components.}
|
|
| 884 |
#' \item{`cos2`}{\eqn{cos^2}{cos2}.}
|
|
| 885 |
#' } |
|
| 886 |
#' |
|
| 887 |
#' `augment()` returns a wide [`data.frame`] of the row/column coordinates |
|
| 888 |
#' along `axes` and the following columns: |
|
| 889 |
#' \describe{
|
|
| 890 |
#' \item{`label`}{Row/column names of the original data.}
|
|
| 891 |
#' \item{`supplementary`}{Whether an observation is active or
|
|
| 892 |
#' supplementary.} |
|
| 893 |
#' \item{`mass`}{Weight/mass of each observation.}
|
|
| 894 |
#' \item{`sum`}{Sum of squared coordinates along `axes`.}
|
|
| 895 |
#' \item{`contribution`}{Joint contributions to the definition of `axes`.}
|
|
| 896 |
#' \item{`cos2`}{Joint \eqn{cos^2}{cos2} along `axes`.}
|
|
| 897 |
#' } |
|
| 898 |
#' @example inst/examples/ex-coordinates.R |
|
| 899 |
#' @author N. Frerebeau |
|
| 900 |
#' @docType methods |
|
| 901 |
#' @family summary |
|
| 902 |
#' @aliases tidy-method |
|
| 903 |
setGeneric( |
|
| 904 |
name = "tidy", |
|
| 905 |
def = function(x, ...) standardGeneric("tidy"),
|
|
| 906 |
valueClass = "data.frame" |
|
| 907 |
) |
|
| 908 | ||
| 909 |
#' @rdname tidy |
|
| 910 |
#' @aliases augment-method |
|
| 911 |
setGeneric( |
|
| 912 |
name = "augment", |
|
| 913 |
def = function(x, ...) standardGeneric("augment"),
|
|
| 914 |
valueClass = "data.frame" |
|
| 915 |
) |
|
| 916 | ||
| 917 |
# Tools ======================================================================== |
|
| 918 |
#' Complete Disjunctive Table |
|
| 919 |
#' |
|
| 920 |
#' Computes the complete disjunctive table of a factor table. |
|
| 921 |
#' @param object A [`data.frame`]. |
|
| 922 |
#' @param exclude A `vector` of values to be excluded when forming the set of |
|
| 923 |
#' levels (see [factor()]). If `NULL` (the default), will make `NA` an extra |
|
| 924 |
#' level. |
|
| 925 |
#' @param abbrev A [`logical`] scalar: should the column names be abbreviated? |
|
| 926 |
#' If `FALSE`, these are of the form 'factor_level' but if `abbrev = TRUE` they |
|
| 927 |
#' are just 'level' which will suffice if the factors have distinct levels. |
|
| 928 |
#' @param ... Currently not used. |
|
| 929 |
#' @return A [`data.frame`]. |
|
| 930 |
#' @example inst/examples/ex-cdt.R |
|
| 931 |
#' @author N. Frerebeau |
|
| 932 |
#' @docType methods |
|
| 933 |
#' @family tools |
|
| 934 |
#' @aliases cdt-method |
|
| 935 |
setGeneric( |
|
| 936 |
name = "cdt", |
|
| 937 | 14x |
def = function(object, ...) standardGeneric("cdt")
|
| 938 |
) |
|
| 939 | ||
| 940 |
#' Burt Table |
|
| 941 |
#' |
|
| 942 |
#' Computes the burt table of a factor table. |
|
| 943 |
#' @param object A [`data.frame`]. |
|
| 944 |
#' @inheritParams cdt |
|
| 945 |
#' @param ... Currently not used. |
|
| 946 |
#' @return A symetric [`matrix`]. |
|
| 947 |
#' @example inst/examples/ex-cdt.R |
|
| 948 |
#' @author N. Frerebeau |
|
| 949 |
#' @docType methods |
|
| 950 |
#' @family tools |
|
| 951 |
#' @aliases burt-method |
|
| 952 |
setGeneric( |
|
| 953 |
name = "burt", |
|
| 954 | 1x |
def = function(object, ...) standardGeneric("burt")
|
| 955 |
) |
| 1 |
# PLOT COORDINATES |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
# Internal environment ========================================================= |
|
| 6 |
the <- new.env(parent = emptyenv()) |
|
| 7 |
the$margin <- 1 # Updated by prepare_plot() |
|
| 8 |
the$axes <- c(1, 2) # Updated by prepare_plot() |
|
| 9 |
the$principal <- TRUE # Updated by prepare_plot() |
|
| 10 | ||
| 11 |
get_margin <- function(...) {
|
|
| 12 | 3x |
get("margin", envir = the)
|
| 13 |
} |
|
| 14 | ||
| 15 |
get_axes <- function(...) {
|
|
| 16 | 6x |
get("axes", envir = the)
|
| 17 |
} |
|
| 18 | ||
| 19 |
get_principal <- function(...) {
|
|
| 20 | 3x |
get("principal", envir = the)
|
| 21 |
} |
|
| 22 | ||
| 23 |
# Rows ========================================================================= |
|
| 24 |
#' @export |
|
| 25 |
#' @rdname viz_individuals |
|
| 26 |
#' @aliases viz_rows,MultivariateAnalysis-method |
|
| 27 |
setMethod( |
|
| 28 |
f = "viz_rows", |
|
| 29 |
signature = c(x = "MultivariateAnalysis"), |
|
| 30 |
definition = function(x, ..., axes = c(1, 2), active = TRUE, sup = TRUE, |
|
| 31 |
labels = FALSE, extra_quali = NULL, extra_quanti = NULL, |
|
| 32 |
ellipse = NULL, hull = NULL, |
|
| 33 |
color = NULL, fill = FALSE, symbol = FALSE, size = c(1, 6), |
|
| 34 |
xlim = NULL, ylim = NULL, main = NULL, sub = NULL, |
|
| 35 |
panel.first = NULL, panel.last = NULL, |
|
| 36 |
legend = list(x = "topleft")) {
|
|
| 37 | 4x |
viz_points(x, margin = 1, axes = axes, ..., |
| 38 | 4x |
active = active, sup = sup, labels = labels, |
| 39 | 4x |
extra_quali = extra_quali, extra_quanti = extra_quanti, |
| 40 | 4x |
color = color, fill = fill, symbol = symbol, size = size, |
| 41 | 4x |
xlim = xlim, ylim = ylim, main = main, sub = sub, |
| 42 | 4x |
panel.first = panel.first, panel.last = panel.last, |
| 43 | 4x |
ellipse = ellipse, hull = hull, |
| 44 | 4x |
legend = legend) |
| 45 | 4x |
invisible(x) |
| 46 |
} |
|
| 47 |
) |
|
| 48 | ||
| 49 |
#' @export |
|
| 50 |
#' @rdname viz_individuals |
|
| 51 |
#' @aliases viz_rows,BootstrapCA-method |
|
| 52 |
setMethod( |
|
| 53 |
f = "viz_rows", |
|
| 54 |
signature = c(x = "BootstrapCA"), |
|
| 55 |
definition = function(x, ..., axes = c(1, 2), color = FALSE, fill = FALSE, |
|
| 56 |
symbol = FALSE, legend = NULL) {
|
|
| 57 | ! |
viz_points(x, margin = 1, axes = axes, ..., active = TRUE, sup = TRUE, |
| 58 | ! |
labels = FALSE, extra_quali = NULL, |
| 59 | ! |
color = color, fill = fill, symbol = symbol, legend = legend) |
| 60 | ! |
invisible(x) |
| 61 |
} |
|
| 62 |
) |
|
| 63 | ||
| 64 |
# Individuals ================================================================== |
|
| 65 |
#' @export |
|
| 66 |
#' @rdname viz_individuals |
|
| 67 |
#' @aliases viz_individuals,PCA-method |
|
| 68 |
setMethod( |
|
| 69 |
f = "viz_individuals", |
|
| 70 |
signature = c(x = "PCA"), |
|
| 71 |
definition = function(x, ..., axes = c(1, 2), active = TRUE, sup = TRUE, |
|
| 72 |
labels = FALSE, extra_quali = NULL, extra_quanti = NULL, |
|
| 73 |
ellipse = NULL, hull = NULL, |
|
| 74 |
color = NULL, fill = FALSE, symbol = FALSE, size = c(1, 6), |
|
| 75 |
xlim = NULL, ylim = NULL, main = NULL, sub = NULL, |
|
| 76 |
panel.first = NULL, panel.last = NULL, |
|
| 77 |
legend = list(x = "topleft")) {
|
|
| 78 | 12x |
viz_points(x, margin = 1, axes = axes, ..., |
| 79 | 12x |
active = active, sup = sup, labels = labels, |
| 80 | 12x |
extra_quali = extra_quali, extra_quanti = extra_quanti, |
| 81 | 12x |
color = color, fill = fill, symbol = symbol, size = size, |
| 82 | 12x |
xlim = xlim, ylim = ylim, main = main, sub = sub, |
| 83 | 12x |
panel.first = panel.first, panel.last = panel.last, |
| 84 | 12x |
ellipse = ellipse, hull = hull, |
| 85 | 12x |
legend = legend) |
| 86 | 12x |
invisible(x) |
| 87 |
} |
|
| 88 |
) |
|
| 89 | ||
| 90 |
# Columns ===================================================================== |
|
| 91 |
#' @export |
|
| 92 |
#' @rdname viz_variables |
|
| 93 |
#' @aliases viz_columns,MultivariateAnalysis-method |
|
| 94 |
setMethod( |
|
| 95 |
f = "viz_columns", |
|
| 96 |
signature = c(x = "MultivariateAnalysis"), |
|
| 97 |
definition = function(x, ..., axes = c(1, 2), active = TRUE, sup = TRUE, |
|
| 98 |
labels = FALSE, extra_quali = NULL, extra_quanti = NULL, |
|
| 99 |
color = NULL, fill = FALSE, symbol = FALSE, size = c(1, 6), |
|
| 100 |
xlim = NULL, ylim = NULL, main = NULL, sub = NULL, |
|
| 101 |
panel.first = NULL, panel.last = NULL, |
|
| 102 |
legend = list(x = "topleft")) {
|
|
| 103 | 5x |
viz_points(x, margin = 2, axes = axes, ..., |
| 104 | 5x |
active = active, sup = sup, labels = labels, |
| 105 | 5x |
extra_quali = extra_quali, extra_quanti = extra_quanti, |
| 106 | 5x |
color = color, fill = fill, symbol = symbol, size = size, |
| 107 | 5x |
xlim = xlim, ylim = ylim, main = main, sub = sub, |
| 108 | 5x |
panel.first = panel.first, panel.last = panel.last, |
| 109 | 5x |
legend = legend) |
| 110 | 5x |
invisible(x) |
| 111 |
} |
|
| 112 |
) |
|
| 113 | ||
| 114 |
#' @export |
|
| 115 |
#' @rdname viz_variables |
|
| 116 |
#' @aliases viz_columns,MultivariateBootstrap-method |
|
| 117 |
setMethod( |
|
| 118 |
f = "viz_columns", |
|
| 119 |
signature = c(x = "MultivariateBootstrap"), |
|
| 120 |
definition = function(x, ..., axes = c(1, 2), color = FALSE, fill = FALSE, |
|
| 121 |
symbol = FALSE, legend = NULL) {
|
|
| 122 | ! |
viz_points(x, ..., margin = 2, axes = axes, active = TRUE, sup = TRUE, |
| 123 | ! |
labels = FALSE, extra_quali = NULL, |
| 124 | ! |
color = color, fill = fill, symbol = symbol, legend = legend) |
| 125 | ! |
invisible(x) |
| 126 |
} |
|
| 127 |
) |
|
| 128 | ||
| 129 |
# Variables ==================================================================== |
|
| 130 |
#' @export |
|
| 131 |
#' @rdname viz_variables |
|
| 132 |
#' @aliases viz_variables,PCA-method |
|
| 133 |
setMethod( |
|
| 134 |
f = "viz_variables", |
|
| 135 |
signature = c(x = "PCA"), |
|
| 136 |
definition = function(x, ..., axes = c(1, 2), active = TRUE, sup = TRUE, |
|
| 137 |
labels = list(filter = "contribution", n = 10), |
|
| 138 |
extra_quali = NULL, extra_quanti = NULL, |
|
| 139 |
color = NULL, symbol = NULL, size = 1, |
|
| 140 |
xlim = NULL, ylim = NULL, main = NULL, sub = NULL, |
|
| 141 |
panel.first = NULL, panel.last = NULL, |
|
| 142 |
legend = list(x = "topleft")) {
|
|
| 143 |
## Prepare data |
|
| 144 | 7x |
coord <- prepare_plot(x, margin = 2, axes = axes, ..., |
| 145 | 7x |
active = active, sup = sup, |
| 146 | 7x |
extra_quali = extra_quali, extra_quanti = extra_quanti, |
| 147 | 7x |
color = color, line_type = symbol, line_width = size) |
| 148 | ||
| 149 |
## Save and restore graphical parameters |
|
| 150 |
## pty: square plotting region, independent of device size |
|
| 151 | 7x |
old_par <- graphics::par(pty = "s", no.readonly = TRUE) |
| 152 | 7x |
on.exit(graphics::par(old_par), add = TRUE) |
| 153 | ||
| 154 |
## Open new window |
|
| 155 | 7x |
grDevices::dev.hold() |
| 156 | 7x |
on.exit(grDevices::dev.flush(), add = TRUE) |
| 157 | 7x |
graphics::plot.new() |
| 158 | ||
| 159 |
## Set plotting coordinates |
|
| 160 | 7x |
xlim <- xlim %||% range(coord$x, na.rm = TRUE, finite = TRUE) |
| 161 | 7x |
if (is_scaled(x)) xlim <- c(-1, 1) |
| 162 | 7x |
ylim <- ylim %||% range(coord$y, na.rm = TRUE, finite = TRUE) |
| 163 | 7x |
if (is_scaled(x)) ylim <- c(-1, 1) |
| 164 | 7x |
graphics::plot.window(xlim = xlim, ylim = ylim, asp = 1) |
| 165 | ||
| 166 |
## Evaluate pre-plot expressions |
|
| 167 | 7x |
panel.first |
| 168 | ||
| 169 |
## Plot |
|
| 170 | 7x |
graphics::abline(h = 0, lty = "dashed", lwd = 1, col = graphics::par("fg"))
|
| 171 | 7x |
graphics::abline(v = 0, lty = "dashed", lwd = 1, col = graphics::par("fg"))
|
| 172 | ||
| 173 |
## Scaled variables? |
|
| 174 | 7x |
if (is_scaled(x)) {
|
| 175 | 7x |
circle(x = 0, y = 0, radius = 1, lwd = 1, |
| 176 | 7x |
border = graphics::par("fg"), n = 100)
|
| 177 |
} |
|
| 178 | ||
| 179 | 7x |
graphics::arrows( |
| 180 | 7x |
x0 = 0, y0 = 0, x1 = coord$x, y1 = coord$y, length = 0.15, angle = 30, |
| 181 | 7x |
col = coord$col, |
| 182 | 7x |
lty = coord$lty, |
| 183 | 7x |
lwd = coord$lwd |
| 184 |
) |
|
| 185 | ||
| 186 |
## Labels |
|
| 187 | ! |
if (isTRUE(labels)) labels <- list() |
| 188 | 7x |
if (is.list(labels)) {
|
| 189 | ! |
viz_labels(coord, filter = labels$filter, n = labels$n) |
| 190 |
} |
|
| 191 | ||
| 192 |
## Evaluate post-plot and pre-axis expressions |
|
| 193 | 7x |
panel.last |
| 194 | ||
| 195 |
## Construct axis (axes) |
|
| 196 | 7x |
if (TRUE) {
|
| 197 | 7x |
graphics::axis(side = 1, las = 1) |
| 198 | 7x |
graphics::axis(side = 2, las = 1) |
| 199 |
} |
|
| 200 | ||
| 201 |
## Plot frame (frame.plot) |
|
| 202 | 7x |
if (TRUE) {
|
| 203 | 7x |
graphics::box() |
| 204 |
} |
|
| 205 | ||
| 206 |
## Add annotation (ann) |
|
| 207 | 7x |
if (TRUE) {
|
| 208 | 7x |
graphics::title( |
| 209 | 7x |
main = main, sub = sub, |
| 210 | 7x |
xlab = print_variance(x, axes[[1]]), |
| 211 | 7x |
ylab = print_variance(x, axes[[2]]) |
| 212 |
) |
|
| 213 |
} |
|
| 214 | ||
| 215 |
## Legend |
|
| 216 | 7x |
viz_legend(coord, legend, points = FALSE, lines = TRUE) |
| 217 | ||
| 218 | 7x |
invisible(x) |
| 219 |
} |
|
| 220 |
) |
|
| 221 | ||
| 222 |
#' @export |
|
| 223 |
#' @rdname viz_variables |
|
| 224 |
#' @aliases viz_variables,CA-method |
|
| 225 |
setMethod( |
|
| 226 |
f = "viz_variables", |
|
| 227 |
signature = c(x = "CA"), |
|
| 228 |
definition = function(x, ..., axes = c(1, 2), active = TRUE, sup = TRUE, |
|
| 229 |
labels = FALSE, extra_quali = NULL, extra_quanti = NULL, |
|
| 230 |
color = NULL, fill = FALSE, symbol = FALSE, size = c(1, 6), |
|
| 231 |
xlim = NULL, ylim = NULL, main = NULL, sub = NULL, |
|
| 232 |
panel.first = NULL, panel.last = NULL, |
|
| 233 |
legend = list(x = "topleft")) {
|
|
| 234 | ! |
viz_points(x, margin = 2, axes = axes, ..., |
| 235 | ! |
active = active, sup = sup, labels = labels, |
| 236 | ! |
extra_quali = extra_quali, extra_quanti = extra_quanti, |
| 237 | ! |
color = color, fill = fill, symbol = symbol, size = size, |
| 238 | ! |
xlim = xlim, ylim = ylim, main = main, sub = sub, |
| 239 | ! |
panel.first = panel.first, panel.last = panel.last, |
| 240 | ! |
legend = legend) |
| 241 |
} |
|
| 242 |
) |
|
| 243 | ||
| 244 |
#' @export |
|
| 245 |
#' @rdname viz_variables |
|
| 246 |
#' @aliases viz_variables,BootstrapPCA-method |
|
| 247 |
setMethod( |
|
| 248 |
f = "viz_variables", |
|
| 249 |
signature = c(x = "BootstrapPCA"), |
|
| 250 |
definition = function(x, ..., axes = c(1, 2), color = FALSE, fill = FALSE, |
|
| 251 |
symbol = FALSE, legend = NULL) {
|
|
| 252 | ! |
viz_points(x, ..., margin = 2, axes = axes, active = TRUE, sup = TRUE, |
| 253 | ! |
labels = FALSE, extra_quali = NULL, |
| 254 | ! |
color = color, fill = fill, symbol = symbol, legend = legend) |
| 255 | ! |
invisible(x) |
| 256 |
} |
|
| 257 |
) |
|
| 258 | ||
| 259 |
# Helpers ====================================================================== |
|
| 260 |
#' Build a Factor Map |
|
| 261 |
#' |
|
| 262 |
#' @param x A [`CA-class`], [`MCA-class`] or [`PCA-class`] object. |
|
| 263 |
#' @param labels A [`logical`] scalar: should labels be drawn? Labeling a large |
|
| 264 |
#' number of points can be computationally expensive and make the graph |
|
| 265 |
#' difficult to read. A selection of points to label can be provided using a |
|
| 266 |
#' `list` of two named elements, `filter` (a string specifying how to filter |
|
| 267 |
#' the labels to be drawn) and `n` (an integer specifying the number of labels |
|
| 268 |
#' to be drawn). See examples below. |
|
| 269 |
#' @param xlim A length-two [`numeric`] vector giving the x limits of the plot. |
|
| 270 |
#' The default value, `NULL`, indicates that the range of the |
|
| 271 |
#' [finite][is.finite()] values to be plotted should be used. |
|
| 272 |
#' @param ylim A length-two [`numeric`] vector giving the y limits of the plot. |
|
| 273 |
#' The default value, `NULL`, indicates that the range of the |
|
| 274 |
#' [finite][is.finite()] values to be plotted should be used. |
|
| 275 |
#' @param main A [`character`] string giving a main title for the plot. |
|
| 276 |
#' @param sub A [`character`] string giving a subtitle for the plot. |
|
| 277 |
#' @param xlab,ylab A [`character`] vector giving the x and y axis labels. |
|
| 278 |
#' @param ann A [`logical`] scalar: should the default annotation (title and x |
|
| 279 |
#' and y axis labels) appear on the plot? |
|
| 280 |
#' @param frame.plot A [`logical`] scalar: should a box be drawn around the |
|
| 281 |
#' plot? |
|
| 282 |
#' @param panel.first An `expression` to be evaluated after the plot axes are |
|
| 283 |
#' set up but before any plotting takes place. This can be useful for drawing |
|
| 284 |
#' background grids. |
|
| 285 |
#' @param panel.last An `expression` to be evaluated after plotting has taken |
|
| 286 |
#' place but before the axes, title and box are added. |
|
| 287 |
#' @param ellipse A [`list`] of additional arguments to be passed to |
|
| 288 |
#' [viz_ellipses()]; names of the list are used as argument names. |
|
| 289 |
#' If `NULL`, no ellipse are displayed. |
|
| 290 |
#' @param hull A [`logical`] scalar: should convex hulls be displayed? |
|
| 291 |
#' @param legend A [`list`] of additional arguments to be passed to |
|
| 292 |
#' [graphics::legend()]; names of the list are used as argument names. |
|
| 293 |
#' If `NULL`, no legend is displayed. |
|
| 294 |
#' @param ... Currently not used. |
|
| 295 |
#' @inheritParams prepare_plot |
|
| 296 |
#' @author N. Frerebeau |
|
| 297 |
#' @keywords internal |
|
| 298 |
viz_points <- function(x, margin, axes, ..., |
|
| 299 |
active = TRUE, sup = TRUE, |
|
| 300 |
labels = list(filter = "contribution", n = 10), |
|
| 301 |
extra_quali = NULL, extra_quanti = NULL, |
|
| 302 |
color = NULL, fill = FALSE, |
|
| 303 |
symbol = NULL, size = c(1, 6), |
|
| 304 |
xlim = NULL, ylim = NULL, |
|
| 305 |
main = NULL, sub = NULL, xlab = NULL, ylab = NULL, |
|
| 306 |
ann = graphics::par("ann"), frame.plot = TRUE,
|
|
| 307 |
panel.first = NULL, panel.last = NULL, |
|
| 308 |
ellipse = NULL, hull = FALSE, |
|
| 309 |
legend = list(x = "topleft")) {
|
|
| 310 |
## Prepare data |
|
| 311 | 21x |
coord <- prepare_plot(x, margin = margin, axes = axes, |
| 312 | 21x |
active = active, sup = sup, |
| 313 | 21x |
extra_quali = extra_quali, |
| 314 | 21x |
extra_quanti = extra_quanti, |
| 315 | 21x |
color = color, fill = fill, |
| 316 | 21x |
symbol = symbol, size = size, ...) |
| 317 | ||
| 318 |
## Save and restore graphical parameters |
|
| 319 |
## pty: square plotting region, independent of device size |
|
| 320 | 21x |
old_par <- graphics::par(pty = "s", no.readonly = TRUE) |
| 321 | 21x |
on.exit(graphics::par(old_par), add = TRUE) |
| 322 | ||
| 323 |
## Open new window |
|
| 324 | 21x |
grDevices::dev.hold() |
| 325 | 21x |
on.exit(grDevices::dev.flush(), add = TRUE) |
| 326 | 21x |
graphics::plot.new() |
| 327 | ||
| 328 |
## Set plotting coordinates |
|
| 329 | 21x |
xlim <- xlim %||% range(coord$x, na.rm = TRUE, finite = TRUE) |
| 330 | 21x |
ylim <- ylim %||% range(coord$y, na.rm = TRUE, finite = TRUE) |
| 331 | 21x |
graphics::plot.window(xlim = xlim, ylim = ylim, asp = 1) |
| 332 | ||
| 333 |
## Evaluate pre-plot expressions |
|
| 334 | 21x |
panel.first |
| 335 | ||
| 336 |
## Plot |
|
| 337 | 21x |
graphics::abline(h = 0, lty = "dashed", lwd = 1, col = graphics::par("fg"))
|
| 338 | 21x |
graphics::abline(v = 0, lty = "dashed", lwd = 1, col = graphics::par("fg"))
|
| 339 | 21x |
graphics::points( |
| 340 | 21x |
x = coord$x, |
| 341 | 21x |
y = coord$y, |
| 342 | 21x |
col = coord$col, |
| 343 | 21x |
bg = coord$bg, |
| 344 | 21x |
pch = coord$pch, |
| 345 | 21x |
cex = coord$cex |
| 346 |
) |
|
| 347 | ||
| 348 |
## Labels |
|
| 349 | ! |
if (isTRUE(labels)) labels <- list() |
| 350 | 21x |
if (is.list(labels)) {
|
| 351 | ! |
viz_labels(coord, filter = labels$filter, n = labels$n) |
| 352 |
} |
|
| 353 | ||
| 354 |
## Evaluate post-plot and pre-axis expressions |
|
| 355 | 21x |
panel.last |
| 356 | ||
| 357 |
## Construct axis (axes) |
|
| 358 | 21x |
if (TRUE) {
|
| 359 | 21x |
graphics::axis(side = 1, las = 1) |
| 360 | 21x |
graphics::axis(side = 2, las = 1) |
| 361 |
} |
|
| 362 | ||
| 363 |
## Plot frame |
|
| 364 | 21x |
if (frame.plot) {
|
| 365 | 21x |
graphics::box() |
| 366 |
} |
|
| 367 | ||
| 368 |
## Add annotation |
|
| 369 | 21x |
if (ann) {
|
| 370 | 21x |
graphics::title( |
| 371 | 21x |
main = main, sub = sub, |
| 372 | 21x |
xlab = xlab %||% print_variance(x, axes[[1]]), |
| 373 | 21x |
ylab = ylab %||% print_variance(x, axes[[2]]) |
| 374 |
) |
|
| 375 |
} |
|
| 376 | ||
| 377 |
## Add ellipse |
|
| 378 | 21x |
if (is.list(ellipse) && length(ellipse) > 0) {
|
| 379 | ! |
args_ell <- list(x = x, group = extra_quali, |
| 380 | ! |
color = color, fill = FALSE, symbol = FALSE) |
| 381 | ! |
ellipse <- modifyList(args_ell, val = ellipse) |
| 382 | ! |
do.call(viz_ellipses, ellipse) |
| 383 |
} |
|
| 384 | ||
| 385 |
## Add convex hull |
|
| 386 | 21x |
if (isTRUE(hull)) {
|
| 387 | 3x |
args_hull <- list(x = x, group = extra_quali, |
| 388 | 3x |
color = color, fill = FALSE, symbol = FALSE) |
| 389 | 3x |
do.call(viz_hull, args_hull) |
| 390 |
} |
|
| 391 | ||
| 392 |
## Legend |
|
| 393 | 21x |
viz_legend(coord, legend, points = TRUE, lines = FALSE) |
| 394 | ||
| 395 | 21x |
invisible(coord) |
| 396 |
} |
|
| 397 | ||
| 398 |
#' Add Legend |
|
| 399 |
#' |
|
| 400 |
#' @inheritParams prepare_legend |
|
| 401 |
#' @author N. Frerebeau |
|
| 402 |
#' @keywords internal |
|
| 403 |
viz_legend <- function(x, args, points = TRUE, lines = TRUE) {
|
|
| 404 | 43x |
leg <- prepare_legend(x, args, points = points, lines = lines) |
| 405 | 4x |
if (is.null(leg)) return(invisible(NULL)) |
| 406 | 39x |
do.call(graphics::legend, args = leg) |
| 407 |
} |
|
| 408 | ||
| 409 |
#' Non-Overlapping Text Labels |
|
| 410 |
#' |
|
| 411 |
#' @param x A [`data.frame`] (typically returned by [prepare_plot()]). |
|
| 412 |
#' @param filter A [`character`] string specifying the variable used to filter |
|
| 413 |
#' observations. If `NULL`, all labels are drawn. |
|
| 414 |
#' @param n An [`integer`] specifying the number of labels to draw. |
|
| 415 |
#' Only the labels of the top \eqn{n} observations according to `filter` will
|
|
| 416 |
#' be drawn. If `NULL`, all labels are drawn. |
|
| 417 |
#' @param type A [`character`] string specifying the shape of the field. |
|
| 418 |
#' It must be one of "`text`", "`shadow`" or "`box`". Any unambiguous substring |
|
| 419 |
#' can be given. |
|
| 420 |
#' @param ... Currently not used. |
|
| 421 |
#' @details |
|
| 422 |
#' Only labels in the plotting region (given by `par("usr")`) will be drawn.
|
|
| 423 |
#' @author N. Frerebeau |
|
| 424 |
#' @keywords internal |
|
| 425 |
viz_labels <- function(x, filter = "contribution", n = 10, |
|
| 426 |
type = "shadow", ...) {
|
|
| 427 |
## Select |
|
| 428 | ! |
if (!is.null(filter) && !is.null(n) && n > 0) {
|
| 429 | ! |
top <- min(nrow(x), n) |
| 430 | ! |
how <- x[[filter]] |
| 431 | ! |
k <- order(how, decreasing = TRUE)[seq_len(top)] # Get order |
| 432 | ! |
x <- x[k, , drop = FALSE] # Subset |
| 433 |
} |
|
| 434 | ||
| 435 |
## Filter |
|
| 436 | ! |
xlim <- graphics::par("usr")[c(1, 2)]
|
| 437 | ! |
ylim <- graphics::par("usr")[c(3, 4)]
|
| 438 | ! |
x_filter <- x$x >= min(xlim) & x$x <= max(xlim) |
| 439 | ! |
y_filter <- x$y >= min(ylim) & x$y <= max(ylim) |
| 440 | ! |
xy_filter <- which(x_filter & y_filter) |
| 441 | ! |
x <- x[xy_filter, , drop = FALSE] |
| 442 | ||
| 443 | ! |
label( |
| 444 | ! |
x = x$x, |
| 445 | ! |
y = x$y, |
| 446 | ! |
labels = x$label, |
| 447 | ! |
type = type, |
| 448 | ! |
col = x$col, |
| 449 |
# cex = x$cex, |
|
| 450 | ! |
xpd = TRUE |
| 451 |
) |
|
| 452 |
} |
|
| 453 | ||
| 454 |
#' Prepare Data for Plotting |
|
| 455 |
#' |
|
| 456 |
#' @param x A [`MultivariateAnalysis-class`] object. |
|
| 457 |
#' @param margin A length-one [`numeric`] vector giving the subscript |
|
| 458 |
#' which the data will be returned: `1` indicates individuals/rows (the |
|
| 459 |
#' default), `2` indicates variables/columns. |
|
| 460 |
#' @param axes A length-two [`numeric`] vector giving the dimensions to be |
|
| 461 |
#' plotted. |
|
| 462 |
#' @param active A [`logical`] scalar: should the active observations be |
|
| 463 |
#' plotted? |
|
| 464 |
#' @param sup A [`logical`] scalar: should the supplementary observations be |
|
| 465 |
#' plotted? |
|
| 466 |
#' @param principal A [`logical`] scalar: should principal coordinates be |
|
| 467 |
#' returned? If `FALSE`, standard coordinates are returned. |
|
| 468 |
#' @param extra_quali An optional vector of qualitative data for aesthetics |
|
| 469 |
#' mapping. |
|
| 470 |
#' @param extra_quanti An optional vector of quantitative data for aesthetics |
|
| 471 |
#' mapping. If a single [`character`] string is passed, it must be one of |
|
| 472 |
#' "`observation`", "`mass`", "`sum`", "`contribution`" or "`cos2`" |
|
| 473 |
#' (see [`augment()`]). |
|
| 474 |
#' @param color The colors for lines and points (will be mapped to |
|
| 475 |
#' `extra_quanti` or `extra_quali`; if both are set, the latter has priority). |
|
| 476 |
#' Ignored if set to `FALSE`. If `NULL`, the default color scheme will be used. |
|
| 477 |
#' @param fill The background colors for points (will be mapped to |
|
| 478 |
#' `extra_quanti` or `extra_quali`; if both are set, the latter has priority). |
|
| 479 |
#' Ignored if set to `FALSE`. |
|
| 480 |
#' @param symbol A vector of plotting characters or symbols (will be mapped to |
|
| 481 |
#' `extra_quali`). This can either be a single character or an integer code for |
|
| 482 |
#' one of a set of graphics symbols. If `symbol` is a named a named vector, |
|
| 483 |
#' then the symbols will be associated with their name within `extra_quali`. |
|
| 484 |
#' Ignored if set to `FALSE`. |
|
| 485 |
#' @param size A length-two [`numeric`] vector giving range of possible sizes |
|
| 486 |
#' (greater than 0; will be mapped to `extra_quanti`). |
|
| 487 |
#' Ignored if set to `FALSE`. |
|
| 488 |
#' @param line_type A specification for the line type (will be mapped to |
|
| 489 |
#' `extra_quali`). If `line_type` is a named a named vector, then the line |
|
| 490 |
#' types will be associated with their name within `extra_quali`. |
|
| 491 |
#' Ignored if set to `FALSE`. |
|
| 492 |
#' @param line_width A specification for the line type and width (will |
|
| 493 |
#' be mapped to `extra_quanti`). |
|
| 494 |
#' Ignored if set to `FALSE`. |
|
| 495 |
#' @param ... Further [graphical parameters][graphics::par]. |
|
| 496 |
#' @return |
|
| 497 |
#' A [`data.frame`] with the following columns: |
|
| 498 |
#' \describe{
|
|
| 499 |
#' \item{`x`}{Coordinates along x.}
|
|
| 500 |
#' \item{`y`}{Coordinates along y.}
|
|
| 501 |
#' \item{`extra_quali`}{Extra qualitative variable to be highlighted.}
|
|
| 502 |
#' \item{`extra_quanti`}{Extra quantitative variable to be highlighted.}
|
|
| 503 |
#' \item{`label`}{Label.}
|
|
| 504 |
#' \item{`sup`}{Is supplementary?}
|
|
| 505 |
#' \item{`col`}{Color for lines and symbols.}
|
|
| 506 |
#' \item{`bg`}{Background color for symbols.}
|
|
| 507 |
#' \item{`pch`}{Symbols.}
|
|
| 508 |
#' \item{`cex`}{Symbol sizes.}
|
|
| 509 |
#' \item{`lty`}{Line types.}
|
|
| 510 |
#' \item{`lwd`}{Line widths.}
|
|
| 511 |
#' } |
|
| 512 |
#' @author N. Frerebeau |
|
| 513 |
#' @keywords internal |
|
| 514 |
prepare_plot <- function(x, margin, ..., axes = c(1, 2), active = TRUE, |
|
| 515 |
sup = TRUE, principal = TRUE, |
|
| 516 |
extra_quali = NULL, extra_quanti = NULL, |
|
| 517 |
color = NULL, fill = FALSE, |
|
| 518 |
symbol = NULL, size = c(1, 6), |
|
| 519 |
line_type = NULL, line_width = size) {
|
|
| 520 |
## Validation |
|
| 521 | 55x |
arkhe::assert_scalar(margin, "numeric") |
| 522 | 55x |
arkhe::assert_type(axes, "numeric") |
| 523 | 55x |
arkhe::assert_length(axes, 2) |
| 524 | 55x |
arkhe::assert_scalar(sup, "logical") |
| 525 | 55x |
arkhe::assert_scalar(principal, "logical") |
| 526 | ||
| 527 |
## Set margin and axes |
|
| 528 | 55x |
assign("margin", value = margin, envir = the)
|
| 529 | 55x |
assign("axes", value = axes, envir = the)
|
| 530 | 55x |
assign("principal", value = principal, envir = the)
|
| 531 | ||
| 532 |
## Prepare data |
|
| 533 | 55x |
data <- augment(x, margin = margin, axes = axes, principal = principal) |
| 534 | 55x |
n <- nrow(data) |
| 535 | ||
| 536 |
## Recode |
|
| 537 | 55x |
data$observation <- ifelse(data$supplementary, "suppl.", "active") |
| 538 | ||
| 539 |
## Reorder |
|
| 540 |
## /!\ See build_results() /!\ |
|
| 541 | 55x |
origin <- get_order(x, margin = margin) |
| 542 | 55x |
if (length(extra_quanti) > 1) {
|
| 543 | 3x |
arkhe::assert_type(extra_quanti, "numeric") |
| 544 | 3x |
arkhe::assert_length(extra_quanti, n) |
| 545 | 3x |
extra_quanti <- extra_quanti[origin] |
| 546 |
} |
|
| 547 | 55x |
if (length(extra_quali) > 1) {
|
| 548 | 10x |
arkhe::assert_length(extra_quali, n) |
| 549 | 10x |
extra_quali <- extra_quali[origin] |
| 550 |
} |
|
| 551 | ||
| 552 |
## Set graphical parameters |
|
| 553 |
## (recycle if of length one) |
|
| 554 | 55x |
dots <- list(...) |
| 555 | 55x |
col <- recycle(dots$col %||% graphics::par("col"), n)
|
| 556 | 55x |
bg <- recycle(dots$bg %||% graphics::par("bg"), n)
|
| 557 | 55x |
pch <- recycle(dots$pch %||% 16, n) |
| 558 | 55x |
cex <- recycle(dots$cex %||% graphics::par("cex"), n)
|
| 559 | 55x |
lty <- recycle(dots$lty %||% graphics::par("lty"), n)
|
| 560 | 55x |
lwd <- recycle(dots$lwd %||% graphics::par("lwd"), n)
|
| 561 | ||
| 562 |
## Highlight quantitative information |
|
| 563 | 55x |
if (length(extra_quanti) == 1) {
|
| 564 | 4x |
extra <- get_extra(x)[[extra_quanti]] |
| 565 | 4x |
if (length(extra) > 1) {
|
| 566 | ! |
extra_quanti <- extra |
| 567 |
} else {
|
|
| 568 | 4x |
choices <- c("mass", "sum", "contribution", "cos2")
|
| 569 | 4x |
extra_quanti <- match.arg(extra_quanti, choices = choices, several.ok = FALSE) |
| 570 | 4x |
extra_quanti <- data[[extra_quanti]] |
| 571 |
} |
|
| 572 |
} |
|
| 573 | 55x |
if (length(extra_quanti) > 0) {
|
| 574 | 7x |
extra_quanti <- as.vector(extra_quanti) |
| 575 |
## Continuous scales |
|
| 576 |
## (ignored if col, bg, cex and lwd are set by user) |
|
| 577 | 7x |
if (is.null(dots$col) && !isFALSE(color)) |
| 578 | 7x |
col <- khroma::palette_color_continuous(colors = color)(extra_quanti) |
| 579 | 7x |
if (is.null(dots$bg) && !isFALSE(fill)) |
| 580 | ! |
bg <- khroma::palette_color_continuous(colors = fill)(extra_quanti) |
| 581 | 7x |
if (is.null(dots$cex) && !isFALSE(size)) |
| 582 | 7x |
cex <- khroma::palette_size_sequential(range = size)(extra_quanti) |
| 583 | 7x |
if (is.null(dots$lwd) && !isFALSE(line_width)) |
| 584 | 7x |
lwd <- khroma::palette_size_sequential(range = line_width)(extra_quanti) |
| 585 |
} else {
|
|
| 586 | 48x |
extra_quanti <- rep(NA_real_, n) |
| 587 |
} |
|
| 588 | ||
| 589 |
## Highlight qualitative information |
|
| 590 | 55x |
if (is.null(extra_quali) && has_groups(x, margin = margin)) {
|
| 591 | ! |
extra_quali <- get_groups(x, margin = margin) |
| 592 |
} |
|
| 593 | 55x |
if (is.character(extra_quali) && length(extra_quali) == 1) {
|
| 594 | 33x |
extra <- get_extra(x)[[extra_quali]] |
| 595 | 33x |
if (length(extra) > 1) {
|
| 596 | 1x |
extra_quali <- extra |
| 597 |
} else {
|
|
| 598 | 32x |
choices <- c("observation")
|
| 599 | 32x |
extra_quali <- match.arg(extra_quali, choices = choices, several.ok = FALSE) |
| 600 | 32x |
extra_quali <- data[[extra_quali]] |
| 601 |
} |
|
| 602 |
} |
|
| 603 | 55x |
if (!isFALSE(extra_quali) && length(extra_quali) > 0) {
|
| 604 | 43x |
extra_quali <- as.vector(extra_quali) |
| 605 |
## Discrete scales |
|
| 606 |
## (ignored if col, bg, pch and lty are set by user) |
|
| 607 | 43x |
if (is.null(dots$col) && !isFALSE(color)) |
| 608 | 43x |
col <- khroma::palette_color_discrete(colors = color)(extra_quali) |
| 609 | 43x |
if (is.null(dots$bg) && !isFALSE(fill)) |
| 610 | ! |
bg <- khroma::palette_color_discrete(colors = fill)(extra_quali) |
| 611 | 43x |
if (is.null(dots$pch) && !isFALSE(symbol)) |
| 612 | 35x |
pch <- khroma::palette_shape(symbols = symbol)(extra_quali) |
| 613 | 43x |
if (is.null(dots$lty) && !isFALSE(line_type)) |
| 614 | 43x |
lty <- khroma::palette_line(types = line_type)(extra_quali) |
| 615 |
} else {
|
|
| 616 | 12x |
extra_quali <- rep(NA_character_, n) |
| 617 |
} |
|
| 618 | ||
| 619 |
## Check |
|
| 620 | 55x |
arkhe::assert_length(col, n) |
| 621 | 55x |
arkhe::assert_length(bg, n) |
| 622 | 55x |
arkhe::assert_length(pch, n) |
| 623 | 55x |
arkhe::assert_length(cex, n) |
| 624 | 55x |
arkhe::assert_length(lty, n) |
| 625 | 55x |
arkhe::assert_length(lwd, n) |
| 626 | ||
| 627 | 55x |
coord <- data.frame( |
| 628 | 55x |
data, |
| 629 | 55x |
x = data[[1L]], |
| 630 | 55x |
y = data[[2L]], |
| 631 | 55x |
extra_quali = extra_quali, |
| 632 | 55x |
extra_quanti = extra_quanti, |
| 633 | 55x |
col = col, |
| 634 | 55x |
bg = bg, |
| 635 | 55x |
pch = pch, |
| 636 | 55x |
cex = cex, |
| 637 | 55x |
lty = lty, |
| 638 | 55x |
lwd = lwd, |
| 639 | 55x |
row.names = NULL |
| 640 |
) |
|
| 641 | ||
| 642 |
## Subset |
|
| 643 | 6x |
if (active & !sup) coord <- coord[!coord$supplementary, , drop = FALSE] |
| 644 | 4x |
if (!active & sup) coord <- coord[coord$supplementary, , drop = FALSE] |
| 645 | ||
| 646 | 55x |
coord |
| 647 |
} |
|
| 648 | ||
| 649 |
#' Build a Legend |
|
| 650 |
#' |
|
| 651 |
#' @param x A [`data.frame`] returned by [prepare_plot()]. |
|
| 652 |
#' @param args A [`list`] of additional arguments to be passed to |
|
| 653 |
#' [graphics::legend()]; names of the list are used as argument names. |
|
| 654 |
#' If `NULL` or empty, no legend is displayed. |
|
| 655 |
#' @param points A [`logical`] scalar: legend for points? |
|
| 656 |
#' @param lines A [`logical`] scalar: legend for lines? |
|
| 657 |
#' @author N. Frerebeau |
|
| 658 |
#' @keywords internal |
|
| 659 |
prepare_legend <- function(x, args, points = TRUE, lines = TRUE) {
|
|
| 660 | 45x |
quanti <- x$extra_quanti |
| 661 | 45x |
quali <- x$extra_quali |
| 662 | ||
| 663 | ! |
if (!is.list(args) || length(args) == 0) return(NULL) |
| 664 | 4x |
if (all(is.na(quanti)) && all(is.na(quali))) return(NULL) |
| 665 | ||
| 666 |
## Continuous scale |
|
| 667 | 41x |
if (!all(is.na(quanti))) {
|
| 668 | 8x |
quanti <- quanti[!is.na(quanti)] |
| 669 | 8x |
solo <- !duplicated(quanti) |
| 670 | ||
| 671 | 8x |
if (sum(solo) > 1) {
|
| 672 | 7x |
pr <- pretty(quanti, n = ifelse(nrow(x) > 5, 5, nrow(x))) |
| 673 | 7x |
pr <- pr[pr <= max(quanti) & pr >= min(quanti)] |
| 674 | 7x |
i <- order(quanti, method = "radix") |
| 675 | 7x |
i <- setdiff(i, which(duplicated(quanti))) |
| 676 | ||
| 677 | 7x |
col <- grDevices::colorRamp(x$col[i])(scale_range(pr, from = range(quanti))) |
| 678 | 7x |
col <- grDevices::rgb(col, maxColorValue = 255) |
| 679 | ||
| 680 | 7x |
leg <- list(legend = pr, col = col) |
| 681 | 7x |
if (points) {
|
| 682 | 5x |
cex <- stats::approx(x = quanti[i], y = x$cex[i], xout = pr, ties = "ordered")$y |
| 683 | 5x |
leg <- utils::modifyList(leg, list(pch = unique(x$pch), pt.cex = cex)) |
| 684 |
} |
|
| 685 | 7x |
if (lines) {
|
| 686 | 2x |
lwd <- stats::approx(x = quanti[i], y = x$lwd[i], xout = pr, ties = "ordered")$y |
| 687 | 2x |
leg <- utils::modifyList(leg, list(lty = unique(x$lty), lwd = lwd)) |
| 688 |
} |
|
| 689 |
} else {
|
|
| 690 | 1x |
leg <- list(legend = quanti[solo], col = x$col[solo]) |
| 691 | 1x |
if (points) {
|
| 692 | 1x |
leg <- utils::modifyList(leg, list(pch = x$pch[solo], pt.cex = x$cex[solo])) |
| 693 |
} |
|
| 694 | 1x |
if (lines) {
|
| 695 | 1x |
leg <- utils::modifyList(leg, list(lty = x$lty[solo], lwd = x$lwd[solo])) |
| 696 |
} |
|
| 697 |
} |
|
| 698 |
} |
|
| 699 |
## Discrete scale |
|
| 700 | 41x |
if (!all(is.na(quali))) {
|
| 701 | 34x |
param <- stats::aggregate( |
| 702 | 34x |
x[, c("col", "bg", "pch", "lty")],
|
| 703 | 34x |
by = list(leg = quali), |
| 704 | 34x |
FUN = unique |
| 705 |
) |
|
| 706 | 34x |
leg <- list(legend = param$leg, col = param$col) |
| 707 | 34x |
if (points) {
|
| 708 | 30x |
leg <- utils::modifyList(leg, list(pt.bg = param$bg, pch = param$pch)) |
| 709 |
} |
|
| 710 | 34x |
if (lines) {
|
| 711 | 9x |
leg <- utils::modifyList(leg, list(lty = param$lty)) |
| 712 |
} |
|
| 713 |
} |
|
| 714 | ||
| 715 | 41x |
utils::modifyList(leg, args) |
| 716 |
} |
| 1 |
# ELLIPSES |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
# Confidence =================================================================== |
|
| 6 |
#' @export |
|
| 7 |
#' @rdname viz_confidence |
|
| 8 |
#' @aliases wrap_confidence,numeric,numeric-method |
|
| 9 |
setMethod( |
|
| 10 |
f = "wrap_confidence", |
|
| 11 |
signature = c(x = "numeric", y = "numeric"), |
|
| 12 |
definition = function(x, y, group = NULL, level = 0.95) {
|
|
| 13 |
## Validation |
|
| 14 | 1x |
n <- length(x) |
| 15 | 1x |
arkhe::assert_length(y, n) |
| 16 | 1x |
arkhe::assert_type(level, "numeric") |
| 17 | ||
| 18 |
## Add groups, if any |
|
| 19 | ! |
if (is.null(group)) group <- rep("", n)
|
| 20 | 1x |
group <- as.character(group) |
| 21 | 1x |
arkhe::assert_length(group, n) |
| 22 | ||
| 23 |
## Clean |
|
| 24 | 1x |
ok <- !is.na(x) & !is.na(y) & !is.na(group) |
| 25 | 1x |
x <- x[ok] |
| 26 | 1x |
y <- y[ok] |
| 27 | 1x |
group <- group[ok] |
| 28 | ||
| 29 |
## Compute ellipse |
|
| 30 | 1x |
index <- split(seq_along(group), f = group) |
| 31 | 1x |
lapply( |
| 32 | 1x |
X = index, |
| 33 | 1x |
FUN = function(i) {
|
| 34 | 3x |
xi <- x[i] |
| 35 | 3x |
yi <- y[i] |
| 36 | ! |
if (length(xi) < 3) return(NULL) |
| 37 | ||
| 38 | 3x |
df1 <- 1 |
| 39 | 3x |
df2 <- length(xi) - 2 |
| 40 | 3x |
radius <- sqrt(stats::qf(p = level, df1, df2) * df1 / df2) |
| 41 | 3x |
wrap_ellipse(xi, yi, radius = radius) |
| 42 |
} |
|
| 43 |
) |
|
| 44 |
} |
|
| 45 |
) |
|
| 46 | ||
| 47 |
#' @export |
|
| 48 |
#' @rdname viz_confidence |
|
| 49 |
#' @aliases wrap_confidence,MultivariateAnalysis,missing-method |
|
| 50 |
setMethod( |
|
| 51 |
f = "wrap_confidence", |
|
| 52 |
signature = c(x = "MultivariateAnalysis", y = "missing"), |
|
| 53 |
definition = function(x, margin = 1, axes = c(1, 2), group = NULL, |
|
| 54 |
level = 0.95, principal = TRUE) {
|
|
| 55 |
## Validation |
|
| 56 | ! |
arkhe::assert_scalar(margin, "numeric") |
| 57 | ! |
arkhe::assert_type(axes, "numeric") |
| 58 | ! |
arkhe::assert_length(axes, 2) |
| 59 | ||
| 60 |
## Get coordinates |
|
| 61 | ! |
data <- get_coordinates(x, margin = margin, principal = principal) |
| 62 | ! |
data <- data[, axes] |
| 63 | ||
| 64 |
## Add groups, if any |
|
| 65 | ! |
if (length(group) > 1) {
|
| 66 | ! |
group <- group[get_order(x, margin = margin)] |
| 67 | ! |
} else if (length(group) == 1) {
|
| 68 | ! |
group <- get_extra(x)[[group]] |
| 69 | ! |
} else if (has_groups(x, margin = margin)) {
|
| 70 | ! |
group <- get_groups(x, margin = margin) |
| 71 |
} |
|
| 72 | ||
| 73 |
## Compute ellipse |
|
| 74 | ! |
methods::callGeneric(x = data[, 1], y = data[, 2], |
| 75 | ! |
group = group, level = level) |
| 76 |
} |
|
| 77 |
) |
|
| 78 | ||
| 79 |
#' @export |
|
| 80 |
#' @rdname viz_confidence |
|
| 81 |
#' @aliases wrap_confidence,PCOA,missing-method |
|
| 82 |
setMethod( |
|
| 83 |
f = "wrap_confidence", |
|
| 84 |
signature = c(x = "PCOA", y = "missing"), |
|
| 85 |
definition = function(x, axes = c(1, 2), group = NULL, level = 0.95) {
|
|
| 86 |
## Validation |
|
| 87 | 1x |
arkhe::assert_type(axes, "numeric") |
| 88 | 1x |
arkhe::assert_length(axes, 2) |
| 89 | ||
| 90 |
## Get coordinates |
|
| 91 | 1x |
data <- get_coordinates(x) |
| 92 | 1x |
data <- data[, axes] |
| 93 | ||
| 94 |
## Compute ellipse |
|
| 95 | 1x |
methods::callGeneric(x = data[, 1], y = data[, 2], |
| 96 | 1x |
group = group, level = level) |
| 97 |
} |
|
| 98 |
) |
|
| 99 | ||
| 100 |
# Tolerance ==================================================================== |
|
| 101 |
#' @export |
|
| 102 |
#' @rdname viz_tolerance |
|
| 103 |
#' @aliases wrap_tolerance,numeric,numeric-method |
|
| 104 |
setMethod( |
|
| 105 |
f = "wrap_tolerance", |
|
| 106 |
signature = c(x = "numeric", y = "numeric"), |
|
| 107 |
definition = function(x, y, group = NULL, level = 0.95) {
|
|
| 108 |
## Validation |
|
| 109 | 1x |
n <- length(x) |
| 110 | 1x |
arkhe::assert_length(y, n) |
| 111 | 1x |
arkhe::assert_type(level, "numeric") |
| 112 | ||
| 113 |
## Add groups, if any |
|
| 114 | ! |
if (is.null(group)) group <- rep("", n)
|
| 115 | 1x |
group <- as.character(group) |
| 116 | 1x |
arkhe::assert_length(group, n) |
| 117 | ||
| 118 |
## Clean |
|
| 119 | 1x |
ok <- !is.na(x) & !is.na(y) & !is.na(group) |
| 120 | 1x |
x <- x[ok] |
| 121 | 1x |
y <- y[ok] |
| 122 | 1x |
group <- group[ok] |
| 123 | ||
| 124 |
## Compute ellipse |
|
| 125 | 1x |
index <- split(seq_along(group), f = group) |
| 126 | 1x |
lapply( |
| 127 | 1x |
X = index, |
| 128 | 1x |
FUN = function(i) {
|
| 129 | 3x |
xi <- x[i] |
| 130 | 3x |
yi <- y[i] |
| 131 | ! |
if (length(xi) < 3) return(NULL) |
| 132 | ||
| 133 | 3x |
df <- 1 |
| 134 | 3x |
radius <- sqrt(stats::qchisq(p = level, df = df)) |
| 135 | 3x |
wrap_ellipse(xi, yi, radius = radius) |
| 136 |
} |
|
| 137 |
) |
|
| 138 |
} |
|
| 139 |
) |
|
| 140 | ||
| 141 |
#' @export |
|
| 142 |
#' @rdname viz_tolerance |
|
| 143 |
#' @aliases wrap_tolerance,MultivariateAnalysis,missing-method |
|
| 144 |
setMethod( |
|
| 145 |
f = "wrap_tolerance", |
|
| 146 |
signature = c(x = "MultivariateAnalysis", y = "missing"), |
|
| 147 |
definition = function(x, margin = 1, axes = c(1, 2), group = NULL, |
|
| 148 |
level = 0.95, principal = TRUE) {
|
|
| 149 |
## Validation |
|
| 150 | ! |
arkhe::assert_scalar(margin, "numeric") |
| 151 | ! |
arkhe::assert_type(axes, "numeric") |
| 152 | ! |
arkhe::assert_length(axes, 2) |
| 153 | ||
| 154 |
## Get coordinates |
|
| 155 | ! |
data <- get_coordinates(x, margin = margin, principal = principal) |
| 156 | ! |
data <- data[, axes] |
| 157 | ||
| 158 |
## Add groups, if any |
|
| 159 | ! |
if (length(group) > 1) {
|
| 160 | ! |
group <- group[get_order(x, margin = margin)] |
| 161 | ! |
} else if (length(group) == 1) {
|
| 162 | ! |
group <- get_extra(x)[[group]] |
| 163 | ! |
} else if (has_groups(x, margin = margin)) {
|
| 164 | ! |
group <- get_groups(x, margin = margin) |
| 165 |
} |
|
| 166 | ||
| 167 |
## Compute ellipse |
|
| 168 | ! |
methods::callGeneric(x = data[, 1], y = data[, 2], |
| 169 | ! |
group = group, level = level) |
| 170 |
} |
|
| 171 |
) |
|
| 172 | ||
| 173 |
#' @export |
|
| 174 |
#' @rdname viz_tolerance |
|
| 175 |
#' @aliases wrap_tolerance,PCOA,missing-method |
|
| 176 |
setMethod( |
|
| 177 |
f = "wrap_tolerance", |
|
| 178 |
signature = c(x = "PCOA", y = "missing"), |
|
| 179 |
definition = function(x, axes = c(1, 2), group = NULL, level = 0.95) {
|
|
| 180 |
## Validation |
|
| 181 | 1x |
arkhe::assert_type(axes, "numeric") |
| 182 | 1x |
arkhe::assert_length(axes, 2) |
| 183 | ||
| 184 |
## Get coordinates |
|
| 185 | 1x |
data <- get_coordinates(x) |
| 186 | 1x |
data <- data[, axes] |
| 187 | ||
| 188 |
## Compute ellipse |
|
| 189 | 1x |
methods::callGeneric(x = data[, 1], y = data[, 2], |
| 190 | 1x |
group = group, level = level) |
| 191 |
} |
|
| 192 |
) |
|
| 193 | ||
| 194 |
# Helpers ====================================================================== |
|
| 195 |
wrap_ellipse <- function(x, y, radius = 1) {
|
|
| 196 |
## Compute ellipse |
|
| 197 | 6x |
xy <- cbind(x, y) |
| 198 | 6x |
mu <- colMeans(xy) |
| 199 | 6x |
sigma <- stats::cov(xy) |
| 200 |
# rob <- robustbase::covMcd(xy) |
|
| 201 |
# mu <- rob$center |
|
| 202 |
# sigma <- rob$cov |
|
| 203 | 6x |
ellipse(sigma = sigma, mu = mu, radius = radius) |
| 204 |
} |
|
| 205 | ||
| 206 |
#' Computes an Ellipse |
|
| 207 |
#' |
|
| 208 |
#' @param sigma A square positive definite \eqn{2 \times 2}{2 x 2} covariance
|
|
| 209 |
#' or correlation `matrix`. |
|
| 210 |
#' @param mu A length-two [`numeric`] vector giving the centre of the ellipse. |
|
| 211 |
#' @param scale If `sigma` is a correlation matrix, then the standard deviations |
|
| 212 |
#' of each parameter can be given in the scale parameter. |
|
| 213 |
#' Defaults to `c(1, 1)`, so no rescaling will be done. |
|
| 214 |
#' @param level A length-\eqn{k} [`numeric`] vector giving the confidence level
|
|
| 215 |
#' of a pairwise confidence region. |
|
| 216 |
#' @param radius The size of the ellipse may also be controlled by specifying |
|
| 217 |
#' the value of a t-statistic on its boundary. |
|
| 218 |
#' @param n A length-one [`numeric`] vector specifying the number of points used |
|
| 219 |
#' in the ellipse. |
|
| 220 |
#' @param ... Currently not used. |
|
| 221 |
#' @note Adapted from [ellipse::ellipse()]. |
|
| 222 |
#' @return |
|
| 223 |
#' A [`list`] of \eqn{k} \eqn{n \times 2}{n x 2} `matrix`, suitable for
|
|
| 224 |
#' plotting. |
|
| 225 |
#' @keywords internal |
|
| 226 |
#' @noRd |
|
| 227 |
ellipse <- function(sigma, ..., mu = c(0, 0), scale = c(1, 1), level = 0.95, |
|
| 228 |
radius = sqrt(stats::qchisq(level, 2)), n = 100) {
|
|
| 229 | 6x |
r <- sigma[1, 2] |
| 230 | ||
| 231 | 6x |
if (missing(scale)) {
|
| 232 | 6x |
scale <- sqrt(diag(sigma)) |
| 233 | 6x |
if (scale[1] > 0) r <- r / scale[1] |
| 234 | 6x |
if (scale[2] > 0) r <- r / scale[2] |
| 235 |
} |
|
| 236 | ||
| 237 | 6x |
r <- min(max(r, -1), 1) # clamp to -1..1, in case of rounding errors |
| 238 | 6x |
d <- acos(r) |
| 239 | 6x |
a <- seq(0, 2 * pi, len = n) |
| 240 | ||
| 241 | 6x |
lapply( |
| 242 | 6x |
X = radius, |
| 243 | 6x |
FUN = function(x) {
|
| 244 | 6x |
matrix( |
| 245 | 6x |
data = c(x * scale[1] * cos(a + d / 2) + mu[1], |
| 246 | 6x |
x * scale[2] * cos(a - d / 2) + mu[2]), |
| 247 | 6x |
nrow = n, |
| 248 | 6x |
ncol = 2, |
| 249 | 6x |
dimnames = list(NULL, c("x", "y"))
|
| 250 |
) |
|
| 251 |
} |
|
| 252 |
) |
|
| 253 |
} |
| 1 |
# SUBSET |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname subset |
|
| 7 |
#' @aliases [[,CA,ANY,missing-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "[[", |
|
| 10 |
signature = c(x = "CA", i = "ANY", j = "missing"), |
|
| 11 |
definition = function(x, i) {
|
|
| 12 | 9x |
data <- as.list(x) |
| 13 | 9x |
methods::callGeneric(x = data, i = i) |
| 14 |
} |
|
| 15 |
) |
|
| 16 | ||
| 17 |
#' @export |
|
| 18 |
#' @rdname subset |
|
| 19 |
#' @aliases [[,PCA,ANY,missing-method |
|
| 20 |
setMethod( |
|
| 21 |
f = "[[", |
|
| 22 |
signature = c(x = "PCA", i = "ANY", j = "missing"), |
|
| 23 |
definition = function(x, i) {
|
|
| 24 | 9x |
data <- as.list(x) |
| 25 | 9x |
data[[1]] <- list( |
| 26 | 9x |
data = x@data, |
| 27 | 9x |
mean = x@center, |
| 28 | 9x |
sd = x@scale |
| 29 |
) |
|
| 30 | 9x |
data[[3]][["cor"]] <- sqrt(x@columns@cosine) |
| 31 | 9x |
methods::callGeneric(x = data, i = i) |
| 32 |
} |
|
| 33 |
) |
| 1 |
# SUMMARY |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
# CA =========================================================================== |
|
| 6 |
#' @export |
|
| 7 |
#' @method summary CA |
|
| 8 |
summary.CA <- function(object, ..., axes = c(1, 2), margin = 1, |
|
| 9 |
active = TRUE, sup = TRUE, rank = NULL) {
|
|
| 10 |
## Get data |
|
| 11 | 3x |
values <- build_summary(object, axes = axes, margin = margin, rank = rank, |
| 12 | 3x |
active = active, sup = sup) |
| 13 | ||
| 14 | 3x |
.SummaryCA( |
| 15 | 3x |
data = object@data, |
| 16 | 3x |
eigenvalues = values$eigenvalues, |
| 17 | 3x |
results = values$results, |
| 18 | 3x |
supplement = values$supplement, |
| 19 | 3x |
margin = as.integer(margin) |
| 20 |
) |
|
| 21 |
} |
|
| 22 | ||
| 23 |
#' @export |
|
| 24 |
#' @rdname summary |
|
| 25 |
#' @aliases summary,CA-method |
|
| 26 |
setMethod("summary", c(object = "CA"), summary.CA)
|
|
| 27 | ||
| 28 |
#' @export |
|
| 29 |
#' @rdname describe |
|
| 30 |
#' @aliases describe,CA-method |
|
| 31 |
setMethod( |
|
| 32 |
f = "describe", |
|
| 33 |
signature = signature(x = "CA"), |
|
| 34 |
definition = function(x, ...) {
|
|
| 35 | 2x |
row_sup <- x@rows@supplement |
| 36 | 2x |
col_sup <- x@columns@supplement |
| 37 | ||
| 38 | 2x |
sup_txt <- tr_(" (+ %d supplementary)")
|
| 39 | 2x |
row_txt <- if (any(row_sup)) sprintf(sup_txt, sum(row_sup)) else "" |
| 40 | 2x |
col_txt <- if (any(col_sup)) sprintf(sup_txt, sum(col_sup)) else "" |
| 41 | ||
| 42 | 2x |
cat( |
| 43 | 2x |
sprintf(tr_("* Row variable: %d categories%s."), sum(!row_sup), row_txt),
|
| 44 | 2x |
sprintf(tr_("* Column variable: %d categories%s."), sum(!col_sup), col_txt),
|
| 45 |
..., |
|
| 46 | 2x |
sep = "\n" |
| 47 |
) |
|
| 48 | 2x |
invisible(x) |
| 49 |
} |
|
| 50 |
) |
|
| 51 | ||
| 52 |
# PCA ========================================================================== |
|
| 53 |
#' @export |
|
| 54 |
#' @method summary PCA |
|
| 55 |
summary.PCA <- function(object, ..., axes = c(1, 2), margin = 1, |
|
| 56 |
active = TRUE, sup = TRUE, rank = NULL) {
|
|
| 57 |
## Get data |
|
| 58 | 3x |
values <- build_summary(object, axes = axes, margin = margin, rank = rank, |
| 59 | 3x |
active = active, sup = sup) |
| 60 | ||
| 61 | 3x |
.SummaryPCA( |
| 62 | 3x |
data = object@data, |
| 63 | 3x |
eigenvalues = values$eigenvalues, |
| 64 | 3x |
results = values$results, |
| 65 | 3x |
supplement = values$supplement, |
| 66 | 3x |
margin = as.integer(margin) |
| 67 |
) |
|
| 68 |
} |
|
| 69 | ||
| 70 |
#' @export |
|
| 71 |
#' @rdname summary |
|
| 72 |
#' @aliases summary,PCA-method |
|
| 73 |
setMethod("summary", c(object = "PCA"), summary.PCA)
|
|
| 74 | ||
| 75 |
#' @export |
|
| 76 |
#' @rdname describe |
|
| 77 |
#' @aliases describe,PCA-method |
|
| 78 |
setMethod( |
|
| 79 |
f = "describe", |
|
| 80 |
signature = signature(x = "PCA"), |
|
| 81 |
definition = function(x, ...) {
|
|
| 82 | 1x |
row_sup <- x@rows@supplement |
| 83 | 1x |
col_sup <- x@columns@supplement |
| 84 | ||
| 85 | 1x |
sup_txt <- tr_(" (+ %d supplementary)")
|
| 86 | 1x |
row_txt <- if (any(row_sup)) sprintf(sup_txt, sum(row_sup)) else "" |
| 87 | 1x |
col_txt <- if (any(col_sup)) sprintf(sup_txt, sum(col_sup)) else "" |
| 88 | ||
| 89 | 1x |
if (is_centered(x)) {
|
| 90 | 1x |
var_center <- tr_("* Variables were shifted to be zero centered.")
|
| 91 |
} else {
|
|
| 92 | ! |
var_center <- tr_("* Variables were NOT shifted to be zero centered.")
|
| 93 |
} |
|
| 94 | 1x |
if (is_scaled(x)) {
|
| 95 | ! |
var_scale <- tr_("* Variables were scaled to unit variance.")
|
| 96 |
} else {
|
|
| 97 | 1x |
var_scale <- tr_("* Variables were NOT scaled to unit variance.")
|
| 98 |
} |
|
| 99 | ||
| 100 | 1x |
cat( |
| 101 | 1x |
sprintf(tr_("* %d individuals%s."), sum(!row_sup), row_txt),
|
| 102 | 1x |
sprintf(tr_("* %d variables%s."), sum(!col_sup), col_txt),
|
| 103 | 1x |
var_center, |
| 104 | 1x |
var_scale, |
| 105 |
..., |
|
| 106 | 1x |
sep = "\n" |
| 107 |
) |
|
| 108 | 1x |
invisible(x) |
| 109 |
} |
|
| 110 |
) |
|
| 111 | ||
| 112 |
# Helpers ====================================================================== |
|
| 113 |
build_summary <- function(object, axes, margin, rank = NULL, |
|
| 114 |
active = TRUE, sup = TRUE, |
|
| 115 |
prefix = "F") {
|
|
| 116 |
## Validation |
|
| 117 | 6x |
arkhe::assert_filled(axes) |
| 118 | 6x |
arkhe::assert_type(axes, "numeric") |
| 119 | ||
| 120 |
## /!\ Backward compatibility /!\ |
|
| 121 | 6x |
if (!is.null(rank)) {
|
| 122 | ! |
axes <- seq_len(rank) |
| 123 | ! |
msg <- "'rank' argument is deprecated, use 'axes' instead." |
| 124 | ! |
warning(msg, call. = FALSE) |
| 125 |
} |
|
| 126 | ||
| 127 |
## Get data |
|
| 128 | 6x |
eig <- get_eigenvalues(object) |
| 129 | 6x |
inertia <- get_distances(object, margin = margin) |
| 130 | 6x |
coord <- get_coordinates(object, margin = margin) |
| 131 | 6x |
contrib <- get_contributions(object, margin = margin) |
| 132 | 6x |
cos2 <- get_cos2(object, margin = margin) |
| 133 | ||
| 134 | 3x |
if (inherits(object, "CA")) inertia <- inertia * 1000 |
| 135 | ||
| 136 |
## Fix lengths |
|
| 137 | 6x |
n <- nrow(coord) |
| 138 | 6x |
m <- nrow(contrib) |
| 139 | 6x |
if (n > m) {
|
| 140 | 6x |
length(inertia) <- n |
| 141 | 6x |
contrib[seq(m + 1, n, 1), ] <- NA |
| 142 |
} |
|
| 143 | ||
| 144 |
## Bind columns |
|
| 145 | 6x |
values <- vector(mode = "list", length = length(axes)) |
| 146 | 6x |
for (j in axes) {
|
| 147 | 12x |
v <- cbind(coord[[j]], contrib[[j]], cos2[[j]]) |
| 148 | 12x |
colnames(v) <- paste0(prefix, j, c("_coord", "_contrib", "_cos2"))
|
| 149 | 12x |
values[[j]] <- v |
| 150 |
} |
|
| 151 | 6x |
values <- do.call(cbind, values) |
| 152 | 6x |
values <- cbind(inertia = inertia, values) |
| 153 | 3x |
if (inherits(object, "PCA")) colnames(values)[1] <- "dist" |
| 154 | 6x |
rownames(values) <- rownames(coord) |
| 155 | ||
| 156 |
## Remove data |
|
| 157 | 6x |
is_sup <- coord$.sup |
| 158 | ! |
if (!active && !sup) active <- TRUE |
| 159 | 6x |
if (!active) {
|
| 160 | 2x |
values <- values[is_sup, , drop = FALSE] |
| 161 | 2x |
is_sup <- is_sup[is_sup] |
| 162 |
} |
|
| 163 | 6x |
if (!sup) {
|
| 164 | 2x |
values <- values[!is_sup, , drop = FALSE] |
| 165 | 2x |
is_sup <- is_sup[!is_sup] |
| 166 |
} |
|
| 167 | ||
| 168 | 6x |
list( |
| 169 | 6x |
eigenvalues = as.matrix(eig), |
| 170 | 6x |
results = as.matrix(values), |
| 171 | 6x |
supplement = is_sup |
| 172 |
) |
|
| 173 |
} |
| 1 |
# BIPLOT |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
# CA =========================================================================== |
|
| 6 |
#' @export |
|
| 7 |
#' @method biplot CA |
|
| 8 |
biplot.CA <- function(x, ..., axes = c(1, 2), |
|
| 9 |
type = c("symetric", "rows", "columns", "contributions"),
|
|
| 10 |
active = TRUE, sup = TRUE, labels = NULL, |
|
| 11 |
col.rows = c("#E69F00", "#E69F00"),
|
|
| 12 |
col.columns = c("#56B4E9", "#56B4E9"),
|
|
| 13 |
pch.rows = c(16, 1), pch.columns = c(17, 2), |
|
| 14 |
size = c(1, 3), |
|
| 15 |
xlim = NULL, ylim = NULL, main = NULL, sub = NULL, |
|
| 16 |
legend = list(x = "topleft")) {
|
|
| 17 |
## Validation |
|
| 18 | 6x |
type <- match.arg(type, several.ok = FALSE) |
| 19 | ||
| 20 |
## Type of biplot |
|
| 21 | 6x |
if (type == "symetric") {
|
| 22 | 3x |
princ_row <- TRUE |
| 23 | 3x |
princ_col <- TRUE |
| 24 |
} |
|
| 25 | 6x |
if (type == "rows") {
|
| 26 | 1x |
princ_row <- TRUE |
| 27 | 1x |
princ_col <- FALSE |
| 28 |
} |
|
| 29 | 6x |
if (type == "columns") {
|
| 30 | 1x |
princ_row <- FALSE |
| 31 | 1x |
princ_col <- TRUE |
| 32 |
} |
|
| 33 | 6x |
if (type == "contributions") {
|
| 34 | 1x |
princ_row <- FALSE |
| 35 | 1x |
princ_col <- TRUE |
| 36 | 1x |
sup <- FALSE # Override |
| 37 |
} |
|
| 38 | ||
| 39 |
## Get data |
|
| 40 | 6x |
coord_col <- prepare_plot(x, margin = 2, axes = axes, active = active, sup = sup, |
| 41 | 6x |
principal = princ_col, extra_quali = "observation", |
| 42 | 6x |
color = col.columns, symbol = pch.columns, line_type = 0) |
| 43 | 6x |
coord_row <- prepare_plot(x, margin = 1, axes = axes, active = active, sup = sup, |
| 44 | 6x |
principal = princ_row, extra_quali = "observation", |
| 45 | 6x |
color = col.rows, symbol = pch.rows, line_type = 0) |
| 46 | ||
| 47 |
## Graphical parameters |
|
| 48 | 6x |
if (type == "contributions") {
|
| 49 | 1x |
mass_row <- get_masses(x, margin = 1) |
| 50 | 1x |
mass_col <- get_masses(x, margin = 2) |
| 51 | ||
| 52 | 1x |
coord_row$x <- coord_row$x * sqrt(mass_row) |
| 53 | 1x |
coord_row$y <- coord_row$y * sqrt(mass_row) |
| 54 | ||
| 55 | 1x |
coord_row$cex <- khroma::palette_size_sequential(size)(mass_row) |
| 56 | 1x |
coord_col$cex <- khroma::palette_size_sequential(size)(mass_col) |
| 57 |
} |
|
| 58 | ||
| 59 | 6x |
coord <- viz_biplot( |
| 60 | 6x |
coord_row, coord_col, |
| 61 | 6x |
rows = TRUE, columns = TRUE, |
| 62 | 6x |
labels = labels, |
| 63 | 6x |
xlim = xlim, ylim = ylim, |
| 64 | 6x |
main = main, sub = sub, |
| 65 | 6x |
xlab = print_variance(x, axes[[1]]), |
| 66 | 6x |
ylab = print_variance(x, axes[[2]]), |
| 67 | 6x |
legend = legend, |
| 68 |
... |
|
| 69 |
) |
|
| 70 | ||
| 71 |
## Add legend |
|
| 72 | 6x |
viz_legend(coord, legend, points = TRUE, lines = FALSE) |
| 73 | ||
| 74 | 6x |
invisible(x) |
| 75 |
} |
|
| 76 | ||
| 77 |
#' @export |
|
| 78 |
#' @rdname biplot |
|
| 79 |
#' @aliases biplot,CA-method |
|
| 80 |
setMethod("biplot", c(x = "CA"), biplot.CA)
|
|
| 81 | ||
| 82 |
# PCA ========================================================================== |
|
| 83 |
#' @export |
|
| 84 |
#' @method biplot PCA |
|
| 85 |
biplot.PCA <- function(x, ..., axes = c(1, 2), type = c("form", "covariance"),
|
|
| 86 |
active = TRUE, sup = TRUE, labels = "variables", |
|
| 87 |
col.rows = c("#E69F00", "#E69F00"),
|
|
| 88 |
col.columns = c("#56B4E9", "#56B4E9"),
|
|
| 89 |
pch.rows = c(16, 1), lty.columns = c(1, 3), |
|
| 90 |
xlim = NULL, ylim = NULL, main = NULL, sub = NULL, |
|
| 91 |
legend = list(x = "topleft")) {
|
|
| 92 |
## Validation |
|
| 93 | 4x |
type <- match.arg(type, several.ok = FALSE) |
| 94 | ||
| 95 |
## Type of biplot |
|
| 96 | 4x |
if (type == "form") {
|
| 97 | 1x |
princ_row <- TRUE |
| 98 | 1x |
princ_col <- FALSE |
| 99 |
} |
|
| 100 | 4x |
if (type == "covariance") {
|
| 101 | 3x |
princ_row <- FALSE |
| 102 | 3x |
princ_col <- TRUE |
| 103 |
} |
|
| 104 | ||
| 105 |
## Get data |
|
| 106 | 4x |
coord_col <- prepare_plot(x, margin = 2, axes = axes, active = active, sup = sup, |
| 107 | 4x |
principal = princ_col, extra_quali = "observation", |
| 108 | 4x |
color = col.columns, symbol = NA, |
| 109 | 4x |
line_type = lty.columns, ...) |
| 110 | 4x |
coord_row <- prepare_plot(x, margin = 1, axes = axes, active = active, sup = sup, |
| 111 | 4x |
principal = princ_row, extra_quali = "observation", |
| 112 | 4x |
color = col.rows, symbol = pch.rows, |
| 113 | 4x |
line_type = NA, ...) |
| 114 | ||
| 115 | 4x |
arrows_col <- function() {
|
| 116 | 4x |
graphics::arrows( |
| 117 | 4x |
x0 = 0, y0 = 0, |
| 118 | 4x |
x1 = coord_col$x, y1 = coord_col$y, |
| 119 | 4x |
length = 0.10, angle = 30, |
| 120 | 4x |
col = coord_col$col, lty = coord_col$lty, lwd = coord_col$lwd |
| 121 |
) |
|
| 122 |
} |
|
| 123 | ||
| 124 | 4x |
coord <- viz_biplot( |
| 125 | 4x |
coord_row, coord_col, |
| 126 | 4x |
rows = TRUE, columns = FALSE, labels = labels, |
| 127 | 4x |
xlim = xlim, ylim = ylim, |
| 128 | 4x |
main = main, sub = sub, |
| 129 | 4x |
xlab = print_variance(x, axes[[1]]), |
| 130 | 4x |
ylab = print_variance(x, axes[[2]]), |
| 131 | 4x |
panel.first = arrows_col(), |
| 132 | 4x |
legend = legend, |
| 133 |
... |
|
| 134 |
) |
|
| 135 | ||
| 136 |
## Add legend |
|
| 137 | 4x |
viz_legend(coord, legend, points = TRUE, lines = TRUE) |
| 138 | ||
| 139 | 4x |
invisible(x) |
| 140 |
} |
|
| 141 | ||
| 142 |
#' @export |
|
| 143 |
#' @rdname biplot |
|
| 144 |
#' @aliases biplot,PCA-method |
|
| 145 |
setMethod("biplot", c(x = "PCA"), biplot.PCA)
|
|
| 146 | ||
| 147 |
# Helpers ====================================================================== |
|
| 148 |
#' Build a Biplot |
|
| 149 |
#' |
|
| 150 |
#' @param coord_row A [`data.frame`] returned by [prepare_plot()]. |
|
| 151 |
#' @param coord_col A [`data.frame`] returned by [prepare_plot()]. |
|
| 152 |
#' @param rows A [`logical`] scalar: should the rows be drawn? |
|
| 153 |
#' @param columns A [`logical`] scalar: should the columns be drawn? |
|
| 154 |
#' @param labels A [`character`] vector specifying whether |
|
| 155 |
#' "`rows`"/"`individuals`" and/or "`columns`"/"`variables`" names must be |
|
| 156 |
#' drawn. Any unambiguous substring can be given. |
|
| 157 |
#' @param xlim A length-two [`numeric`] vector giving the x limits of the plot. |
|
| 158 |
#' The default value, `NULL`, indicates that the range of the |
|
| 159 |
#' [finite][is.finite()] values to be plotted should be used. |
|
| 160 |
#' @param ylim A length-two [`numeric`] vector giving the y limits of the plot. |
|
| 161 |
#' The default value, `NULL`, indicates that the range of the |
|
| 162 |
#' [finite][is.finite()] values to be plotted should be used. |
|
| 163 |
#' @param main A [`character`] string giving a main title for the plot. |
|
| 164 |
#' @param sub A [`character`] string giving a subtitle for the plot. |
|
| 165 |
#' @param xlab,ylab A [`character`] vector giving the x and y axis labels. |
|
| 166 |
#' @param axes A [`logical`] scalar: should axes be drawn on the plot? |
|
| 167 |
#' @param frame.plot A [`logical`] scalar: should a box be drawn around the |
|
| 168 |
#' plot? |
|
| 169 |
#' @param ann A [`logical`] scalar: should the default annotation (title and x |
|
| 170 |
#' and y axis labels) appear on the plot? |
|
| 171 |
#' @param panel.first An `expression` to be evaluated after the plot axes are |
|
| 172 |
#' set up but before any plotting takes place. This can be useful for drawing |
|
| 173 |
#' background grids. |
|
| 174 |
#' @param panel.last An `expression` to be evaluated after plotting has taken |
|
| 175 |
#' place but before the axes, title and box are added. |
|
| 176 |
#' @return A [`data.frame`] to be passed to [prepare_legend()]. |
|
| 177 |
#' @author N. Frerebeau |
|
| 178 |
#' @keywords internal |
|
| 179 |
#' @noRd |
|
| 180 |
viz_biplot <- function(coord_row, coord_col, ..., rows = TRUE, columns = TRUE, |
|
| 181 |
labels = c("rows", "columns", "individuals", "variables"),
|
|
| 182 |
xlim = NULL, ylim = NULL, main = NULL, sub = NULL, |
|
| 183 |
xlab = NULL, ylab = NULL, axes = TRUE, frame.plot = axes, |
|
| 184 |
ann = graphics::par("ann"),
|
|
| 185 |
panel.first = NULL, panel.last = NULL) {
|
|
| 186 | ||
| 187 |
## Save and restore graphical parameters |
|
| 188 |
## pty: square plotting region, independent of device size |
|
| 189 | 10x |
old_par <- graphics::par(pty = "s", no.readonly = TRUE) |
| 190 | 10x |
on.exit(graphics::par(old_par), add = TRUE) |
| 191 | ||
| 192 |
## Open new window |
|
| 193 | 10x |
grDevices::dev.hold() |
| 194 | 10x |
on.exit(grDevices::dev.flush(), add = TRUE) |
| 195 | 10x |
graphics::plot.new() |
| 196 | ||
| 197 |
## Set plotting coordinates |
|
| 198 | 10x |
xlim <- xlim %||% range(coord_row$x, coord_col$x, na.rm = TRUE, finite = TRUE) |
| 199 | 10x |
ylim <- ylim %||% range(coord_row$y, coord_col$y, na.rm = TRUE, finite = TRUE) |
| 200 | 10x |
graphics::plot.window(xlim = xlim, ylim = ylim, asp = 1) |
| 201 | ||
| 202 |
## Evaluate pre-plot expressions |
|
| 203 | 10x |
panel.first |
| 204 | ||
| 205 |
## Plot |
|
| 206 | 10x |
graphics::abline(h = 0, lty = "dashed", lwd = 1, col = graphics::par("fg"))
|
| 207 | 10x |
graphics::abline(v = 0, lty = "dashed", lwd = 1, col = graphics::par("fg"))
|
| 208 | 10x |
if (rows) {
|
| 209 | 10x |
graphics::points(x = coord_row$x, y = coord_row$y, col = coord_row$col, |
| 210 | 10x |
pch = coord_row$pch, cex = coord_row$cex) |
| 211 |
} |
|
| 212 | 10x |
if (columns) {
|
| 213 | 6x |
graphics::points(x = coord_col$x, y = coord_col$y, col = coord_col$col, |
| 214 | 6x |
pch = coord_col$pch, cex = coord_col$cex) |
| 215 |
} |
|
| 216 | ||
| 217 |
## Labels |
|
| 218 | 10x |
if (!is.null(labels)) {
|
| 219 | ! |
labels <- match.arg(labels, several.ok = TRUE) |
| 220 | ! |
if (any(labels == "rows") | any(labels == "individuals")) {
|
| 221 | ! |
viz_labels(coord_row, filter = NULL) |
| 222 |
} |
|
| 223 | ! |
if (any(labels == "columns") | any(labels == "variables")) {
|
| 224 | ! |
viz_labels(coord_col, filter = NULL) |
| 225 |
} |
|
| 226 |
} |
|
| 227 | ||
| 228 |
## Evaluate post-plot and pre-axis expressions |
|
| 229 | 10x |
panel.last |
| 230 | ||
| 231 |
## Construct axis |
|
| 232 | 10x |
if (axes) {
|
| 233 | 10x |
graphics::axis(side = 1, las = 1) |
| 234 | 10x |
graphics::axis(side = 2, las = 1) |
| 235 |
} |
|
| 236 | ||
| 237 |
## Plot frame |
|
| 238 | 10x |
if (frame.plot) {
|
| 239 | 10x |
graphics::box() |
| 240 |
} |
|
| 241 | ||
| 242 |
## Add annotation |
|
| 243 | 10x |
if (ann) {
|
| 244 | 10x |
graphics::title(main = main, sub = sub, xlab = xlab, ylab = ylab) |
| 245 |
} |
|
| 246 | ||
| 247 |
## Legend |
|
| 248 | 10x |
coord_row$extra_quali <- paste(coord_row$extra_quali, "ind.", sep = " ") |
| 249 | 10x |
coord_col$extra_quali <- paste(coord_col$extra_quali, "var.", sep = " ") |
| 250 | 10x |
rbind(coord_row, coord_col) |
| 251 |
} |
| 1 |
# PLOT CONVEX HULL |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname viz_hull |
|
| 7 |
#' @aliases viz_hull,numeric,numeric-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "viz_hull", |
|
| 10 |
signature = c(x = "numeric", y = "numeric"), |
|
| 11 |
definition = function(x, y, ..., group = NULL, |
|
| 12 |
color = NULL, fill = FALSE, symbol = FALSE) {
|
|
| 13 | ! |
hull <- wrap_hull(x, y, group = group) |
| 14 | ! |
.viz_hull(hull, color = color, fill = fill, symbol = symbol, ...) |
| 15 | ! |
invisible(list(x = x, y = y)) |
| 16 |
} |
|
| 17 |
) |
|
| 18 | ||
| 19 |
#' @export |
|
| 20 |
#' @rdname viz_hull |
|
| 21 |
#' @aliases viz_hull,MultivariateAnalysis,missing-method |
|
| 22 |
setMethod( |
|
| 23 |
f = "viz_hull", |
|
| 24 |
signature = c(x = "MultivariateAnalysis", y = "missing"), |
|
| 25 |
definition = function(x, ..., group = NULL, |
|
| 26 |
color = NULL, fill = FALSE, symbol = FALSE) {
|
|
| 27 | 3x |
hull <- wrap_hull(x, margin = get_margin(), axes = get_axes(), |
| 28 | 3x |
group = group, principal = get_principal()) |
| 29 | 3x |
.viz_hull(hull, color = color, fill = fill, symbol = symbol, ...) |
| 30 | 3x |
invisible(x) |
| 31 |
} |
|
| 32 |
) |
|
| 33 | ||
| 34 |
#' @export |
|
| 35 |
#' @rdname viz_hull |
|
| 36 |
#' @aliases viz_hull,MultivariateBootstrap,missing-method |
|
| 37 |
setMethod( |
|
| 38 |
f = "viz_hull", |
|
| 39 |
signature = c(x = "MultivariateBootstrap", y = "missing"), |
|
| 40 |
definition = function(x, ..., color = FALSE, fill = FALSE, symbol = FALSE) {
|
|
| 41 | ! |
hull <- wrap_hull(x, margin = get_margin(), axes = get_axes(), |
| 42 | ! |
group = NULL, principal = get_principal()) |
| 43 | ! |
.viz_hull(hull, color = color, fill = fill, symbol = symbol, ...) |
| 44 | ! |
invisible(x) |
| 45 |
} |
|
| 46 |
) |
|
| 47 | ||
| 48 |
#' @export |
|
| 49 |
#' @rdname viz_hull |
|
| 50 |
#' @aliases viz_hull,PCOA,missing-method |
|
| 51 |
setMethod( |
|
| 52 |
f = "viz_hull", |
|
| 53 |
signature = c(x = "PCOA", y = "missing"), |
|
| 54 |
definition = function(x, ..., group = NULL, |
|
| 55 |
color = FALSE, fill = FALSE, symbol = FALSE) {
|
|
| 56 | 1x |
hull <- wrap_hull(x, axes = get_axes(), group = group) |
| 57 | 1x |
.viz_hull(hull, color = color, fill = fill, symbol = symbol, ...) |
| 58 | 1x |
invisible(x) |
| 59 |
} |
|
| 60 |
) |
|
| 61 | ||
| 62 |
#' @param x A `list` of `matrix` returned by [wrap_hull()]. |
|
| 63 |
#' @noRd |
|
| 64 |
.viz_hull <- function(x, ..., color = NULL, fill = FALSE, symbol = FALSE) {
|
|
| 65 | 4x |
n <- length(x) |
| 66 | ||
| 67 |
## Recycle graphical parameters if of length one |
|
| 68 | 4x |
dots <- list(...) |
| 69 | 4x |
col <- recycle(dots$border %||% graphics::par("fg"), n)
|
| 70 | 4x |
bg <- recycle(dots$col %||% NA, n) |
| 71 | 4x |
lty <- recycle(dots$lty %||% graphics::par("lty"), n)
|
| 72 | 4x |
lwd <- recycle(dots$lwd %||% graphics::par("lwd"), n)
|
| 73 | ||
| 74 | 4x |
if (n > 1) {
|
| 75 |
## Discrete scales |
|
| 76 | 4x |
extra_quali <- names(x) |
| 77 | 4x |
if (is.null(dots$border) && !isFALSE(color)) |
| 78 | 4x |
col <- khroma::palette_color_discrete(color)(extra_quali) |
| 79 | 4x |
if (is.null(dots$col) && !isFALSE(fill)) |
| 80 | ! |
bg <- khroma::palette_color_discrete(fill)(extra_quali) |
| 81 | 4x |
if (is.null(dots$lty) && !isFALSE(symbol)) |
| 82 | ! |
lty <- khroma::palette_line(symbol)(extra_quali) |
| 83 |
} |
|
| 84 | ||
| 85 | 4x |
for (i in seq_along(x)) {
|
| 86 | 12x |
graphics::polygon( |
| 87 | 12x |
x = x[[i]], |
| 88 | 12x |
border = col[i], |
| 89 | 12x |
col = bg[i], |
| 90 | 12x |
lty = lty[i], |
| 91 | 12x |
lwd = lwd[i] |
| 92 |
) |
|
| 93 |
} |
|
| 94 |
} |
| 1 |
# SVD |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' Singular Value Decomposition of a Matrix |
|
| 6 |
#' |
|
| 7 |
#' @param x A \eqn{m \times p}{m x p} numeric [`matrix`].
|
|
| 8 |
#' @param rank An [`integer`] value specifying the maximal number of components |
|
| 9 |
#' to be kept in the results. |
|
| 10 |
#' @return |
|
| 11 |
#' A [`list`] with the following elements: |
|
| 12 |
#' \describe{
|
|
| 13 |
#' \item{`d`}{A vector containing the singular values of `x`, of length
|
|
| 14 |
#' `rank`, sorted decreasingly.} |
|
| 15 |
#' \item{`u`}{A matrix whose columns contain the left singular vectors of
|
|
| 16 |
#' `x`. Dimension `c(m, rank)`.} |
|
| 17 |
#' \item{`v`}{A matrix whose columns contain the right singular vectors of
|
|
| 18 |
#' `x`. Dimension `c(p, rank)`.} |
|
| 19 |
#' } |
|
| 20 |
#' @note |
|
| 21 |
#' In both PCA and PCA-cor whitening there is a sign-ambiguity in the |
|
| 22 |
#' eigenvector matrices. In order to resolve the sign-ambiguity we use |
|
| 23 |
#' eigenvector matrices with a positive diagonal. This has the effect to make |
|
| 24 |
#' cross-correlations and cross-correlations positive diagonal for PCA. |
|
| 25 |
#' @keywords internal |
|
| 26 |
svd2 <- function(x, rank = Inf) {
|
|
| 27 | 23x |
D <- svd(x, nu = rank, nv = rank) |
| 28 | ||
| 29 | 23x |
keep <- seq_len(rank) |
| 30 | 23x |
sv <- D$d[keep] |
| 31 | ||
| 32 | 23x |
U <- D$u |
| 33 | 23x |
V <- D$v |
| 34 | ||
| 35 |
# Fix sign for consistency with FactoMineR |
|
| 36 | 23x |
if (rank > 1) {
|
| 37 | 23x |
mult <- sign(as.vector(crossprod(rep(1, nrow(V)), as.matrix(V)))) |
| 38 | 23x |
mult[mult == 0] <- 1 |
| 39 | ||
| 40 |
# Build matrix |
|
| 41 |
# matrix * vector is faster (!) than: |
|
| 42 |
# matrix %*% t(vector) |
|
| 43 |
# t(t(matrix) * vector) |
|
| 44 | 23x |
mult_U <- matrix(mult, nrow = nrow(U), ncol = rank, byrow = TRUE) |
| 45 | 23x |
mult_V <- matrix(mult, nrow = nrow(V), ncol = rank, byrow = TRUE) |
| 46 | ||
| 47 | 23x |
U <- U * mult_U |
| 48 | 23x |
V <- V * mult_V |
| 49 |
} |
|
| 50 | ||
| 51 | 23x |
names(sv) <- paste0("F", keep)
|
| 52 | 23x |
list( |
| 53 | 23x |
d = sv, |
| 54 | 23x |
u = U, |
| 55 | 23x |
v = V |
| 56 |
) |
|
| 57 |
} |
| 1 |
# GET VARIANCE |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname get_eigenvalues |
|
| 7 |
#' @aliases get_variance,MultivariateAnalysis-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "get_variance", |
|
| 10 |
signature = c(x = "MultivariateAnalysis"), |
|
| 11 |
definition = function(x, digits = 2) {
|
|
| 12 | 76x |
eig <- x@singular_values^2 |
| 13 | 76x |
pc <- round(eig / sum(eig) * 100, digits = digits) |
| 14 | 76x |
return(pc) |
| 15 |
} |
|
| 16 |
) |
| 1 |
# AUGMENT |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname tidy |
|
| 7 |
#' @aliases augment,MultivariateAnalysis-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "augment", |
|
| 10 |
signature = c(x = "MultivariateAnalysis"), |
|
| 11 |
definition = function(x, ..., margin = 1, axes = c(1, 2), principal = TRUE) {
|
|
| 12 |
## Validation |
|
| 13 | 61x |
arkhe::assert_scalar(margin, "numeric") |
| 14 | 61x |
arkhe::assert_scalar(principal, "logical") |
| 15 | 61x |
arkhe::assert_type(axes, "numeric") |
| 16 | 61x |
arkhe::assert_length(axes, 2) |
| 17 | ||
| 18 |
## Get data |
|
| 19 | 61x |
coords <- get_coordinates(x, margin = margin, principal = principal) |
| 20 | ||
| 21 | 61x |
mass <- contrib <- rep(NA_real_, nrow(coords)) |
| 22 | 61x |
mass[!coords$.sup] <- get_masses(x, margin = margin) |
| 23 | 61x |
contrib[!coords$.sup] <- joint_contributions(x, margin = margin, axes = axes) |
| 24 | 61x |
sum <- joint_coordinates(x, margin = margin, axes = axes, principal = principal) |
| 25 | 61x |
cos2 <- joint_cos2(x, margin = margin, axes = axes) |
| 26 | ||
| 27 | 61x |
data.frame( |
| 28 | 61x |
coords[, axes, drop = FALSE], |
| 29 | 61x |
label = rownames(coords), |
| 30 | 61x |
supplementary = coords$.sup, |
| 31 | 61x |
mass = mass, |
| 32 | 61x |
sum = sum, |
| 33 | 61x |
contribution = contrib, |
| 34 | 61x |
cos2 = cos2, |
| 35 | 61x |
row.names = NULL |
| 36 |
) |
|
| 37 |
} |
|
| 38 |
) |
|
| 39 | ||
| 40 |
#' Joint |
|
| 41 |
#' |
|
| 42 |
#' @param object A [`CA-class`] or [`PCA-class`] object. |
|
| 43 |
#' @param what A [`character`] string. |
|
| 44 |
#' @param margin A length-one [`numeric`] vector giving the subscript |
|
| 45 |
#' which the data will be returned: `1` indicates individuals/rows (the |
|
| 46 |
#' default), `2` indicates variables/columns. |
|
| 47 |
#' @param axes A length-two [`numeric`] vector giving the dimensions |
|
| 48 |
#' to be for which to compute results. |
|
| 49 |
#' @param sup A [`logical`] scalar: should supplementary points be |
|
| 50 |
#' returned? |
|
| 51 |
#' @param ... Extra parameters to be passed to internal methods. |
|
| 52 |
#' @seealso \link[=mutator]{get_*()}
|
|
| 53 |
#' @example inst/examples/ex-joint.R |
|
| 54 |
#' @author N. Frerebeau |
|
| 55 |
#' @docType methods |
|
| 56 |
#' @family summary |
|
| 57 |
#' @name joint |
|
| 58 |
#' @rdname joint |
|
| 59 |
#' @noRd |
|
| 60 |
NULL |
|
| 61 | ||
| 62 |
joint <- function(object, what, ...) {
|
|
| 63 | 6x |
choices <- c("coordinates", "contributions", "cos2")
|
| 64 | 6x |
what <- match.arg(what, choices = choices, several.ok = FALSE) |
| 65 | ||
| 66 | 6x |
fun <- switch ( |
| 67 | 6x |
what, |
| 68 | 6x |
coordinates = joint_coordinates, |
| 69 | 6x |
contributions = joint_contributions, |
| 70 | 6x |
cos2 = joint_cos2 |
| 71 |
) |
|
| 72 | ||
| 73 | 6x |
fun(object, ...) |
| 74 |
} |
|
| 75 | ||
| 76 |
joint_coordinates <- function(object, ..., margin = 1, axes = c(1, 2), |
|
| 77 |
principal = TRUE) {
|
|
| 78 | 63x |
axes <- axes[c(1, 2)] |
| 79 | 63x |
coord <- get_coordinates(object, margin = margin, principal = principal) |
| 80 | 63x |
rowSums(coord[, axes]^2) |
| 81 |
} |
|
| 82 | ||
| 83 |
joint_contributions <- function(object, ..., margin = 1, axes = c(1, 2)) {
|
|
| 84 | 65x |
axes <- axes[c(1, 2)] |
| 85 | 65x |
contrib <- get_contributions(object, margin = margin) |
| 86 | 65x |
eig <- matrix( |
| 87 | 65x |
data = object@singular_values[axes]^2, |
| 88 | 65x |
nrow = nrow(contrib), |
| 89 | 65x |
ncol = 2, |
| 90 | 65x |
byrow = TRUE |
| 91 |
) |
|
| 92 | 65x |
rowSums(contrib[, axes] * eig) |
| 93 |
} |
|
| 94 | ||
| 95 |
joint_cos2 <- function(object, ..., margin = 1, axes = c(1, 2)) {
|
|
| 96 | 63x |
axes <- axes[c(1, 2)] |
| 97 | 63x |
cos2 <- get_cos2(object, margin = margin) |
| 98 | 63x |
rowSums(cos2[, axes]) |
| 99 |
} |
| 1 |
# GET COORDINATES |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
# Coordinates ================================================================== |
|
| 6 |
#' @export |
|
| 7 |
#' @rdname get_coordinates |
|
| 8 |
#' @aliases get_coordinates,MultivariateAnalysis-method |
|
| 9 |
setMethod( |
|
| 10 |
f = "get_coordinates", |
|
| 11 |
signature = c(x = "MultivariateAnalysis"), |
|
| 12 |
definition = function(x, margin = 1, principal = TRUE, sup_name = ".sup") {
|
|
| 13 | 157x |
arkhe::assert_scalar(margin, "numeric") |
| 14 | 157x |
arkhe::assert_scalar(principal, "logical") |
| 15 | 157x |
arkhe::assert_scalar(sup_name, "character") |
| 16 | ||
| 17 | 157x |
if (margin == 1) {
|
| 18 | 91x |
coords <- x@rows@principal |
| 19 | 91x |
suppl <- x@rows@supplement |
| 20 | 91x |
id <- x@rows@names |
| 21 |
} |
|
| 22 | 157x |
if (margin == 2) {
|
| 23 | 66x |
coords <- x@columns@principal |
| 24 | 66x |
suppl <- x@columns@supplement |
| 25 | 66x |
id <- x@columns@names |
| 26 |
} |
|
| 27 | ||
| 28 |
# P = sqrt(eigenvalue) X S |
|
| 29 | 157x |
if (!principal) {
|
| 30 | 20x |
coords <- t(t(coords) / x@singular_values) |
| 31 |
} |
|
| 32 | ||
| 33 | 157x |
coords <- as.data.frame(coords, row.names = id) |
| 34 | 157x |
coords[[sup_name]] <- suppl |
| 35 | ||
| 36 | 157x |
coords |
| 37 |
} |
|
| 38 |
) |
|
| 39 | ||
| 40 |
#' @export |
|
| 41 |
#' @rdname get_coordinates |
|
| 42 |
#' @aliases get_coordinates,PCOA-method |
|
| 43 |
setMethod( |
|
| 44 |
f = "get_coordinates", |
|
| 45 |
signature = c(x = "PCOA"), |
|
| 46 |
definition = function(x) {
|
|
| 47 | 9x |
as.data.frame(x@points) |
| 48 |
} |
|
| 49 |
) |
|
| 50 | ||
| 51 |
# Replications ================================================================= |
|
| 52 |
#' @export |
|
| 53 |
#' @rdname get_coordinates |
|
| 54 |
#' @aliases get_replications,MultivariateBootstrap-method |
|
| 55 |
setMethod( |
|
| 56 |
f = "get_replications", |
|
| 57 |
signature = c(x = "MultivariateBootstrap"), |
|
| 58 |
definition = function(x, margin = 1) {
|
|
| 59 | ! |
coords <- get_coordinates(x = x, margin = margin) |
| 60 | ||
| 61 | ! |
k <- x@replications |
| 62 | ! |
i <- nrow(coords) / (k + 1) |
| 63 | ! |
j <- ncol(coords) - 1 |
| 64 | ||
| 65 |
## Drop the original data and the last column |
|
| 66 | ! |
repl_coords <- coords[-seq_len(i), seq_len(j)] |
| 67 | ! |
repl <- split(x = repl_coords, f = rep(seq_len(k), each = i)) |
| 68 | ! |
repl <- array(data = unlist(repl), dim = c(i, j, k)) |
| 69 | ! |
rownames(repl) <- rownames(coords)[seq_len(i)] |
| 70 | ! |
colnames(repl) <- colnames(repl_coords) |
| 71 | ! |
repl |
| 72 |
} |
|
| 73 |
) |
|
| 74 | ||
| 75 |
#' @export |
|
| 76 |
#' @rdname get_coordinates |
|
| 77 |
#' @aliases get_replications,BootstrapPCA-method |
|
| 78 |
setMethod( |
|
| 79 |
f = "get_replications", |
|
| 80 |
signature = c(x = "BootstrapPCA"), |
|
| 81 |
definition = function(x) {
|
|
| 82 | ! |
methods::callNextMethod(x = x, margin = 2) |
| 83 |
} |
|
| 84 |
) |
| 1 |
# HELPERS |
|
| 2 | ||
| 3 |
## https://michaelchirico.github.io/potools/articles/developers.html |
|
| 4 |
tr_ <- function(...) {
|
|
| 5 | 71x |
enc2utf8(gettext(paste0(...), domain = "R-dimensio")) |
| 6 |
} |
|
| 7 | ||
| 8 |
recycle <- function(x, n) {
|
|
| 9 | ! |
if (length(x) == 1) rep(x, n) else x |
| 10 |
} |
|
| 11 | ||
| 12 |
print_variance <- function(object, axis) {
|
|
| 13 | 76x |
v <- get_variance(object, digits = 1) # Get percentage of variance |
| 14 | 76x |
sprintf("%s (%g%%)", names(v)[[axis]], v[[axis]])
|
| 15 |
} |
|
| 16 | ||
| 17 |
#' Weighted Column Means and Standard Deviations |
|
| 18 |
#' |
|
| 19 |
#' @param x A [`numeric`] matrix. |
|
| 20 |
#' @param w An [`numeric`] vector. |
|
| 21 |
#' @return A [`numeric`] vector. |
|
| 22 |
#' @keywords internal |
|
| 23 |
#' @noRd |
|
| 24 |
weighted_mean <- function(x, w) {
|
|
| 25 | 14x |
as.vector(crossprod(w, x)) |
| 26 |
} |
|
| 27 |
weighted_sd <- function(x, w) {
|
|
| 28 | 9x |
sqrt(as.vector(crossprod(w, x^2))) |
| 29 |
} |
|
| 30 | ||
| 31 |
#' Column Index |
|
| 32 |
#' |
|
| 33 |
#' @param index A [`numeric`] vector. |
|
| 34 |
#' @param n An [`integer`] value. |
|
| 35 |
#' @param names A [`character`] vector. |
|
| 36 |
#' @return A [`logical`] vector. |
|
| 37 |
#' @keywords internal |
|
| 38 |
#' @noRd |
|
| 39 |
find_variable <- function(index, n, names = NULL) {
|
|
| 40 | 120x |
x <- logical(n) |
| 41 | ||
| 42 | 84x |
if (is.null(index)) return(x) |
| 43 | ||
| 44 | 36x |
if (is.logical(index)) {
|
| 45 | ! |
arkhe::assert_length(index, n) |
| 46 | ! |
return(index) |
| 47 |
} |
|
| 48 | ||
| 49 | 36x |
if (is.character(index)) {
|
| 50 | ! |
index <- match(index, names) |
| 51 | ! |
index <- index[!is.na(index)] |
| 52 | ! |
if (length(index) == 0) return(x) |
| 53 |
} |
|
| 54 | ||
| 55 | 36x |
if (is.numeric(index)) {
|
| 56 | 36x |
x[index] <- TRUE |
| 57 | 36x |
return(x) |
| 58 |
} |
|
| 59 | ||
| 60 | ! |
arkhe::assert_type(index, "numeric") |
| 61 |
} |
|
| 62 | ||
| 63 |
#' Remove Columns Using a Predicate |
|
| 64 |
#' |
|
| 65 |
#' @param x A [`data.frame`]. |
|
| 66 |
#' @param f A predicate [`function`]. |
|
| 67 |
#' @param negate A [`logical`] scalar: should the negation of `f` be used |
|
| 68 |
#' instead of `f`? |
|
| 69 |
#' @param sup A `vector` specifying the indices of the supplementary columns. |
|
| 70 |
#' @param extra A `vector` specifying the indices of the extra columns. |
|
| 71 |
#' @param auto A [`logical`] scalar: should invalid variables be automatically |
|
| 72 |
#' removed? |
|
| 73 |
#' @param what A [`character`] string to be used in the message. |
|
| 74 |
#' @param verbose A [`logical`] scalar: should \R report extra information on |
|
| 75 |
#' progress? |
|
| 76 |
#' @details |
|
| 77 |
#' Side effect: move `sup` and `extra` columns at the end of `x`. |
|
| 78 |
#' @return A `list` with the following elements: `data` (a `matrix`), |
|
| 79 |
#' `sup` (an `integer` vector) and `extra` (a `data.frame` or `NULL`). |
|
| 80 |
#' @keywords internal |
|
| 81 |
#' @noRd |
|
| 82 |
drop_variable <- function(x, f, negate = FALSE, sup = NULL, extra = NULL, |
|
| 83 |
auto = TRUE, what = "extra", |
|
| 84 |
verbose = getOption("dimensio.verbose")) {
|
|
| 85 |
## Check variables |
|
| 86 | 30x |
if (negate) f <- Negate(f) |
| 87 | 32x |
not_ok <- vapply(x, FUN = f, FUN.VALUE = logical(1)) |
| 88 | ||
| 89 |
## Get extra variables |
|
| 90 | 32x |
is_extra <- find_variable(extra, ncol(x), names = colnames(x)) |
| 91 | 32x |
is_sup <- find_variable(sup, ncol(x), names = colnames(x)) |
| 92 | ||
| 93 | 32x |
both <- intersect(which(is_sup), which(is_extra)) |
| 94 | 32x |
if (length(both) > 0) {
|
| 95 | ! |
msg <- tr_("Some supplementary variables are specified twice.")
|
| 96 | ! |
stop(msg, call. = FALSE) |
| 97 |
} |
|
| 98 | ||
| 99 |
## Quit |
|
| 100 | 32x |
if (!auto && any(not_ok & !is_extra)) {
|
| 101 | 2x |
msg <- tr_("Some variables are invalid: %s.")
|
| 102 | 2x |
col <- paste(colnames(x)[not_ok & !is_extra], collapse = ", ") |
| 103 | 2x |
stop(sprintf(msg, col), call. = FALSE) |
| 104 |
} |
|
| 105 | ||
| 106 |
## Extract extra variables, if any |
|
| 107 | 30x |
if (any(is_extra)) {
|
| 108 | 8x |
extra <- x[, is_extra, drop = FALSE] |
| 109 |
} |
|
| 110 | ||
| 111 |
## Remove not OK variables, if any |
|
| 112 | 30x |
tmp <- x |
| 113 | 30x |
x <- x[, !(not_ok | is_extra), drop = FALSE] |
| 114 | ||
| 115 |
## Recompute supplementary variable positions |
|
| 116 | 30x |
is_sup_ok <- is_sup[!(not_ok | is_extra)] |
| 117 | 30x |
sup <- if (any(is_sup_ok)) which(is_sup_ok) else NULL |
| 118 | ||
| 119 |
## Generate message |
|
| 120 | 30x |
if (any(not_ok)) {
|
| 121 | 12x |
not_ok[is_sup | is_extra] <- FALSE |
| 122 | 12x |
if (any(not_ok) && isTRUE(verbose)) {
|
| 123 | 1x |
tot <- sum(not_ok) |
| 124 | 1x |
msg <- ngettext(tot, "%d %s variable was removed: %s.", |
| 125 | 1x |
"%d %s variables were removed: %s.") |
| 126 | 1x |
col <- paste(colnames(tmp)[not_ok], collapse = ", ") |
| 127 | 1x |
message(sprintf(msg, tot, what, col)) |
| 128 |
} |
|
| 129 |
} |
|
| 130 | ||
| 131 | 30x |
list( |
| 132 | 30x |
data = as.matrix(x), |
| 133 | 30x |
sup = sup, |
| 134 | 30x |
extra = extra |
| 135 |
) |
|
| 136 |
} |
| 1 |
# GET CONTRIBUTIONS |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname get_contributions |
|
| 7 |
#' @aliases get_contributions,MultivariateAnalysis-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "get_contributions", |
|
| 10 |
signature = c(x = "MultivariateAnalysis"), |
|
| 11 |
definition = function(x, margin = 1) {
|
|
| 12 | 81x |
arkhe::assert_scalar(margin, "numeric") |
| 13 | ||
| 14 | 49x |
if (margin == 1) contrib <- x@rows@contributions |
| 15 | 32x |
if (margin == 2) contrib <- x@columns@contributions |
| 16 | ||
| 17 | 81x |
as.data.frame(contrib) |
| 18 |
} |
|
| 19 |
) |
| 1 |
# GET EIGENVALUES |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname get_eigenvalues |
|
| 7 |
#' @aliases get_eigenvalues,MultivariateAnalysis-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "get_eigenvalues", |
|
| 10 |
signature = c(x = "MultivariateAnalysis"), |
|
| 11 |
definition = function(x) {
|
|
| 12 | 19x |
eig <- x@singular_values^2 # Eigenvalues |
| 13 | 19x |
pvar <- eig / sum(eig) * 100 # Percentage |
| 14 | 19x |
cvar <- cumsum(pvar) # Cumulative percentage |
| 15 | ||
| 16 | 19x |
z <- data.frame(eig, pvar, cvar) |
| 17 | 19x |
colnames(z) <- c("eigenvalues", "variance", "cumulative")
|
| 18 | 19x |
z |
| 19 |
} |
|
| 20 |
) |
|
| 21 | ||
| 22 |
#' @export |
|
| 23 |
#' @rdname get_eigenvalues |
|
| 24 |
#' @aliases get_eigenvalues,PCOA-method |
|
| 25 |
setMethod( |
|
| 26 |
f = "get_eigenvalues", |
|
| 27 |
signature = c(x = "PCOA"), |
|
| 28 |
definition = function(x) {
|
|
| 29 | 1x |
eig <- x@eigenvalues# Eigenvalues |
| 30 | 1x |
pvar <- eig / sum(eig) * 100 # Percentage |
| 31 | 1x |
cvar <- cumsum(pvar) # Cumulative percentage |
| 32 | ||
| 33 | 1x |
z <- data.frame(eig, pvar, cvar) |
| 34 | 1x |
colnames(z) <- c("eigenvalues", "variance", "cumulative")
|
| 35 | 1x |
z |
| 36 |
} |
|
| 37 |
) |
| 1 |
# PLOT COS2 |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname viz_contributions |
|
| 7 |
#' @aliases viz_cos2,MultivariateAnalysis-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "viz_cos2", |
|
| 10 |
signature = c(x = "MultivariateAnalysis"), |
|
| 11 |
definition = function(x, ..., margin = 2, axes = 1, active = TRUE, |
|
| 12 |
sup = TRUE, sort = TRUE, decreasing = TRUE, |
|
| 13 |
limit = 10, horiz = FALSE, |
|
| 14 |
col = "grey90", border = "grey10") {
|
|
| 15 |
## Prepare data |
|
| 16 | 2x |
data <- prepare_cos2(x, margin = margin, axes = axes, |
| 17 | 2x |
active = active, sup = sup, sort = sort, |
| 18 | 2x |
decreasing = decreasing, limit = limit) |
| 19 | ||
| 20 | 2x |
xx <- paste0("(F", axes, ")", collapse = "-")
|
| 21 | 2x |
ylab <- bquote(paste(plain(cos)^2~.(xx))) |
| 22 | ||
| 23 |
## Bar plot |
|
| 24 | 2x |
mid <- graphics::barplot( |
| 25 | 2x |
height = data$y, |
| 26 | 2x |
names.arg = data$x, |
| 27 | 2x |
horiz = horiz, |
| 28 | 2x |
xlab = if (horiz) ylab else NULL, |
| 29 | 2x |
ylab = if (horiz) NULL else ylab, |
| 30 | 2x |
col = col, |
| 31 | 2x |
border = border, |
| 32 | 2x |
las = 1, |
| 33 |
... |
|
| 34 |
) |
|
| 35 | ||
| 36 | 2x |
invisible(x) |
| 37 |
} |
|
| 38 |
) |
|
| 39 | ||
| 40 |
# Must return a data.frame (`x`, `y`, `label`) |
|
| 41 |
prepare_cos2 <- function(object, margin, axes, active = TRUE, sup = TRUE, |
|
| 42 |
sort = TRUE, decreasing = TRUE, limit = 10) {
|
|
| 43 |
## Get data |
|
| 44 | 2x |
cos2 <- get_cos2(object, margin = margin) |
| 45 | 2x |
if (length(axes) > 1) {
|
| 46 | ! |
values <- joint_cos2(object, margin = margin, axes = axes) |
| 47 |
} else {
|
|
| 48 | 2x |
values <- cos2[[axes[[1]]]] |
| 49 |
} |
|
| 50 | ||
| 51 |
## Prepare data |
|
| 52 | 2x |
data <- data.frame( |
| 53 | 2x |
x = rownames(cos2), |
| 54 | 2x |
y = values, |
| 55 | 2x |
label = round(values, digits = 2) |
| 56 |
) |
|
| 57 | ||
| 58 |
## Subset |
|
| 59 | ! |
if (!active & sup) data <- data[cos2$.sup, ] |
| 60 | ! |
if (active & !sup) data <- data[!cos2$.sup, ] |
| 61 | ||
| 62 |
## Sort data |
|
| 63 | 2x |
if (sort) {
|
| 64 | 2x |
data <- data[order(data$y, decreasing = decreasing), ] |
| 65 |
} |
|
| 66 | ||
| 67 |
## Subset |
|
| 68 | 2x |
if (!is.null(limit)) {
|
| 69 | 2x |
limit <- min(nrow(data), limit) |
| 70 | 2x |
data <- data[seq_len(limit), , drop = FALSE] |
| 71 |
} |
|
| 72 | ||
| 73 |
## Prevent reordering |
|
| 74 | 2x |
data$x <- factor(data$x, levels = unique(data$x)) |
| 75 | ||
| 76 | 2x |
data |
| 77 |
} |
| 1 |
# EXPORT |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
# The -r9X flags specify that the zip command should recursively search |
|
| 6 |
# sub-directories, use maximum compression, and remove depreciated file fields. |
|
| 7 |
# The -j flag allows the file names to be stored rather than the full file path. |
|
| 8 | ||
| 9 |
#' @export |
|
| 10 |
#' @rdname export |
|
| 11 |
#' @aliases export,MultivariateAnalysis-method |
|
| 12 |
setMethod( |
|
| 13 |
f = "export", |
|
| 14 |
signature = c(object = "MultivariateAnalysis"), |
|
| 15 |
definition = function(object, file, flags = "-r9Xjq", ...) {
|
|
| 16 |
## Create temporary directory |
|
| 17 | ! |
dir_path <- tempfile(pattern = "export_") |
| 18 | ! |
dir.create(path = dir_path) |
| 19 | ! |
on.exit(unlink(x = dir_path)) |
| 20 | ||
| 21 |
## Write results |
|
| 22 | ! |
utils::write.csv( |
| 23 | ! |
x = get_data(object), |
| 24 | ! |
file = make_file_name(dir_path, "data") |
| 25 |
) |
|
| 26 | ! |
utils::write.csv( |
| 27 | ! |
x = get_eigenvalues(object), |
| 28 | ! |
file = make_file_name(dir_path, "eigenvalues") |
| 29 |
) |
|
| 30 | ! |
export_results(object, path = dir_path, margin = 1) |
| 31 | ! |
export_results(object, path = dir_path, margin = 2) |
| 32 | ||
| 33 |
## Zip |
|
| 34 | ! |
status <- utils::zip(zipfile = file, files = dir_path, flags = flags, ...) |
| 35 | ! |
invisible(status) |
| 36 |
} |
|
| 37 |
) |
|
| 38 | ||
| 39 |
#' @export |
|
| 40 |
#' @rdname export |
|
| 41 |
#' @aliases export,PCOA-method |
|
| 42 |
setMethod( |
|
| 43 |
f = "export", |
|
| 44 |
signature = c(object = "PCOA"), |
|
| 45 |
definition = function(object, file, flags = "-r9Xjq", ...) {
|
|
| 46 |
## Create temporary directory |
|
| 47 | ! |
dir_path <- tempfile(pattern = "export_") |
| 48 | ! |
dir.create(path = dir_path) |
| 49 | ! |
on.exit(unlink(x = dir_path)) |
| 50 | ||
| 51 |
## Write results |
|
| 52 | ! |
utils::write.csv( |
| 53 | ! |
x = get_coordinates(object), |
| 54 | ! |
file = make_file_name(dir_path, "coordinates") |
| 55 |
) |
|
| 56 | ! |
utils::write.csv( |
| 57 | ! |
x = get_eigenvalues(object), |
| 58 | ! |
file = make_file_name(dir_path, "eigenvalues") |
| 59 |
) |
|
| 60 | ||
| 61 |
## Zip |
|
| 62 | ! |
status <- utils::zip(zipfile = file, files = dir_path, flags = flags, ...) |
| 63 | ! |
invisible(status) |
| 64 |
} |
|
| 65 |
) |
|
| 66 | ||
| 67 |
export_results <- function(object, path, margin, sup_name = ".sup") {
|
|
| 68 |
## Coordinates |
|
| 69 | ! |
coords <- get_coordinates( |
| 70 | ! |
x = object, |
| 71 | ! |
margin = margin, |
| 72 | ! |
principal = TRUE, |
| 73 | ! |
sup_name = sup_name |
| 74 |
) |
|
| 75 | ||
| 76 |
## Contributions |
|
| 77 | ! |
contrib <- get_contributions( |
| 78 | ! |
x = object, |
| 79 | ! |
margin = margin |
| 80 |
) |
|
| 81 | ||
| 82 |
## cos2 |
|
| 83 | ! |
cos2 <- get_cos2( |
| 84 | ! |
x = object, |
| 85 | ! |
margin = margin, |
| 86 | ! |
sup_name = sup_name |
| 87 |
) |
|
| 88 | ||
| 89 |
## Write |
|
| 90 | ! |
utils::write.csv(x = coords, file = make_file_name(path, "coordinates", margin)) |
| 91 | ! |
utils::write.csv(x = contrib, file = make_file_name(path, "contributions", margin)) |
| 92 | ! |
utils::write.csv(x = cos2, file = make_file_name(path, "cos2", margin)) |
| 93 | ||
| 94 | ! |
invisible(NULL) |
| 95 |
} |
|
| 96 | ||
| 97 |
make_file_name <- function(path, name, margin = NULL) {
|
|
| 98 | ! |
prefix <- "" |
| 99 | ! |
if (!is.null(margin) && margin == 1) prefix <- "row_" |
| 100 | ! |
if (!is.null(margin) && margin == 2) prefix <- "col_" |
| 101 | ||
| 102 | ! |
file_name <- paste0(prefix, name, ".csv") |
| 103 | ! |
file_path <- file.path(path, file_name) |
| 104 | ||
| 105 | ! |
file_path |
| 106 |
} |
| 1 |
# TIDY DATA |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname tidy |
|
| 7 |
#' @aliases tidy,MultivariateAnalysis-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "tidy", |
|
| 10 |
signature = c(x = "MultivariateAnalysis"), |
|
| 11 |
definition = function(x, ..., margin = 1, principal = TRUE) {
|
|
| 12 |
## Validation |
|
| 13 | 6x |
arkhe::assert_length(margin, 1) |
| 14 | ||
| 15 |
## Get data |
|
| 16 | 6x |
coords <- get_coordinates(x, margin = margin, principal = principal) |
| 17 | 6x |
coords_long <- cbind(rownames(coords), coords[, ncol(coords)], |
| 18 | 6x |
utils::stack(coords[, -ncol(coords)])) |
| 19 | 6x |
colnames(coords_long) <- c("label", "supplementary", "coordinate", "component")
|
| 20 | ||
| 21 | 6x |
contrib <- get_contributions(x, margin = margin) |
| 22 | 6x |
contrib_long <- cbind(rownames(contrib), utils::stack(contrib)) |
| 23 | 6x |
colnames(contrib_long) <- c("label", "contribution", "component")
|
| 24 | ||
| 25 | 6x |
cos2 <- get_cos2(x, margin = margin) |
| 26 | 6x |
cos2_long <- cbind(rownames(cos2), utils::stack(cos2[, -ncol(cos2)])) |
| 27 | 6x |
colnames(cos2_long) <- c("label", "cos2", "component")
|
| 28 | ||
| 29 |
## Join data |
|
| 30 | 6x |
Reduce( |
| 31 | 6x |
f = function(df1, df2) {
|
| 32 | 12x |
merge(df1, df2, by = c("label", "component"), all = TRUE, sort = TRUE)
|
| 33 |
}, |
|
| 34 | 6x |
x = list(coords_long, contrib_long, cos2_long) |
| 35 |
) |
|
| 36 |
} |
|
| 37 |
) |
| 1 |
# PLOT CONTRIBUTIONS |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname viz_contributions |
|
| 7 |
#' @aliases viz_contributions,MultivariateAnalysis-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "viz_contributions", |
|
| 10 |
signature = c(x = "MultivariateAnalysis"), |
|
| 11 |
definition = function(x, ..., margin = 2, axes = 1, |
|
| 12 |
sort = TRUE, decreasing = TRUE, limit = 10, |
|
| 13 |
horiz = FALSE, col = "grey90", border = "grey10") {
|
|
| 14 |
## Prepare data |
|
| 15 | 4x |
data <- prepare_contrib(x, margin = margin, axes = axes, sort = sort, |
| 16 | 4x |
decreasing = decreasing, limit = limit) |
| 17 | ||
| 18 |
## Expected average contribution |
|
| 19 | 4x |
theo <- 100 / length(data$y) |
| 20 | 4x |
if (length(axes) > 1) {
|
| 21 | 2x |
eig <- get_eigenvalues(x)[axes, 1] |
| 22 | 2x |
theo <- sum(theo * eig) / sum(eig) |
| 23 |
} |
|
| 24 | ||
| 25 |
## Bar plot |
|
| 26 | 4x |
msg <- tr_("Contribution to %s (%%)")
|
| 27 | 4x |
ylab <- sprintf(msg, paste0("F", axes, collapse = "-"))
|
| 28 | 4x |
mid <- graphics::barplot( |
| 29 | 4x |
height = data$y, |
| 30 | 4x |
names.arg = data$x, |
| 31 | 4x |
horiz = horiz, |
| 32 | 4x |
xlab = if (horiz) ylab else NULL, |
| 33 | 4x |
ylab = if (horiz) NULL else ylab, |
| 34 | 4x |
col = col, |
| 35 | 4x |
border = border, |
| 36 | 4x |
las = 1, |
| 37 |
... |
|
| 38 |
) |
|
| 39 | 4x |
graphics::abline(h = theo, lty = 2, col = "red") |
| 40 | ||
| 41 | 4x |
invisible(x) |
| 42 |
} |
|
| 43 |
) |
|
| 44 | ||
| 45 |
# Must return a data.frame (`x`, `y`, `label`) |
|
| 46 |
prepare_contrib <- function(object, margin, axes, sort = TRUE, |
|
| 47 |
decreasing = TRUE, limit = 10) {
|
|
| 48 |
## Get data |
|
| 49 | 4x |
contrib <- get_contributions(object, margin = margin) |
| 50 | 4x |
if (length(axes) > 1) {
|
| 51 | 2x |
values <- joint_contributions(object, margin = margin, axes = axes) |
| 52 |
} else {
|
|
| 53 | 2x |
values <- contrib[[axes[[1]]]] |
| 54 |
} |
|
| 55 | ||
| 56 |
## Prepare data |
|
| 57 | 4x |
data <- data.frame( |
| 58 | 4x |
x = rownames(contrib), |
| 59 | 4x |
y = values, |
| 60 | 4x |
label = round(values, digits = 2) |
| 61 |
) |
|
| 62 | ||
| 63 |
## Sort data |
|
| 64 | 4x |
if (sort) {
|
| 65 | 4x |
data <- data[order(data$y, decreasing = decreasing), ] |
| 66 |
} |
|
| 67 | ||
| 68 |
## Subset |
|
| 69 | 4x |
if (!is.null(limit)) {
|
| 70 | 4x |
limit <- min(nrow(data), limit) |
| 71 | 4x |
data <- data[seq_len(limit), , drop = FALSE] |
| 72 |
} |
|
| 73 | ||
| 74 |
## Prevent reordering |
|
| 75 | 4x |
data$x <- factor(data$x, levels = unique(data$x)) |
| 76 | ||
| 77 | 4x |
data |
| 78 |
} |
| 1 |
# CLASSES DEFINITION |
|
| 2 | ||
| 3 |
# Register S3 classes ========================================================== |
|
| 4 |
setOldClass("dist")
|
|
| 5 | ||
| 6 |
# MultivariateAnalysis ========================================================= |
|
| 7 |
## Results --------------------------------------------------------------------- |
|
| 8 |
#' Multivariate Data Analysis Results |
|
| 9 |
#' |
|
| 10 |
#' An S4 class to store the results of a multivariate data analysis. |
|
| 11 |
#' @slot names A [`character`] vector specifying the row names. |
|
| 12 |
#' @slot principal A [`numeric`] [`matrix`] giving the principal coordinates. |
|
| 13 |
#' @slot standard A [`numeric`] [`matrix`] giving the standard coordinates. |
|
| 14 |
#' @slot contributions A [`numeric`] [`matrix`] giving the contributions to the |
|
| 15 |
#' definition of the dimensions. |
|
| 16 |
#' @slot cosine A [`numeric`] [`matrix`] giving the \eqn{cos^2}{cos2} values.
|
|
| 17 |
#' @slot distances A [`numeric`] vector giving the distances to centroid. |
|
| 18 |
#' @slot weights A [`numeric`] vector giving the masses/weights. |
|
| 19 |
#' @slot supplement A [`logical`] vector specifying the supplementary points. |
|
| 20 |
#' @slot order An [`integer`] vector giving the original indices of the data |
|
| 21 |
#' (computation moves all supplementary points at the end of the results). |
|
| 22 |
#' @slot groups A [`character`] vector specifying the class for each |
|
| 23 |
#' observation. |
|
| 24 |
#' @author N. Frerebeau |
|
| 25 |
#' @family class |
|
| 26 |
#' @docType class |
|
| 27 |
#' @name MultivariateResults |
|
| 28 |
#' @aliases MultivariateResults-class |
|
| 29 |
#' @keywords internal |
|
| 30 |
.MultivariateResults <- setClass( |
|
| 31 |
Class = "MultivariateResults", |
|
| 32 |
slots = c( |
|
| 33 |
names = "character", |
|
| 34 |
principal = "matrix", |
|
| 35 |
standard = "matrix", |
|
| 36 |
contributions = "matrix", |
|
| 37 |
cosine = "matrix", |
|
| 38 |
distances = "numeric", |
|
| 39 |
weights = "numeric", |
|
| 40 |
supplement = "logical", |
|
| 41 |
order = "integer", |
|
| 42 |
groups = "character" |
|
| 43 |
) |
|
| 44 |
) |
|
| 45 | ||
| 46 |
## Output ---------------------------------------------------------------------- |
|
| 47 |
#' Output of Multivariate Data Analysis |
|
| 48 |
#' |
|
| 49 |
#' A virtual S4 class to store the output of a multivariate data analysis. |
|
| 50 |
#' @slot data A [`numeric`] [`matrix`]. |
|
| 51 |
#' @slot dimension An [`integer`] giving the dimension of the solution. |
|
| 52 |
#' @slot singular_values A [`numeric`] vector giving the singular values. |
|
| 53 |
#' @slot rows A [`MultivariateResults-class`] object. |
|
| 54 |
#' @slot columns A [`MultivariateResults-class`] object. |
|
| 55 |
#' @slot extra A [`list`] of extra variables. |
|
| 56 |
#' @section Subset: |
|
| 57 |
#' In the code snippets below, `x` is a `MultivariateAnalysis` object. |
|
| 58 |
#' \describe{
|
|
| 59 |
#' \item{`x[[i]]`}{Extracts information from a slot selected by subscript `i`.
|
|
| 60 |
#' `i` is a length-one [`character`] vector.} |
|
| 61 |
#' } |
|
| 62 |
#' @author N. Frerebeau |
|
| 63 |
#' @family class |
|
| 64 |
#' @docType class |
|
| 65 |
#' @name MultivariateAnalysis |
|
| 66 |
#' @aliases MultivariateAnalysis-class |
|
| 67 |
#' @keywords internal |
|
| 68 |
.MultivariateAnalysis <- setClass( |
|
| 69 |
Class = "MultivariateAnalysis", |
|
| 70 |
slots = c( |
|
| 71 |
data = "matrix", |
|
| 72 |
dimension = "integer", |
|
| 73 |
singular_values = "numeric", |
|
| 74 |
rows = "MultivariateResults", |
|
| 75 |
columns = "MultivariateResults", |
|
| 76 |
extra = "list" |
|
| 77 |
), |
|
| 78 |
contains = "VIRTUAL" |
|
| 79 |
) |
|
| 80 | ||
| 81 |
## Bootstrap ------------------------------------------------------------------- |
|
| 82 |
#' Output of Bootstrap Replications |
|
| 83 |
#' |
|
| 84 |
#' A virtual S4 class to store the output of a bootstrap analysis. |
|
| 85 |
#' @slot replications An [`integer`] giving the number of bootstrap |
|
| 86 |
#' replications. |
|
| 87 |
#' @author N. Frerebeau |
|
| 88 |
#' @family class |
|
| 89 |
#' @docType class |
|
| 90 |
#' @name MultivariateBootstrap |
|
| 91 |
#' @aliases MultivariateBootstrap-class |
|
| 92 |
#' @keywords internal |
|
| 93 |
.MultivariateBootstrap <- setClass( |
|
| 94 |
Class = "MultivariateBootstrap", |
|
| 95 |
slots = c( |
|
| 96 |
replications = "integer" |
|
| 97 |
), |
|
| 98 |
contains = "VIRTUAL" |
|
| 99 |
) |
|
| 100 | ||
| 101 |
## Summary --------------------------------------------------------------------- |
|
| 102 |
#' Summary of Multivariate Data Analysis |
|
| 103 |
#' |
|
| 104 |
#' A virtual S4 class to store the summary of a multivariate data analysis. |
|
| 105 |
#' @slot data A [`numeric`] [`matrix`]. |
|
| 106 |
#' @slot eigenvalues A [`numeric`] [`matrix`]. |
|
| 107 |
#' @slot results A [`numeric`] [`matrix`]. |
|
| 108 |
#' @slot supplement A [`logical`] vector specifying the supplementary points. |
|
| 109 |
#' @slot margin An [`integer`]. |
|
| 110 |
#' @author N. Frerebeau |
|
| 111 |
#' @family class |
|
| 112 |
#' @docType class |
|
| 113 |
#' @name MultivariateSummary |
|
| 114 |
#' @aliases MultivariateSummary-class |
|
| 115 |
#' @keywords internal |
|
| 116 |
.MultivariateSummary <- setClass( |
|
| 117 |
Class = "MultivariateSummary", |
|
| 118 |
slots = c( |
|
| 119 |
data = "matrix", |
|
| 120 |
eigenvalues = "matrix", |
|
| 121 |
results = "matrix", |
|
| 122 |
supplement = "logical", |
|
| 123 |
margin = "integer" |
|
| 124 |
), |
|
| 125 |
contains = "VIRTUAL" |
|
| 126 |
) |
|
| 127 | ||
| 128 |
#' @rdname MultivariateSummary |
|
| 129 |
#' @aliases SummaryCA-class |
|
| 130 |
.SummaryCA <- setClass( |
|
| 131 |
Class = "SummaryCA", |
|
| 132 |
contains = "MultivariateSummary" |
|
| 133 |
) |
|
| 134 | ||
| 135 |
#' @rdname MultivariateSummary |
|
| 136 |
#' @aliases SummaryPCA-class |
|
| 137 |
.SummaryPCA <- setClass( |
|
| 138 |
Class = "SummaryPCA", |
|
| 139 |
contains = "MultivariateSummary" |
|
| 140 |
) |
|
| 141 | ||
| 142 |
# CA =========================================================================== |
|
| 143 |
#' CA Results |
|
| 144 |
#' |
|
| 145 |
#' An S4 class to store the results of a simple correspondence analysis. |
|
| 146 |
#' @note |
|
| 147 |
#' This class inherits from [`MultivariateAnalysis-class`]. |
|
| 148 |
#' @example inst/examples/ex-ca.R |
|
| 149 |
#' @author N. Frerebeau |
|
| 150 |
#' @family class |
|
| 151 |
#' @docType class |
|
| 152 |
#' @exportClass CA |
|
| 153 |
#' @aliases CA-class |
|
| 154 |
#' @keywords internal |
|
| 155 |
.CA <- setClass( |
|
| 156 |
Class = "CA", |
|
| 157 |
contains = "MultivariateAnalysis" |
|
| 158 |
) |
|
| 159 | ||
| 160 |
#' Bootstrap CA Results |
|
| 161 |
#' |
|
| 162 |
#' An S4 class to store the bootstrap of a correspondence analysis. |
|
| 163 |
#' @note |
|
| 164 |
#' This class inherits from [`CA-class`] and [`MultivariateBootstrap-class`]. |
|
| 165 |
#' @example inst/examples/ex-bootstrap.R |
|
| 166 |
#' @author N. Frerebeau |
|
| 167 |
#' @family class |
|
| 168 |
#' @docType class |
|
| 169 |
#' @aliases BootstrapCA-class |
|
| 170 |
#' @keywords internal |
|
| 171 |
.BootstrapCA <- setClass( |
|
| 172 |
Class = "BootstrapCA", |
|
| 173 |
contains = c("MultivariateBootstrap", "CA")
|
|
| 174 |
) |
|
| 175 | ||
| 176 |
# MCA ========================================================================== |
|
| 177 |
#' MCA Results |
|
| 178 |
#' |
|
| 179 |
#' An S4 class to store the results of a multiple correspondence analysis. |
|
| 180 |
#' @note |
|
| 181 |
#' This class inherits from [`CA-class`]. |
|
| 182 |
# @example inst/examples/ex-mca.R |
|
| 183 |
#' @author N. Frerebeau |
|
| 184 |
#' @family class |
|
| 185 |
#' @docType class |
|
| 186 |
#' @exportClass MCA |
|
| 187 |
#' @aliases MCA-class |
|
| 188 |
#' @keywords internal |
|
| 189 |
.MCA <- setClass( |
|
| 190 |
Class = "MCA", |
|
| 191 |
contains = "CA" |
|
| 192 |
) |
|
| 193 | ||
| 194 |
# PCA ========================================================================== |
|
| 195 |
#' PCA Results |
|
| 196 |
#' |
|
| 197 |
#' An S4 class to store the results of a principal components analysis. |
|
| 198 |
#' @slot center A [`numeric`] vector giving the column mean of the initial |
|
| 199 |
#' dataset (active individuals only). |
|
| 200 |
#' @slot scale A [`numeric`] vector giving the column standard deviations of the |
|
| 201 |
#' initial dataset (active individuals only). |
|
| 202 |
#' @note |
|
| 203 |
#' This class inherits from [`MultivariateAnalysis-class`]. |
|
| 204 |
#' @example inst/examples/ex-pca.R |
|
| 205 |
#' @author N. Frerebeau |
|
| 206 |
#' @family class |
|
| 207 |
#' @docType class |
|
| 208 |
#' @exportClass PCA |
|
| 209 |
#' @aliases PCA-class |
|
| 210 |
#' @keywords internal |
|
| 211 |
.PCA <- setClass( |
|
| 212 |
Class = "PCA", |
|
| 213 |
slots = c( |
|
| 214 |
center = "numeric", |
|
| 215 |
scale = "numeric" |
|
| 216 |
), |
|
| 217 |
contains = "MultivariateAnalysis" |
|
| 218 |
) |
|
| 219 | ||
| 220 |
#' Bootstrap PCA Results |
|
| 221 |
#' |
|
| 222 |
#' An S4 class to store the bootstrap of a principal components analysis. |
|
| 223 |
#' @note |
|
| 224 |
#' This class inherits from [`PCA-class`] and [`MultivariateBootstrap-class`]. |
|
| 225 |
#' @example inst/examples/ex-bootstrap.R |
|
| 226 |
#' @author N. Frerebeau |
|
| 227 |
#' @family class |
|
| 228 |
#' @docType class |
|
| 229 |
#' @aliases BootstrapPCA-class |
|
| 230 |
#' @keywords internal |
|
| 231 |
.BootstrapPCA <- setClass( |
|
| 232 |
Class = "BootstrapPCA", |
|
| 233 |
contains = c("MultivariateBootstrap", "PCA")
|
|
| 234 |
) |
|
| 235 | ||
| 236 |
# PCOA ========================================================================= |
|
| 237 |
#' PCoA Results |
|
| 238 |
#' |
|
| 239 |
#' An S4 class to store the results of a principal coordinates analysis. |
|
| 240 |
#' @slot points A `numeric` matrix whose rows give the coordinates of the points |
|
| 241 |
#' chosen to represent the dissimilarities. |
|
| 242 |
#' @slot eigenvalues A [`numeric`] vector giving the eigenvalues computed during |
|
| 243 |
#' the scaling process. |
|
| 244 |
#' @slot method A [`character`] string giving the distance that has been used to |
|
| 245 |
#' create the distance structure. |
|
| 246 |
#' @slot GOF A length-two [`numeric`] vector. |
|
| 247 |
#' @slot groups A [`character`] vector specifying the class for each |
|
| 248 |
#' observation. |
|
| 249 |
#' @example inst/examples/ex-pcoa.R |
|
| 250 |
#' @author N. Frerebeau |
|
| 251 |
#' @family class |
|
| 252 |
#' @docType class |
|
| 253 |
#' @exportClass PCOA |
|
| 254 |
#' @aliases PCOA-class |
|
| 255 |
#' @keywords internal |
|
| 256 |
.PCOA <- setClass( |
|
| 257 |
Class = "PCOA", |
|
| 258 |
slots = c( |
|
| 259 |
points = "matrix", |
|
| 260 |
eigenvalues = "numeric", |
|
| 261 |
GOF = "numeric", |
|
| 262 |
method = "character", |
|
| 263 |
groups = "character" |
|
| 264 |
) |
|
| 265 |
) |
|
| 266 | ||
| 267 |
# Initialize =================================================================== |
|
| 268 |
build_results <- function(names, principal, standard, contributions, |
|
| 269 |
distances, cosine, weights, supplement, |
|
| 270 |
groups = NULL) {
|
|
| 271 |
## /!\ Reorder active/supplementary points /!\ |
|
| 272 |
## Computation moves all supplementary points at the end of the results |
|
| 273 | 46x |
new_i <- seq_len(nrow(principal)) |
| 274 | 46x |
sup_i <- new_i * -1 |
| 275 | 46x |
if (any(supplement)) {
|
| 276 | 16x |
sup_i <- utils::tail(new_i, n = sum(supplement)) |
| 277 | 16x |
new_i <- c(new_i[!supplement], new_i[supplement]) |
| 278 | 16x |
names <- names[new_i] |
| 279 | 16x |
if (length(groups) > 0) {
|
| 280 | ! |
groups <- groups[new_i] |
| 281 |
} |
|
| 282 |
} |
|
| 283 | ||
| 284 |
## Prepare names |
|
| 285 |
# names <- rep(names, length.out = length(supplement)) |
|
| 286 | 46x |
col_names <- paste0("F", seq_len(ncol(principal)))
|
| 287 | 46x |
dim_names0 <- list(names[-sup_i], col_names) |
| 288 | 46x |
dim_names1 <- list(names, col_names) |
| 289 | ||
| 290 |
## Set names |
|
| 291 | 46x |
dimnames(principal) <- dimnames(cosine) <- dim_names1 |
| 292 | 46x |
dimnames(standard) <- dimnames(contributions) <- dim_names0 |
| 293 | 46x |
names(distances) <- names |
| 294 | 46x |
names(weights) <- names[!supplement] |
| 295 | ||
| 296 | 46x |
.MultivariateResults( |
| 297 | 46x |
names = names, |
| 298 | 46x |
principal = principal, |
| 299 | 46x |
standard = standard, |
| 300 | 46x |
contributions = contributions, |
| 301 | 46x |
cosine = cosine, |
| 302 | 46x |
distances = distances, |
| 303 | 46x |
weights = weights, |
| 304 | 46x |
supplement = sort(supplement), |
| 305 | 46x |
order = new_i, |
| 306 | 46x |
groups = groups %||% character(0) |
| 307 |
) |
|
| 308 |
} |
| 1 |
# TOOLS |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname cdt |
|
| 7 |
#' @aliases cdt,matrix-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "cdt", |
|
| 10 |
signature = c(object = "matrix"), |
|
| 11 |
definition = function(object, exclude = NULL, abbrev = TRUE) {
|
|
| 12 |
## Fix colnames |
|
| 13 | 8x |
if (is.null(colnames(object))) {
|
| 14 | ! |
colnames(object) <- paste0("V", seq_len(ncol(object)))
|
| 15 |
} |
|
| 16 | ||
| 17 | 8x |
d <- apply( |
| 18 | 8x |
X = object, |
| 19 | 8x |
MARGIN = 2, |
| 20 | 8x |
FUN = function(cl, exclude) {
|
| 21 | 24x |
cl <- factor(x = cl, exclude = exclude) |
| 22 | 24x |
n <- length(cl) |
| 23 | 24x |
z <- matrix(0, nrow = n, ncol = nlevels(cl)) |
| 24 | 24x |
z[seq_len(n) + n * (unclass(cl) - 1)] <- 1 |
| 25 | 24x |
dimnames(z) <- list(names(cl), levels(cl)) |
| 26 | 24x |
z |
| 27 |
}, |
|
| 28 | 8x |
exclude = exclude, |
| 29 | 8x |
simplify = FALSE |
| 30 |
) |
|
| 31 | 8x |
mtx <- do.call(cbind, d) |
| 32 | ||
| 33 | 8x |
if (!abbrev) {
|
| 34 | 3x |
n <- vapply(X = d, FUN = ncol, FUN.VALUE = integer(1)) |
| 35 | 3x |
colnames(mtx) <- paste(rep(colnames(object), n), colnames(mtx), sep = "_") |
| 36 |
} |
|
| 37 | ||
| 38 | 8x |
mtx |
| 39 |
} |
|
| 40 |
) |
|
| 41 | ||
| 42 |
#' @export |
|
| 43 |
#' @rdname cdt |
|
| 44 |
#' @aliases cdt,data.frame-method |
|
| 45 |
setMethod( |
|
| 46 |
f = "cdt", |
|
| 47 |
signature = c(object = "data.frame"), |
|
| 48 |
definition = function(object, exclude = NULL, abbrev = TRUE) {
|
|
| 49 | 6x |
object <- as.matrix(object) |
| 50 | 6x |
methods::callGeneric(object, exclude = exclude, abbrev = abbrev) |
| 51 |
} |
|
| 52 |
) |
|
| 53 | ||
| 54 |
#' @export |
|
| 55 |
#' @rdname burt |
|
| 56 |
#' @aliases burt,data.frame-method |
|
| 57 |
setMethod( |
|
| 58 |
f = "burt", |
|
| 59 |
signature = c(object = "data.frame"), |
|
| 60 |
definition = function(object, exclude = NULL, abbrev = TRUE) {
|
|
| 61 | 1x |
x <- cdt(object, exclude = exclude, abbrev = abbrev) |
| 62 | 1x |
crossprod(x, x) |
| 63 |
} |
|
| 64 |
) |
| 1 |
# GET INERTIA |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname get_eigenvalues |
|
| 7 |
#' @aliases get_inertia,MultivariateAnalysis-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "get_inertia", |
|
| 10 |
signature = c(x = "MultivariateAnalysis"), |
|
| 11 |
definition = function(x, margin = 1) {
|
|
| 12 | 7x |
arkhe::assert_scalar(margin, "numeric") |
| 13 | ||
| 14 | 7x |
if (margin == 1) {
|
| 15 | 4x |
masses <- x@rows@weights |
| 16 | 4x |
d2 <- x@rows@distances |
| 17 | 4x |
suppl <- x@rows@supplement |
| 18 | 4x |
name <- x@rows@names |
| 19 |
} |
|
| 20 | 7x |
if (margin == 2) {
|
| 21 | 3x |
masses <- x@columns@weights |
| 22 | 3x |
d2 <- x@columns@distances |
| 23 | 3x |
suppl <- x@columns@supplement |
| 24 | 3x |
name <- x@columns@names |
| 25 |
} |
|
| 26 | ||
| 27 | 7x |
i <- masses * d2[!suppl] |
| 28 | 7x |
names(i) <- name[!suppl] |
| 29 | 7x |
i |
| 30 |
} |
|
| 31 |
) |
| 1 |
# PRINCIPAL COORDINATES ANALYSIS |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname pcoa |
|
| 7 |
#' @aliases pcoa,dist-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "pcoa", |
|
| 10 |
signature = c(object = "dist"), |
|
| 11 |
definition = function(object, rank = 2) {
|
|
| 12 |
## Multidimensional scaling |
|
| 13 | 1x |
res <- stats::cmdscale( |
| 14 | 1x |
d = object, |
| 15 | 1x |
k = rank, |
| 16 | 1x |
eig = TRUE, |
| 17 | 1x |
add = FALSE, |
| 18 | 1x |
list. = TRUE |
| 19 |
) |
|
| 20 | ||
| 21 | 1x |
points <- res$points |
| 22 | 1x |
colnames(points) <- paste0("F", seq_len(NCOL(points)))
|
| 23 | ||
| 24 | 1x |
.PCOA( |
| 25 | 1x |
points = points, |
| 26 | 1x |
eigenvalues = res$eig, |
| 27 | 1x |
GOF= res$GOF, |
| 28 | 1x |
method = attr(object, "method") %||% character(0) |
| 29 |
) |
|
| 30 |
} |
|
| 31 |
) |
| 1 |
# GET DATA |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname get_data |
|
| 7 |
#' @aliases get_data,MultivariateAnalysis-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "get_data", |
|
| 10 |
signature = c(x = "MultivariateAnalysis"), |
|
| 11 |
definition = function(x) {
|
|
| 12 | 2x |
as.data.frame(x@data) |
| 13 |
} |
|
| 14 |
) |
| 1 |
# GET COS2 |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname get_contributions |
|
| 7 |
#' @aliases get_cos2,MultivariateAnalysis-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "get_cos2", |
|
| 10 |
signature = c(x = "MultivariateAnalysis"), |
|
| 11 |
definition = function(x, margin = 1, sup_name = ".sup") {
|
|
| 12 | 77x |
arkhe::assert_scalar(margin, "numeric") |
| 13 | 77x |
arkhe::assert_scalar(sup_name, "character") |
| 14 | ||
| 15 | 77x |
if (margin == 1) {
|
| 16 | 45x |
cos2 <- x@rows@cosine |
| 17 | 45x |
suppl <- x@rows@supplement |
| 18 |
} |
|
| 19 | 77x |
if (margin == 2) {
|
| 20 | 32x |
cos2 <- x@columns@cosine |
| 21 | 32x |
suppl <- x@columns@supplement |
| 22 |
} |
|
| 23 | ||
| 24 | 77x |
cos2 <- as.data.frame(cos2) |
| 25 | 77x |
cos2[[sup_name]] <- suppl |
| 26 | ||
| 27 | 77x |
cos2 |
| 28 |
} |
|
| 29 |
) |
| 1 |
# GET DISTANCES |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname get_eigenvalues |
|
| 7 |
#' @aliases get_distances,MultivariateAnalysis-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "get_distances", |
|
| 10 |
signature = c(x = "MultivariateAnalysis"), |
|
| 11 |
definition = function(x, margin = 1) {
|
|
| 12 | 12x |
arkhe::assert_scalar(margin, "numeric") |
| 13 | ||
| 14 | 12x |
if (margin == 1) {
|
| 15 | 9x |
d2 <- x@rows@distances |
| 16 | 9x |
names(d2) <- x@rows@names |
| 17 | 9x |
suppl <- x@rows@supplement |
| 18 |
} |
|
| 19 | 12x |
if (margin == 2) {
|
| 20 | 3x |
d2 <- x@columns@distances |
| 21 | 3x |
names(d2) <- x@columns@names |
| 22 | 3x |
suppl <- x@columns@supplement |
| 23 |
} |
|
| 24 | ||
| 25 | 12x |
d2 |
| 26 |
} |
|
| 27 |
) |
| 1 |
# GET CORRELATIONS |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname get_contributions |
|
| 7 |
#' @aliases get_correlations,PCA-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "get_correlations", |
|
| 10 |
signature = c(x = "PCA"), |
|
| 11 |
definition = function(x, sup_name = ".sup") {
|
|
| 12 | 1x |
arkhe::assert_scalar(sup_name, "character") |
| 13 | ||
| 14 | 1x |
corr <- x@columns@principal / x@columns@distances |
| 15 | 1x |
suppl <- x@columns@supplement |
| 16 | ||
| 17 | 1x |
corr <- as.data.frame(corr) |
| 18 | 1x |
corr[[sup_name]] <- suppl |
| 19 | ||
| 20 | 1x |
corr |
| 21 |
} |
|
| 22 |
) |
| 1 |
# COERCE |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
# To data.frame ================================================================ |
|
| 6 |
#' @export |
|
| 7 |
#' @method as.data.frame MultivariateSummary |
|
| 8 |
as.data.frame.MultivariateSummary <- function(x, row.names = NULL, optional = FALSE, ...) {
|
|
| 9 | ! |
as.data.frame(x@results, row.names = row.names, optional = optional, ...) |
| 10 |
} |
|
| 11 | ||
| 12 |
#' @export |
|
| 13 |
#' @rdname summary |
|
| 14 |
#' @aliases as.data.frame,MultivariateSummary-method |
|
| 15 |
setMethod("as.data.frame", "MultivariateSummary", as.data.frame.MultivariateSummary)
|
|
| 16 | ||
| 17 |
# To list ====================================================================== |
|
| 18 |
#' @method as.list MultivariateResults |
|
| 19 |
as.list.MultivariateResults <- function(x, ...) {
|
|
| 20 | 36x |
list( |
| 21 |
# names = x@names, |
|
| 22 | 36x |
coordinates = x@principal, |
| 23 |
# standard = x@standard, |
|
| 24 | 36x |
contributions = x@contributions, |
| 25 | 36x |
cos2 = x@cosine, |
| 26 |
# distances = x@distances, |
|
| 27 | 36x |
masses = x@weights, |
| 28 | 36x |
supplement = x@supplement |
| 29 |
) |
|
| 30 |
} |
|
| 31 | ||
| 32 |
#' @method as.list MultivariateAnalysis |
|
| 33 |
#' @export |
|
| 34 |
as.list.MultivariateAnalysis <- function(x, ...) {
|
|
| 35 | 18x |
list( |
| 36 | 18x |
data = x@data, |
| 37 | 18x |
rows = as.list(x@rows), |
| 38 | 18x |
columns = as.list(x@columns), |
| 39 | 18x |
eigenvalues = x@singular_values^2 |
| 40 |
) |
|
| 41 |
} |