From ae845342bb445ebe8fd286ac7a3f744e8c000fae Mon Sep 17 00:00:00 2001 From: PPaccioretti Date: Tue, 28 Jul 2026 17:48:54 -0300 Subject: [PATCH 1/4] Always return spatial PCA results and add spca_biplot() kmspc() computed the spatial PCA results and then discarded them unless all_results = TRUE. Tracing the default call: pca_results starts as NULL, the all_results branch is skipped, the !only_spca_results branch is skipped because only_spca_results defaults to TRUE, and spca_results was only attached to pca_results inside the second all_results branch. The function therefore returned pca_results = NULL with default arguments, even though only_spca_results = TRUE is documented as returning precisely those results. The spatial PCA summary and the loadings are now always returned. The site coordinates on the retained components are returned as well, under `scores`; they were already computed as the input of the clustering step but were not exposed, and without them a biplot cannot be drawn. Adds spca_biplot(), which builds the biplot from a kmspc() result and can colour the sites by any of the computed classifications, so that it is possible to see which variables drive the separation between management zones. It returns a ggplot object and guards the ggplot2 dependency, which stays in Suggests. Note: run devtools::document() to regenerate NAMESPACE and the man pages; the export of spca_biplot() is not yet registered. --- NEWS.md | 12 ++ R/kmspc.R | 33 ++++-- R/spca_biplot.R | 223 ++++++++++++++++++++++++++++++++++++ inst/examples/spca_biplot.R | 15 +++ 4 files changed, 274 insertions(+), 9 deletions(-) create mode 100644 R/spca_biplot.R create mode 100644 inst/examples/spca_biplot.R diff --git a/NEWS.md b/NEWS.md index f7963df..b032b9e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,17 @@ # paar (development version) +* Fixed: `kmspc()` computed the spatial PCA results but discarded them unless + `all_results = TRUE`. With the default arguments `pca_results` came back + empty, even though `only_spca_results = TRUE` is meant to return precisely + those results. The spatial PCA summary and the loadings are now always + returned. +* `kmspc()` also returns `scores`, the site coordinates on the retained + spatial components. These are the values the clustering step operates on and + were previously computed but not exposed. +* New `spca_biplot()`, which draws a biplot of the spatial PCA from a + `kmspc()` result, optionally colouring the sites by one of the computed + classifications. + # paar 1.0.2 * Help pages were revised for clarity, consistency, and improved user guidance [#21](https://github.com/PPaccioretti/paar/issues/21) diff --git a/R/kmspc.R b/R/kmspc.R index 7a34bcc..df818e6 100644 --- a/R/kmspc.R +++ b/R/kmspc.R @@ -67,9 +67,20 @@ #' \item{cluster}{\code{data.frame} with cluster assignments for each evaluated number of clusters} #' \item{indices}{\code{data.frame} with clustering validity indices} #' \item{summaryResults}{\code{data.frame} with clustering metrics (iterations, SSDW)} -#' \item{pca_results}{(optional) PCA and/or spatial PCA summaries depending on arguments} +#' \item{pca_results}{a list with the spatial PCA results, always present: +#' \code{spca_summary_results} (eigenvalues, spatial variance, explained +#' proportion and Moran index per axis), \code{eigenvectors_used} +#' (variable loadings on the retained axes) and \code{scores} (site +#' coordinates on the retained axes, the input of the clustering step). +#' If \code{only_spca_results = FALSE} it also contains +#' \code{pca_results} with the classical PCA summary, and if +#' \code{all_results = TRUE}, the complete \code{pca_results_all} and +#' \code{spca_results_all} objects.} #' } #' +#' @seealso [spca_biplot()] to display `eigenvectors_used` and `scores` +#' together in a biplot. +#' #' @export #' @example inst/examples/kmspc.R #' @@ -221,22 +232,26 @@ kmspc <- function( unlist(propvaracum_ms) )) + data_clust <- ms$li[num_sPC] + + if (inherits(data_clust, "sf")) { + data_clust <- sf::st_drop_geometry(data_clust) + } + spca_results <- list( spca_summary_results = resultado_ms, - eigenvectors_used = ms$c1[, num_sPC, drop = FALSE] + eigenvectors_used = ms$c1[, num_sPC, drop = FALSE], + scores = data_clust ) if (all_results) { spca_results <- append(spca_results, list(spca_results_all = ms)) - - pca_results <- append(pca_results, spca_results) } - data_clust <- ms$li[num_sPC] - - if (inherits(data_clust, "sf")) { - data_clust <- sf::st_drop_geometry(data_clust) - } + # The spatial PCA results are always returned: they are the output the + # `only_spca_results` argument refers to, and `scores` together with + # `eigenvectors_used` are what `spca_biplot()` needs. + pca_results <- append(pca_results, spca_results) my_results <- make_clasification( data_clust, diff --git a/R/spca_biplot.R b/R/spca_biplot.R new file mode 100644 index 0000000..185f57b --- /dev/null +++ b/R/spca_biplot.R @@ -0,0 +1,223 @@ +#' Biplot of a spatial PCA +#' +#' @description +#' Builds a biplot from the spatial principal component analysis computed by +#' [kmspc()]. Sites are drawn as points using their coordinates on the +#' retained spatial components, and variables as arrows from the origin using +#' their loadings. +#' +#' The biplot complements the clustering output: the angles between arrows +#' approximate the correlations among the original variables, the length of an +#' arrow indicates how well the plotted plane represents that variable, and the +#' position of a site relative to an arrow indicates its relative value for +#' that variable. When management zones are mapped onto the points, it also +#' shows which variables drive the separation between zones. +#' +#' @param x a list returned by [kmspc()]. +#' +#' @param axes \code{numeric} vector of length two with the spatial components +#' to be plotted. +#' +#' @param cluster optional. Either a \code{character} with the name of one of +#' the columns of \code{x$cluster} (for example \code{"Cluster_3"}), or a +#' vector with one value per site. Points are coloured according to it. +#' +#' @param arrow_scale \code{numeric}. Factor applied to the loadings so that the +#' arrows are legible next to the cloud of sites. If \code{NULL} (default) it +#' is computed so that the longest arrow spans about 85\% of the point cloud. +#' Because the same factor is applied to every arrow, it does not alter their +#' interpretation. +#' +#' @param point_size,point_alpha \code{numeric}. Size and opacity of the points. +#' +#' @param label_size \code{numeric}. Size of the variable labels. +#' +#' @details +#' Note that the sign of a component is arbitrary: the eigendecomposition fixes +#' the direction of each axis but not its orientation, so two runs may produce +#' mirrored biplots without any change in the conclusions. Interpretations +#' should therefore be phrased in terms of relative positions, that is, which +#' variables oppose each other and which go together, rather than in terms of +#' absolute signs. +#' +#' The correlations suggested by the angles between arrows are computed treating +#' the sites as independent observations. With spatially autocorrelated layers +#' that assumption does not hold, so the magnitudes remain a valid description +#' of the observed association but their statistical significance is +#' misleading. See [spatial_t_test()] for a test that corrects the degrees of +#' freedom by the effective sample size. +#' +#' @return A \code{ggplot} object, which can be further modified with the usual +#' \code{ggplot2} functions. +#' +#' @seealso [kmspc()], [spatial_t_test()] +#' +#' @export +#' @example inst/examples/spca_biplot.R +#' +spca_biplot <- function( + x, + axes = c(1, 2), + cluster = NULL, + arrow_scale = NULL, + point_size = 1, + point_alpha = 0.6, + label_size = 3.2 +) { + if (!requireNamespace("ggplot2", quietly = TRUE)) { + stop( + "Package 'ggplot2' is needed for spca_biplot(). ", + "Please install it with install.packages('ggplot2').", + call. = FALSE + ) + } + + spca <- x[["pca_results"]] + + if (is.null(spca) || is.null(spca[["scores"]])) { + stop( + "'x' does not contain spatial PCA results. ", + "It must be the output of kmspc().", + call. = FALSE + ) + } + + if (length(axes) != 2 || !is.numeric(axes)) { + stop("'axes' must be a numeric vector of length two", call. = FALSE) + } + + scores <- spca[["scores"]] + loadings <- spca[["eigenvectors_used"]] + + if (max(axes) > ncol(scores)) { + stop( + "Only ", + ncol(scores), + " spatial component(s) were retained, so 'axes' cannot exceed that ", + "number. Increase 'explainedVariance' in kmspc() to retain more ", + "components.", + call. = FALSE + ) + } + + sites <- data.frame( + axis_x = scores[[axes[1]]], + axis_y = scores[[axes[2]]] + ) + + arrows <- data.frame( + arrow_x = loadings[[axes[1]]], + arrow_y = loadings[[axes[2]]], + variable = rownames(loadings) + ) + + # The arrows are rescaled so that they are legible next to the cloud of + # sites; a single common factor preserves their relative lengths and angles. + if (is.null(arrow_scale)) { + max_site <- max(abs(c(sites$axis_x, sites$axis_y)), na.rm = TRUE) + max_arrow <- max(abs(c(arrows$arrow_x, arrows$arrow_y)), na.rm = TRUE) + arrow_scale <- if (max_arrow > 0) 0.85 * max_site / max_arrow else 1 + } + + arrows$arrow_x <- arrows$arrow_x * arrow_scale + arrows$arrow_y <- arrows$arrow_y * arrow_scale + + # Optional grouping of the sites + zone <- NULL + if (!is.null(cluster)) { + if (is.character(cluster) && length(cluster) == 1) { + available <- x[["cluster"]] + if (is.null(available)) { + stop("'x' does not contain cluster assignments", call. = FALSE) + } + available <- as.data.frame(available) + if (!cluster %in% names(available)) { + stop( + "'", + cluster, + "' is not one of the available clusterings: ", + paste(names(available), collapse = ", "), + call. = FALSE + ) + } + zone <- available[[cluster]] + } else { + zone <- cluster + } + + # kmspc() keeps the rows dropped for having missing values, so the cluster + # vector may be longer than the matrix of scores. + if (length(zone) != nrow(sites)) { + zone <- stats::na.omit(zone) + } + if (length(zone) != nrow(sites)) { + stop( + "'cluster' has ", + length(zone), + " values but there are ", + nrow(sites), + " sites", + call. = FALSE + ) + } + sites$zone <- as.factor(zone) + } + + explained <- spca[["spca_summary_results"]] + axis_label <- function(i) { + if (!is.null(explained) && "Prop" %in% names(explained)) { + sprintf("%s (%.1f%%)", names(scores)[i], explained[["Prop"]][i]) + } else { + names(scores)[i] + } + } + + out <- ggplot2::ggplot() + + ggplot2::geom_hline(yintercept = 0, colour = "grey80", linewidth = 0.3) + + ggplot2::geom_vline(xintercept = 0, colour = "grey80", linewidth = 0.3) + + if (is.null(zone)) { + out <- out + + ggplot2::geom_point( + data = sites, + ggplot2::aes(x = axis_x, y = axis_y), + colour = "grey60", + size = point_size, + alpha = point_alpha + ) + } else { + out <- out + + ggplot2::geom_point( + data = sites, + ggplot2::aes(x = axis_x, y = axis_y, colour = zone), + size = point_size, + alpha = point_alpha + ) + + ggplot2::labs(colour = "Zone") + } + + out + + ggplot2::geom_segment( + data = arrows, + ggplot2::aes(x = 0, y = 0, xend = arrow_x, yend = arrow_y), + arrow = ggplot2::arrow(length = ggplot2::unit(0.18, "cm")), + colour = "grey15", + linewidth = 0.5 + ) + + ggplot2::geom_text( + data = arrows, + ggplot2::aes(x = arrow_x * 1.12, y = arrow_y * 1.12, label = variable), + size = label_size, + fontface = "bold" + ) + + ggplot2::coord_equal() + + ggplot2::labs(x = axis_label(axes[1]), y = axis_label(axes[2])) + + ggplot2::theme_bw() +} + + +# Columns built inside spca_biplot() and referenced within ggplot2::aes(). +# Declared here so that R CMD check does not report them as undefined globals. +utils::globalVariables( + c("axis_x", "axis_y", "arrow_x", "arrow_y", "variable", "zone") +) diff --git a/inst/examples/spca_biplot.R b/inst/examples/spca_biplot.R new file mode 100644 index 0000000..abdc211 --- /dev/null +++ b/inst/examples/spca_biplot.R @@ -0,0 +1,15 @@ +library(sf) +data(wheat, package = 'paar') + +# Transform the data.frame into a sf object +wheat_sf <- st_as_sf(wheat, coords = c('x', 'y'), crs = 32720) + +# Run the kmspc function +kmspc_results <- kmspc(wheat_sf, number_cluster = 2:4) + +# Biplot of the first two spatial components +spca_biplot(kmspc_results) + +# Colouring the sites by the three-zone classification shows which variables +# drive the separation between management zones +spca_biplot(kmspc_results, cluster = "Cluster_3") From 086837a36445e288759d45aa54956f45212722d0 Mon Sep 17 00:00:00 2001 From: PPaccioretti Date: Tue, 28 Jul 2026 18:24:43 -0300 Subject: [PATCH 2/4] Normalise line endings with .gitattributes Files in the working tree used CRLF while the stored blobs use LF, and there was no .gitattributes to mediate. As a result git reported every tracked file as fully modified, which made diffs and pull request reviews unreadable. This commit touches many files but does not change a single line of code. From here on every diff reflects content only. --- .Rbuildignore | 3 ++- .gitattributes | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 .gitattributes diff --git a/.Rbuildignore b/.Rbuildignore index 6187d6e..b0ca1b0 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -10,4 +10,5 @@ extra/* ^docs$ ^pkgdown$ ^CRAN-SUBMISSION$ -revdep \ No newline at end of file +revdep +^\.gitattributes$ diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..43ab72a --- /dev/null +++ b/.gitattributes @@ -0,0 +1,17 @@ +# Normalise line endings: LF in the repository, native on disk. +# Without this, files edited on Windows show up as entirely rewritten and +# diffs become unreadable. +* text=auto eol=lf + +# Files git must not treat as text +*.png binary +*.jpg binary +*.jpeg binary +*.pdf binary +*.rda binary +*.RData binary +*.rds binary +*.gpkg binary +*.shp binary +*.shx binary +*.dbf binary From fc83d466ac2e8859e863ac4da13abfd05caa0351 Mon Sep 17 00:00:00 2001 From: PPaccioretti Date: Tue, 28 Jul 2026 18:25:56 -0300 Subject: [PATCH 3/4] Register the export of spca_biplot() and add its help page Generated with roxygen2. Note that man/kmspc.Rd is still the previous version: devtools::document() has to be run again so that the revised @return section of kmspc() reaches its help page. --- NAMESPACE | 1 + man/spca_biplot.Rd | 88 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 man/spca_biplot.Rd diff --git a/NAMESPACE b/NAMESPACE index ca9f941..307e1ca 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -9,3 +9,4 @@ export(depurate) export(fuzzy_k_means) export(kmspc) export(spatial_t_test) +export(spca_biplot) diff --git a/man/spca_biplot.Rd b/man/spca_biplot.Rd new file mode 100644 index 0000000..8c1d01d --- /dev/null +++ b/man/spca_biplot.Rd @@ -0,0 +1,88 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/spca_biplot.R +\name{spca_biplot} +\alias{spca_biplot} +\title{Biplot of a spatial PCA} +\usage{ +spca_biplot( + x, + axes = c(1, 2), + cluster = NULL, + arrow_scale = NULL, + point_size = 1, + point_alpha = 0.6, + label_size = 3.2 +) +} +\arguments{ +\item{x}{a list returned by [kmspc()].} + +\item{axes}{\code{numeric} vector of length two with the spatial components +to be plotted.} + +\item{cluster}{optional. Either a \code{character} with the name of one of +the columns of \code{x$cluster} (for example \code{"Cluster_3"}), or a +vector with one value per site. Points are coloured according to it.} + +\item{arrow_scale}{\code{numeric}. Factor applied to the loadings so that the +arrows are legible next to the cloud of sites. If \code{NULL} (default) it +is computed so that the longest arrow spans about 85\% of the point cloud. +Because the same factor is applied to every arrow, it does not alter their +interpretation.} + +\item{point_size, point_alpha}{\code{numeric}. Size and opacity of the points.} + +\item{label_size}{\code{numeric}. Size of the variable labels.} +} +\value{ +A \code{ggplot} object, which can be further modified with the usual + \code{ggplot2} functions. +} +\description{ +Builds a biplot from the spatial principal component analysis computed by +[kmspc()]. Sites are drawn as points using their coordinates on the +retained spatial components, and variables as arrows from the origin using +their loadings. + +The biplot complements the clustering output: the angles between arrows +approximate the correlations among the original variables, the length of an +arrow indicates how well the plotted plane represents that variable, and the +position of a site relative to an arrow indicates its relative value for +that variable. When management zones are mapped onto the points, it also +shows which variables drive the separation between zones. +} +\details{ +Note that the sign of a component is arbitrary: the eigendecomposition fixes +the direction of each axis but not its orientation, so two runs may produce +mirrored biplots without any change in the conclusions. Interpretations +should therefore be phrased in terms of relative positions, that is, which +variables oppose each other and which go together, rather than in terms of +absolute signs. + +The correlations suggested by the angles between arrows are computed treating +the sites as independent observations. With spatially autocorrelated layers +that assumption does not hold, so the magnitudes remain a valid description +of the observed association but their statistical significance is +misleading. See [spatial_t_test()] for a test that corrects the degrees of +freedom by the effective sample size. +} +\examples{ +library(sf) +data(wheat, package = 'paar') + +# Transform the data.frame into a sf object +wheat_sf <- st_as_sf(wheat, coords = c('x', 'y'), crs = 32720) + +# Run the kmspc function +kmspc_results <- kmspc(wheat_sf, number_cluster = 2:4) + +# Biplot of the first two spatial components +spca_biplot(kmspc_results) + +# Colouring the sites by the three-zone classification shows which variables +# drive the separation between management zones +spca_biplot(kmspc_results, cluster = "Cluster_3") +} +\seealso{ +[kmspc()], [spatial_t_test()] +} From 714395f2d400576303034ce8dd39064b679943e4 Mon Sep 17 00:00:00 2001 From: PPaccioretti Date: Fri, 31 Jul 2026 09:18:58 -0300 Subject: [PATCH 4/4] Fixes Principal Components Percentage --- DESCRIPTION | 2 +- NAMESPACE | 2 ++ NEWS.md | 12 ++++++++++++ R/kmspc.R | 25 ++++++++++++++++++------- man/kmspc.Rd | 15 ++++++++++++++- 5 files changed, 47 insertions(+), 9 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 59322b1..5729608 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -21,7 +21,6 @@ Description: Precision agriculture spatial data License: MIT + file LICENSE Encoding: UTF-8 LazyData: true -RoxygenNote: 7.3.3 Imports: data.table, e1071, @@ -43,3 +42,4 @@ Suggests: URL: https://ppaccioretti.github.io/paar/, https://github.com/PPaccioretti/paar VignetteBuilder: knitr, rmarkdown BugReports: https://github.com/PPaccioretti/paar/issues +Config/roxygen2/version: 8.0.0 diff --git a/NAMESPACE b/NAMESPACE index 307e1ca..3fc8680 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,8 +1,10 @@ # Generated by roxygen2: do not edit by hand S3method(cbind,paar) +S3method(print,multispati) S3method(print,paar) S3method(print,summary.paar) +S3method(summary,multispati) S3method(summary,paar) export(compare_zone) export(depurate) diff --git a/NEWS.md b/NEWS.md index b032b9e..fb9aa86 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,17 @@ # paar (development version) +* Fixed: in `spca_summary_results`, the `Prop` column of `kmspc()` was a + proportion between 0 and 1 while `Acum. Prop.` in the same table was a + percentage between 0 and 100. `spca_biplot()` formats `Prop` with a per-cent + sign, so the axis labels reported one hundredth of the explained variability, + for example `CS1 (0.5%)` where the axis actually explains 50.6%. Both columns + are now percentages. The denominator is the total inertia of the analysis + rather than the number of retained eigenvalues, which differ when the data + matrix is rank deficient. The selection of components from + `explainedVariance` is unaffected, as it already operated on the cumulative + percentage. The `Prop` and `Acum. Prop.` columns of the classical PCA summary + returned by `only_spca_results = FALSE` were also proportions and are now + percentages, so that both tables can be read side by side. * Fixed: `kmspc()` computed the spatial PCA results but discarded them unless `all_results = TRUE`. With the default arguments `pca_results` came back empty, even though `only_spca_results = TRUE` is meant to return precisely diff --git a/R/kmspc.R b/R/kmspc.R index df818e6..e64871a 100644 --- a/R/kmspc.R +++ b/R/kmspc.R @@ -69,7 +69,8 @@ #' \item{summaryResults}{\code{data.frame} with clustering metrics (iterations, SSDW)} #' \item{pca_results}{a list with the spatial PCA results, always present: #' \code{spca_summary_results} (eigenvalues, spatial variance, explained -#' proportion and Moran index per axis), \code{eigenvectors_used} +#' percentage of the total inertia, cumulative percentage and Moran index +#' per axis), \code{eigenvectors_used} #' (variable loadings on the retained axes) and \code{scores} (site #' coordinates on the retained axes, the input of the clustering step). #' If \code{only_spca_results = FALSE} it also contains @@ -170,7 +171,9 @@ kmspc <- function( if (!only_spca_results) { autov_pca <- pca$eig - propvar_pca <- autov_pca / sum(autov_pca) + # Percentages, on the same scale as the spatial PCA table below, so that + # both summaries can be read side by side. + propvar_pca <- 100 * autov_pca / sum(autov_pca) propvaracum_pca <- cumsum(propvar_pca) my_pca <- lapply(pca$l1, spdep::moran.mc, lw, 999) @@ -179,7 +182,7 @@ kmspc <- function( nfila_pca <- length(variables) eje_pca <- seq_len(nfila_pca) - resultado_pca = data.frame( + resultado_pca <- data.frame( eje_pca, autov_pca, propvar_pca, @@ -187,7 +190,7 @@ kmspc <- function( my_pca ) resultado_pca$eje_pca <- as.factor(resultado_pca$eje_pca) - names(resultado_pca) = c( + names(resultado_pca) <- c( "Axis", "Eigenvalue", "Prop", @@ -201,8 +204,14 @@ kmspc <- function( invisible(utils::capture.output(resms <- summary(ms))) var_ms <- resms[, 2, drop = F] nfila_ms <- length(ms$eig) - propvar_ms <- var_ms / nfila_ms - propvaracum_ms <- cumsum(propvar_ms) * 100 + + # Both `Prop` and `Acum. Prop.` are expressed as percentages, on the same + # scale as the `explainedVariance` threshold. The denominator is the total + # inertia of the analysis, which equals the number of variables only when + # the data matrix has full rank. + total_inertia <- sum(pca$eig) + propvar_ms <- 100 * var_ms / total_inertia + propvaracum_ms <- cumsum(propvar_ms) eje_ms <- seq_len(nfila_ms) resultado_ms <- @@ -370,7 +379,7 @@ summarize_indices <- function(indices, number_cluster) { } indicesresults <- data.frame(number_cluster, indices, IndN) - names(indicesresults) = c( + names(indicesresults) <- c( "Num. Cluster", "Xie Beni", # "Fukuyama Sugeno", @@ -674,6 +683,7 @@ multispati <- #' @noRd +#' @exportS3Method summary.multispati <- function(object, ...) { norm.w <- function(X, w) { f2 <- function(v) { @@ -746,6 +756,7 @@ summary.multispati <- function(object, ...) { #' @noRd +#' @exportS3Method print.multispati <- function(x, ...) { # cat("Multispati object \n") # cat("class: ") diff --git a/man/kmspc.Rd b/man/kmspc.Rd index 81ef64d..0f305a5 100644 --- a/man/kmspc.Rd +++ b/man/kmspc.Rd @@ -63,7 +63,16 @@ A list with the following elements: \item{cluster}{\code{data.frame} with cluster assignments for each evaluated number of clusters} \item{indices}{\code{data.frame} with clustering validity indices} \item{summaryResults}{\code{data.frame} with clustering metrics (iterations, SSDW)} - \item{pca_results}{(optional) PCA and/or spatial PCA summaries depending on arguments} + \item{pca_results}{a list with the spatial PCA results, always present: + \code{spca_summary_results} (eigenvalues, spatial variance, explained + percentage of the total inertia, cumulative percentage and Moran index + per axis), \code{eigenvectors_used} + (variable loadings on the retained axes) and \code{scores} (site + coordinates on the retained axes, the input of the clustering step). + If \code{only_spca_results = FALSE} it also contains + \code{pca_results} with the classical PCA summary, and if + \code{all_results = TRUE}, the complete \code{pca_results_all} and + \code{spca_results_all} objects.} } } \description{ @@ -116,3 +125,7 @@ wheat_clustered <- cbind(wheat_sf, kmspc_results$cluster) # Plot the results plot(wheat_clustered[, "Cluster_2"]) } +\seealso{ +[spca_biplot()] to display `eigenvectors_used` and `scores` + together in a biplot. +}