diff --git a/CRAN-SUBMISSION b/CRAN-SUBMISSION
deleted file mode 100644
index e805da4..0000000
--- a/CRAN-SUBMISSION
+++ /dev/null
@@ -1,3 +0,0 @@
-Version: 1.0.1
-Date: 2024-07-27 16:14:14 UTC
-SHA: a9383a8960cbca2dfd399ee24cd8ddb53516dd06
diff --git a/DESCRIPTION b/DESCRIPTION
index 780d07c..59322b1 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,7 +1,7 @@
Package: paar
Type: Package
Title: Precision Agriculture Data Analysis
-Version: 1.0.1
+Version: 1.0.2.9000
Authors@R:
c(
person("Pablo", "Paccioretti", email = "pablopaccioretti@agro.unc.edu.ar",
diff --git a/NEWS.md b/NEWS.md
index 8e36e2b..f7963df 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,4 +1,6 @@
-# (development version)
+# paar (development version)
+
+# paar 1.0.2
* Help pages were revised for clarity, consistency, and improved user guidance [#21](https://github.com/PPaccioretti/paar/issues/21)
* Now Summary index is calculated correctly.
diff --git a/README.Rmd b/README.Rmd
index 3b9426b..96462d0 100644
--- a/README.Rmd
+++ b/README.Rmd
@@ -22,8 +22,7 @@ knitr::opts_chunk$set(
[](https://github.com/PPaccioretti/paar/actions/workflows/R-CMD-check.yaml)
-The goal of `paar` is to provide useful functions for
-precision agriculture spatial data depuration.
+The goal of `paar` is to provide useful tools for cleaning and processing spatial data in precision agriculture.
## Installation
@@ -41,8 +40,8 @@ pak::pkg_install("PPaccioretti/paar")
## Example
-The package has a complete protocol for automating error removal. Default
-values of all functions are optimized for precision agricultural data.
+The package provides a complete protocol for automated error removal.
+Default values of all functions are optimized for precision agriculture data.
```{r example, message=FALSE}
library(paar)
@@ -50,30 +49,27 @@ library(sf)
data("barley", package = 'paar')
```
-`barley` data contains barley grain yield which were obtained using calibrated commercial yield monitors, mounted on combines equipped with DGPS.
+The `barley` dataset contains grain yield data collected were using calibrated commercial yield monitors, mounted on combines equipped with DGPS.
```{r barley}
#Convert barley data to an spatial object
-barley_sf <- st_as_sf(barley,
- coords = c("X", "Y"),
- crs = 32720)
+barley_sf <- st_as_sf(barley, coords = c("X", "Y"), crs = 32720)
barley_dep <-
- depurate(barley_sf,
- "Yield")
+ depurate(barley_sf, "Yield")
# Summary of depurated data
summary(barley_dep)
```
-Spatial yield values before and after depuration process can be plotted
+Spatial yield values before and after the depuration process can be visualized
```{r, fig.height=2, fig.height=5, out.width="50%", fig.show="hold"}
plot(barley_sf["Yield"], main = "Before depuration")
plot(barley_dep$depurated_data["Yield"], main = "After depuration")
```
-Also distribution of yield values can be plotted
+The distribution of yield values can also be compared
```{r, fig.height=2, fig.height=5, out.width="50%", fig.show="hold"}
boxplot(barley_sf[["Yield"]], main = "Before depuration")
diff --git a/README.md b/README.md
index 401cb23..438d26c 100644
--- a/README.md
+++ b/README.md
@@ -11,8 +11,8 @@ experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](h
[](https://github.com/PPaccioretti/paar/actions/workflows/R-CMD-check.yaml)
-The goal of `paar` is to provide useful functions for precision
-agriculture spatial data depuration.
+The goal of `paar` is to provide useful tools for cleaning and
+processing spatial data in precision agriculture.
## Installation
@@ -33,29 +33,27 @@ pak::pkg_install("PPaccioretti/paar")
## Example
-The package has a complete protocol for automating error removal.
-Default values of all functions are optimized for precision agricultural
+The package provides a complete protocol for automated error removal.
+Default values of all functions are optimized for precision agriculture
data.
``` r
library(paar)
library(sf)
+#> Warning: package 'sf' was built under R version 4.5.2
data("barley", package = 'paar')
```
-`barley` data contains barley grain yield which were obtained using
+The `barley` dataset contains grain yield data collected were using
calibrated commercial yield monitors, mounted on combines equipped with
DGPS.
``` r
#Convert barley data to an spatial object
-barley_sf <- st_as_sf(barley,
- coords = c("X", "Y"),
- crs = 32720)
+barley_sf <- st_as_sf(barley, coords = c("X", "Y"), crs = 32720)
barley_dep <-
- depurate(barley_sf,
- "Yield")
+ depurate(barley_sf, "Yield")
#> Concave hull algorithm is computed with
#> concavity = 2 and length_threshold = 0
@@ -67,23 +65,24 @@ summary(barley_dep)
#> 99 (1.3%) 6 (0.081%)
```
-Spatial yield values before and after depuration process can be plotted
+Spatial yield values before and after the depuration process can be
+visualized
``` r
plot(barley_sf["Yield"], main = "Before depuration")
plot(barley_dep$depurated_data["Yield"], main = "After depuration")
```
-
+
-Also distribution of yield values can be plotted
+The distribution of yield values can also be compared
``` r
boxplot(barley_sf[["Yield"]], main = "Before depuration")
boxplot(barley_dep$depurated_data[["Yield"]], main = "After depuration")
```
-
+
## References
diff --git a/cran-comments.md b/cran-comments.md
index 6c4cb92..6f33fc9 100644
--- a/cran-comments.md
+++ b/cran-comments.md
@@ -1,7 +1,13 @@
-## R CMD check results
+## Test environments
+* macOS (devel and release)
+* windows (devel and release)
+* Ubuntu (devel and release)
+
+## Submission note
-0 errors | 0 warnings | 1 note
+* This submission fixes some bugs
+* No reverse dependencies
-* This is a new release.
+## R CMD check results
-We are resubmitting the package after addressing the CRAN comments from the first submission.
+0 errors | 0 warnings | 0 note
\ No newline at end of file
diff --git a/man/compare_zone.Rd b/man/compare_zone.Rd
index bddfe0d..9fe3a83 100644
--- a/man/compare_zone.Rd
+++ b/man/compare_zone.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/compare_zone.R
\name{compare_zone}
\alias{compare_zone}
-\title{Compare spatial zone means}
+\title{Compare means between spatial zones}
\usage{
compare_zone(
data,
@@ -15,25 +15,58 @@ compare_zone(
)
}
\arguments{
-\item{data}{\code{sf} object with zones}
+\item{data}{an \code{sf} object containing the spatial zones}
-\item{variable}{\code{character} or \code{sf} object to use for mean comparison}
+\item{variable}{either:
+\itemize{
+ \item a \code{character} vector with column names in \code{data}, or
+ \item an \code{sf} object with external variables to be compared. In this
+ case, values are spatially joined to \code{data}.
+}}
-\item{zonesCol}{\code{character} colname from data were zone are specified}
+\item{zonesCol}{\code{character}. Column name in \code{data} defining zones}
-\item{alpha}{\code{numeric} Significance level to use for comparison}
+\item{alpha}{\code{numeric}. Significance level for mean comparison}
-\item{join}{function to use for st_join if variable is \code{sf} object}
+\item{join}{function used in \code{sf::st_join} when \code{variable} is an
+external \code{sf} object (default: \code{sf::st_nearest_feature})}
-\item{returnLSD}{\code{logical} when LSD calculates with spatial variance should be returned}
+\item{returnLSD}{\code{logical}. If \code{TRUE}, returns the LSD value used
+for comparisons}
-\item{grid_dim}{\code{numeric} grid dimentins to estimate spatial variance}
+\item{grid_dim}{\code{numeric}. Grid resolution used to estimate spatial
+variance when interpolating external variables. If missing, it is
+automatically determined.}
}
\value{
-\code{list} with differences and descriptive_stat
+A list with:
+\describe{
+ \item{differences}{list of data frames with mean comparisons per variable}
+ \item{descriptive_stat}{data frame with descriptive statistics and spatial variance}
+}
}
\description{
-Compare spatial zone means
+Compares variable means across spatial zones using a spatially-adjusted
+least significant difference (LSD) approach based on kriging variance.
+
+The function accounts for spatial variability by estimating semivariograms
+and deriving a spatial variance component, which is then used to assess
+differences between zone means.
+}
+\details{
+When \code{variable} is an external \code{sf} object, values are interpolated
+using ordinary kriging before comparison. Otherwise, cross-validation of the
+variogram model is used to estimate spatial variance.
+
+Pairwise comparisons between zones are evaluated using a spatially-adjusted
+LSD criterion:
+
+\deqn{LSD = z_{1-\alpha/2} \times \sigma_{spatial}}
+
+where \eqn{\sigma_{spatial}} is derived from kriging variance.
+
+Results are presented using compact letter displays to indicate groups
+of zones that are not significantly different.
}
\examples{
library(sf)
@@ -55,6 +88,6 @@ compare_zone(data_clusters, "Elev", "Cluster_3")
\references{
Paccioretti, P., Córdoba, M., & Balzarini, M. (2020).
FastMapping: Software to create field maps and identify management zones
-in precision agriculture. Computers and Electronics in Agriculture, 175,
-105556 https://doi.org/10.1016/j.compag.2020.105556.
+in precision agriculture. \emph{Computers and Electronics in Agriculture},
+175, 105556. \doi{10.1016/j.compag.2020.105556}
}
diff --git a/man/depurate.Rd b/man/depurate.Rd
index 230d7da..1ca7c2d 100644
--- a/man/depurate.Rd
+++ b/man/depurate.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/depurate.R
\name{depurate}
\alias{depurate}
-\title{Remove errors from spatial data}
+\title{Spatial data depuration (error removal)}
\usage{
depurate(
x,
@@ -21,69 +21,98 @@ depurate(
)
}
\arguments{
-\item{x}{an \code{sf} points object}
+\item{x}{An \code{sf} object with POINT geometries.}
-\item{y}{\code{character} with the name of the variable to use for
-depuration/filtering process}
+\item{y}{A \code{character} string indicating the variable name used
+for filtering. If missing and only one attribute column is present,
+it is used by default.}
-\item{toremove}{\code{character} vector specifying the procedure to
-implement for errors removal. Default 'edges', 'outlier', 'inlier'.
-See Details.}
+\item{toremove}{A \code{character} vector specifying which procedures
+to apply. Options are \code{"edges"}, \code{"outlier"}, and
+\code{"inlier"}. The order of execution is fixed and cannot be modified.}
-\item{crs}{coordinate reference system: integer with the EPSG code,
-or character with proj4string to convert coordinates if \code{x} has
-longitude/latitude data}
+\item{crs}{Coordinate reference system used when transforming
+longitude/latitude data. Can be an EPSG code or proj4string.}
-\item{buffer}{\code{numeric} distance in meters to be removed. Negative
-values are recommended}
+\item{buffer}{A \code{numeric} value indicating the distance (in meters)
+for edge removal. Negative values are recommended to shrink boundaries.}
-\item{ylimitmax}{\code{numeric} of length 1 indicating the maximum limit
-for the \code{y} variable. If \code{NA} \code{Inf} is assumed}
+\item{ylimitmax}{Numeric upper bound for \code{y}. If \code{NA}, \code{Inf}
+is used.}
-\item{ylimitmin}{\code{numeric} of length 1 indicating the minimum limit
-for the \code{y} variable. If \code{NA} \code{-Inf} is assumed}
+\item{ylimitmin}{Numeric lower bound for \code{y}. If \code{NA}, \code{-Inf}
+is used.}
-\item{sdout}{\code{numeric} values outside the interval
-\eqn{mean ± sdout × sdout} values will be removed}
+\item{sdout}{Numeric multiplier for standard deviation used to detect
+global outliers.}
-\item{ldist}{\code{numeric} lower distance bound to identify neighbors}
+\item{ldist}{Numeric lower distance bound for neighborhood definition.}
-\item{udist}{\code{numeric} upper distance bound to identify neighbors}
+\item{udist}{Numeric upper distance bound for neighborhood definition.}
-\item{criteria}{\code{character} with "LM" and/or "MP" for methods to
-identify spatial outliers}
+\item{criteria}{Character vector specifying spatial outlier detection
+methods: \code{"LM"} (Local Moran) and/or \code{"MP"} (Moran Plot).}
-\item{zero.policy}{default NULL, use global option value;
-if FALSE stop with error for any empty neighbors sets,
-if TRUE permit the weights list to be formed with zero-length
-weights vectors}
+\item{zero.policy}{Logical. If \code{TRUE}, allows empty neighbor sets;
+if \code{FALSE}, stops with an error.}
-\item{poly_border}{\code{sf} object with one polygon or NULL. Can be
-the result of \code{concaveman::concaveman}}
+\item{poly_border}{Optional \code{sf} polygon defining field boundaries.
+If \code{NULL}, a hull is computed automatically.}
}
\value{
-an object of class \code{paar} with two elements:
+An object of class \code{paar} (list) with:
\describe{
- \item{depurated_data}{\code{sf} object with the data after the removal
- process}
- \item{condition}{\code{character} vector with the condition of each
- observation}
- }
+ \item{depurated_data}{Filtered \code{sf} object}
+ \item{condition}{Character vector indicating the reason each observation
+ was removed (or \code{NA} if retained)}
+}
}
\description{
-Data can be filtered by null, edge values, global
- outliers and spatial outliers or local defective observations. Default
- values are optimized for precision agricultural data.
+Filters spatial point data by removing erroneous observations based on
+geometric, statistical, and spatial criteria. The function implements a
+sequential depuration workflow commonly used in precision agriculture.
}
\details{
-Possible values for \code{toremove} are one or more elements of:
+The depuration process is applied in a fixed sequence:
+
+\enumerate{
+ \item Edge removal (\code{"edges"})
+ \item Global outlier removal (\code{"outlier"})
+ \item Spatial outlier removal (\code{"inlier"})
+}
+
+The \code{toremove} argument controls which of these steps are applied,
+but **does not modify the order of execution**.
+
+Available procedures are:
+
\describe{
- \item{edges}{All data points for a distance of \code{buffer} m from data
- edges are deleted.}
- \item{outlier}{Values that are outside the mean±\code{sdout} are removed}
- \item{inlier}{Local Moran index of spatial autocorrelation is calculated
- for each datum as a tool to identify inliers}
+ \item{edges}{
+ Removes points located within a specified \code{buffer} distance from
+ the field boundary. The boundary is computed using a concave hull
+ (\code{concaveman}) or a convex hull if the package is not available.
+ }
+
+ \item{outlier}{
+ Removes global outliers based on:
+ \itemize{
+ \item user-defined limits (\code{ylimitmin}, \code{ylimitmax})
+ \item statistical thresholds defined as
+ \eqn{mean \pm sdout \times sd}
+ }
+ }
+
+ \item{inlier}{
+ Identifies and removes spatial outliers using:
+ \itemize{
+ \item Local Moran's I statistic ("LM")
+ \item Moran scatterplot influence ("MP")
+ }
+ }
}
+
+Default parameter values are tuned for precision agriculture datasets
+(e.g., yield maps).
}
\examples{
library(sf)
@@ -103,7 +132,8 @@ depurated_data <- depurated$depurated_data
all_data_condition <- cbind(depurated, barley)
}
\references{
-Vega, A., Córdoba, M., Castro-Franco, M. et al. Protocol for
-automating error removal from yield maps. Precision Agric 20, 1030–1044
-(2019). https://doi.org/10.1007/s11119-018-09632-8
+Vega, A., Córdoba, M., Castro-Franco, M. et al. (2019).
+Protocol for automating error removal from yield maps.
+\emph{Precision Agriculture}, 20, 1030–1044.
+\doi{10.1007/s11119-018-09632-8}
}
diff --git a/man/figures/README-unnamed-chunk-2-1.png b/man/figures/README-unnamed-chunk-2-1.png
index da5b932..f68b4dd 100644
Binary files a/man/figures/README-unnamed-chunk-2-1.png and b/man/figures/README-unnamed-chunk-2-1.png differ
diff --git a/man/figures/README-unnamed-chunk-2-2.png b/man/figures/README-unnamed-chunk-2-2.png
index 2130bfb..c53b8a2 100644
Binary files a/man/figures/README-unnamed-chunk-2-2.png and b/man/figures/README-unnamed-chunk-2-2.png differ
diff --git a/man/figures/README-unnamed-chunk-3-1.png b/man/figures/README-unnamed-chunk-3-1.png
index 9d730d3..1b2cfc8 100644
Binary files a/man/figures/README-unnamed-chunk-3-1.png and b/man/figures/README-unnamed-chunk-3-1.png differ
diff --git a/man/figures/README-unnamed-chunk-3-2.png b/man/figures/README-unnamed-chunk-3-2.png
index d9672bb..a63391e 100644
Binary files a/man/figures/README-unnamed-chunk-3-2.png and b/man/figures/README-unnamed-chunk-3-2.png differ
diff --git a/man/fuzzy_k_means.Rd b/man/fuzzy_k_means.Rd
index 0c7f9a3..a5568ed 100644
--- a/man/fuzzy_k_means.Rd
+++ b/man/fuzzy_k_means.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/fuzzy_k_means.R
\name{fuzzy_k_means}
\alias{fuzzy_k_means}
-\title{Fuzzy k-means clustering}
+\title{Fuzzy k-means clustering (non-spatial)}
\usage{
fuzzy_k_means(
data,
@@ -13,28 +13,51 @@ fuzzy_k_means(
)
}
\arguments{
-\item{data}{sf object}
+\item{data}{an \code{sf} object with point geometries}
-\item{variables}{variables to use for clustering, if missing, all numeric
-variables will be used}
+\item{variables}{\code{character} vector with variable names used for clustering.
+If missing, all numeric variables in \code{data} are used.}
-\item{number_cluster}{\code{numeric} vector with number of final clusters}
+\item{number_cluster}{\code{numeric} vector indicating the number of clusters
+to evaluate (e.g., \code{3:5})}
-\item{fuzzyness}{A number greater than 1 giving the degree of fuzzification.}
+\item{fuzzyness}{\code{numeric} value greater than 1 controlling the degree of
+fuzziness in clustering (see \code{e1071::cmeans})}
-\item{distance}{\code{character} Must be one of the following:
-If "euclidean", the mean square error, if "manhattan", the mean
-absolute error is computed. Abbreviations are also accepted.}
+\item{distance}{\code{character} distance metric for clustering.
+One of \code{"euclidean"} or \code{"manhattan"} (abbreviations allowed)}
}
\value{
-a list with classification results and indices to select best number of
-clusters.
+A list with:
+\describe{
+ \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}
+}
}
\description{
-Performs a vectorized fuzzy k-means clustering, this procedure
-it is not spatial. The function is almost a wrapper of the function cmeans
-from the package e1071. Is intended to be used when `KM-sPC` procedure is not
-possible because data set has only 1 variable.
+Performs fuzzy k-means clustering on tabular data (non-spatial).
+This function is a lightweight wrapper around \code{e1071::cmeans},
+providing a vectorized workflow and clustering quality indices.
+
+It is primarily intended as a fallback method when spatial clustering
+(e.g., \code{kmspc}) cannot be applied, such as when only one variable
+is available.
+}
+\details{
+Missing values are removed prior to clustering. Observations with missing
+values are reintroduced in the output with \code{NA} cluster assignments.
+
+Clustering is performed for each value in \code{number_cluster}, and
+several indices are returned to assist in selecting the optimal number
+of clusters:
+\itemize{
+ \item Xie-Beni index
+ \item Partition coefficient
+ \item Partition entropy
+ \item Summary index
+}
}
\examples{
library(sf)
@@ -65,3 +88,6 @@ wheat_clustered <- cbind(wheat_sf, fuzzy_k_means_results$cluster)
# Plot the results
plot(wheat_clustered[, "Cluster_2"])
}
+\seealso{
+\code{\link{kmspc}}
+}
diff --git a/man/kmspc.Rd b/man/kmspc.Rd
index c686615..81ef64d 100644
--- a/man/kmspc.Rd
+++ b/man/kmspc.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/kmspc.R
\name{kmspc}
\alias{kmspc}
-\title{MULTISPATI-PCA clustering}
+\title{Spatial PCA-based fuzzy clustering (MULTISPATI-PCA)}
\usage{
kmspc(
data,
@@ -20,50 +20,76 @@ kmspc(
)
}
\arguments{
-\item{data}{sf object}
+\item{data}{an \code{sf} object with point geometries}
-\item{variables}{variables to use for clustering, if missing, all numeric
-variables will be used}
+\item{variables}{\code{character} vector with variable names used for clustering.
+If missing, all numeric variables in \code{data} are used.}
-\item{number_cluster}{\code{numeric} vector with number of final clusters}
+\item{number_cluster}{\code{numeric} vector indicating the number of clusters
+to evaluate (e.g., \code{3:5})}
-\item{explainedVariance}{\code{numeric} number in percentage of explained variance
-from PCA analysis to keep and make cluster process}
+\item{explainedVariance}{\code{numeric}. Percentage (0–100) of cumulative
+explained spatial variance used to select spatial principal components.
+Values between 0 and 1 are interpreted as proportions.}
-\item{ldist}{\code{numeric} lower distance bound to identify neighbors}
+\item{ldist, udist}{\code{numeric}. Lower and upper distance thresholds used
+to define spatial neighbors.}
-\item{udist}{\code{numeric} upper distance bound to identify neighbors}
+\item{center}{centering option passed to PCA:
+\describe{
+ \item{TRUE}{center variables by their mean}
+ \item{FALSE}{no centering}
+ \item{numeric}{custom centering vector}
+}}
-\item{center}{a logical or numeric value, centring option
-if TRUE, centring by the mean
-if FALSE no centring
-if a numeric vector, its length must be equal to the number of
-columns of the data frame df and gives the decentring}
+\item{fuzzyness}{\code{numeric} value greater than 1 controlling the degree of
+fuzziness in clustering (see \code{e1071::cmeans})}
-\item{fuzzyness}{A number greater than 1 giving the degree of fuzzification.}
+\item{distance}{\code{character} distance metric for clustering.
+One of \code{"euclidean"} or \code{"manhattan"} (abbreviations allowed)}
-\item{distance}{\code{character} Must be one of the following:
-If "euclidean", the mean square error, if "manhattan", the mean
-absolute error is computed. Abbreviations are also accepted.}
+\item{zero.policy}{Logical. If \code{TRUE}, allows empty neighbor sets;
+if \code{FALSE}, stops with an error.}
-\item{zero.policy}{default NULL, use global option value;
-if FALSE stop with error for any empty neighbors sets,
-if TRUE permit the weights list to be formed with zero-length
-weights vectors}
+\item{only_spca_results}{\code{logical}. If \code{TRUE}, only spatial PCA results
+are returned. If \code{FALSE}, both PCA and spatial PCA summaries are included.}
-\item{only_spca_results}{\code{logical}; should return both PCA and sPCA
-results (\code{FALSE}), or only sPCA results (\code{TRUE})? This can be a
-time consuming process if there are multiple variables.}
-
-\item{all_results}{\code{logical}; should return the results from the
-sPCA and PCA call?}
+\item{all_results}{\code{logical}. If \code{TRUE}, full PCA and spatial PCA
+objects are returned (can increase computation time and memory use).}
}
\value{
-a list with classification results and indices to select best number of
-clusters.
+A list with the following elements:
+\describe{
+ \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}
+}
}
\description{
-MULTISPATI-PCA clustering
+Performs clustering of spatial data using a combination of spatial
+Principal Component Analysis (PCA), and fuzzy k-means clustering.
+
+The workflow consists of:
+\enumerate{
+ \item Dimensionality reduction using spatial PCA
+ \item Selection of components based on explained spatial variance
+ \item Fuzzy clustering over selected components
+}
+}
+\details{
+Spatial relationships are defined using distance-based neighbors
+(\code{spdep::dnearneigh}). These relationships are incorporated into the
+spatial PCA analysis to extract spatially structured components.
+
+Clustering is performed using fuzzy c-means over selected spatial components.
+Several indices are computed to help determine the optimal number of clusters:
+\itemize{
+ \item Xie-Beni index
+ \item Partition coefficient
+ \item Partition entropy
+ \item Summary index (normalized combination)
+}
}
\examples{
library(sf)