cv_knndm: Use the k-fold Nearest Neighbour Distance Matching (kNNDM) to...

View source: R/cv_knndm.R

cv_knndmR Documentation

Use the k-fold Nearest Neighbour Distance Matching (kNNDM) to separate train and test folds

Description

A k-fold version of the Nearest Neighbour Distance Matching algorithm (Linnenbrink et al., 2024). Unlike cv_nndm, which is a leave-one-out (LOO) strategy producing as many folds as there are points, kNNDM produces a small number of k folds (cheap to fit models on) while still matching the nearest neighbour distance distribution function between the test and training data to the one between the target prediction points and the training data.

Usage

cv_knndm(
  x,
  column = NULL,
  r = NULL,
  pred_points = NULL,
  model_domain = NULL,
  k = 5L,
  maxp = 0.5,
  clustering = "blocks",
  space = "geographical",
  hexagon = TRUE,
  keep_blocks = TRUE,
  num_sample = 10000L,
  sampling = "regular",
  nk_len = 100L,
  linkage = "ward.D2",
  scale = TRUE,
  balance = TRUE,
  presence_bg = FALSE,
  biomod2 = TRUE,
  deg_to_metre = 111325,
  seed = NULL,
  num_bins = 4L,
  plot = interactive(),
  report = interactive()
)

Arguments

x

a simple features (sf) or SpatialPoints object of spatial sample data (e.g., species data or ground truth sample for image classification).

column

character (optional). Indicating the name of the column in which response variable (e.g. species data as a binary response i.e. 0s and 1s) is stored. This is used to report whether all folds contain all classes and, when balance = TRUE, to prefer class-complete candidate groupings.

r

a terra SpatRaster object. This defines the area that the model is going to predict; when neither pred_points nor model_domain is supplied, prediction points are sampled from it. It is also required (for the covariates) when space = "feature".

pred_points

a simple features (sf) object of prediction points (optional). If provided, these are used directly as the prediction locations instead of sampling from r or model_domain.

model_domain

an sf polygon of the prediction area (optional). If provided (and pred_points is not), prediction points are sampled from it.

k

integer value. The number of desired folds for cross-validation. The default is k = 5.

maxp

numeric; strictly between 1/k and 1. The maximum proportion of the points allowed in any one fold; groupings that exceed it are discarded.

clustering

character; the method used to group the points before allocating them to folds. One of "blocks" (default; a blockCV spatial-blocks variant), "hierarchical" (hclust), or "kmeans" (kmeans).

space

character; "geographical" (default) matches distances in geographical space, or "feature" matches distances in the (scaled) covariate space of r.

hexagon

logical. Creates hexagonal (default) spatial blocks when clustering = "blocks". If FALSE, square blocks are used.

keep_blocks

logical; if TRUE (default) and the folds were produced by clustering = "blocks", the spatial blocks of the selected grouping are returned in the output (element blocks) so they can be drawn as a background layer by cv_plot. It has no effect for the "hierarchical" and "kmeans" methods, in feature space, or when a random cross-validation is returned; in those cases blocks is NULL.

num_sample

integer; the number of prediction points to sample from r or model_domain when pred_points is not supplied.

sampling

either "regular" (default) or "random" for sampling prediction points.

nk_len

integer; the number of candidate groupings (numbers of clusters / block sizes) to explore.

linkage

character; the agglomeration method passed to hclust when clustering = "hierarchical".

scale

logical; whether to scale the covariates when space = "feature".

balance

logical. When TRUE (default) and column is supplied, class completeness is used as a validity gate to prefer groupings in which every class is present in every test fold (see details). When FALSE, the grouping with the smallest W is returned regardless of class completeness and column only feeds the report.

presence_bg

logical; whether to treat column as species presence-background data (0s for background points and 1s for presences; see ‘Details’). When TRUE, the whole distance matching (the sample-to-sample, prediction-to-sample and test-to-train nearest-neighbour distances, and the grouping search) is computed on the presences only, so the prediction domain is expressed relative to the presence-only training data rather than a random background. Each background point then inherits the fold of its nearest presence. The class-completeness gate of balance is not applied in this mode. Requires a binary numeric column. The default is FALSE.

biomod2

logical. Creates a matrix of folds that can be directly used in the biomod2 package as a CV.user.table for cross-validation.

deg_to_metre

integer. The conversion rate of metres to degree. See the details section for more information.

seed

integer; a random seed for reproducibility.

num_bins

integer; the number of quantile bins used to stratify a continuous numeric column. The default is 4. Set num_bins = NULL to disable binning and treat every unique value as a separate class (the behaviour prior to version 3.3). If quantile breaks are tied, fewer bins may be used. The raw response values are not modified; bins are only used for fold balancing and record summaries.

plot

logical; whether to plot the distance distribution functions. Defaults to interactive().

report

logical; whether to print summary of records in each fold. Defaults to interactive().

Details

The method groups the sample points and assigns whole groups to folds, tuning the degree of aggregation so that the resulting test-to-train nearest neighbour distance distribution (\hat{G}_{j}^{*}) best matches the prediction-to-sample distribution (\hat{G}_{ij}). The match is scored with the Wasserstein-1 distance (W in the output; the area between the two empirical distribution functions), and the grouping with the smallest value is returned. Following Linnenbrink et al. (2024), a one-sided Kolmogorov-Smirnov test is first used to check whether the points are clustered relative to the prediction area; if they are not, a random cross-validation is returned. When a clustering is needed, the intermediate groups are ordered along the first principal component of the coordinates and distributed across the k folds, and only groupings in which no fold holds more than maxp of the data are considered.

Three ways of generating the groups are available via clustering: "hierarchical" and "kmeans" follow Linnenbrink et al. (2024), while "blocks" (the default) is a blockCV-specific variant that groups the points with hexagonal (or square) spatial blocks, in the spirit of cv_spatial. The "blocks" option is a blockCV extension and is not part of the original kNNDM algorithm. When space = "feature" the distances are computed in the (scaled) covariate space of r instead of the geographical space, and "blocks" is not available.

When column is supplied and balance = TRUE, class balance is used as a validity gate during the candidate scan: among groupings with every class present in every test fold, the one with the smallest W is selected. If no class-complete grouping is available, the overall smallest-W grouping is returned and the final records table reports the missing class(es). Note that this gate is weaker than the balancing in cv_spatial: it only ensures every class is represented in every test fold (no empty class), it does not equalise the class counts across folds. With balance = FALSE the grouping with the overall smallest W is always returned and column is used only for the report.

For presence-background data (presence_bg = TRUE), column holds 1 for presences and 0 for background points – locations sampled across the study area to represent the available conditions rather than confirmed absences. The distance matching is then computed on the presences only and each background point inherits the fold of its nearest presence, so the prediction geometry reflects the presence data rather than a random background.

Value

An object of class S3. A list of objects including:

  • folds_list - a list containing the folds. Each fold has two vectors with the training (first) and testing (second) indices

  • folds_ids - a vector of values indicating the number of the fold for each observation (each number corresponds to the same point in x)

  • biomod_table - a matrix with the folds to be used in biomod2 package

  • k - number of the folds

  • column - the name of the column if provided

  • type - the clustering method used (or "random" if a random CV was returned)

  • space - whether distances were matched in geographical or feature space

  • q - the selected number of intermediate groups

  • W - the Wasserstein statistic of the selected folds (lower means a better match)

  • Gij - the prediction-to-sample nearest neighbour distances

  • Gj - the sample-to-sample (leave-one-out) nearest neighbour distances

  • Gjstar - the test-to-train nearest neighbour distances of the selected folds

  • blocks - the spatial blocks of the selected grouping, each tagged with its fold; only when clustering = "blocks" and keep_blocks = TRUE, otherwise NULL

  • records - a table with the number of points in each category of training and testing

References

Linnenbrink, J., Milà, C., Ludwig, M., & Meyer, H. (2024). kNNDM CV: k-fold nearest neighbour distance matching cross-validation for map accuracy estimation. Geoscientific Model Development, 17(15), 5897-5912. https://doi.org/10.5194/gmd-17-5897-2024

Milà, C., Mateu, J., Pebesma, E., & Meyer, H. (2022). Nearest neighbour distance matching leave-one-out cross-validation for map validation. Methods in Ecology and Evolution, 13(6), 1304-1316.

See Also

cv_nndm, cv_spatial, cv_cluster, cv_spatial_autocor, and CAST::knndm() for the authors' CAST implementation of kNNDM. Use cv_plot to visualise, and cv_distance and cv_similarity to evaluate, the folds.

Examples


library(blockCV)

# import presence-absence species data
points <- read.csv(system.file("extdata/", "species.csv", package = "blockCV"))
# make an sf object from data.frame
pa_data <- sf::st_as_sf(points, coords = c("x", "y"), crs = 7845)

# load raster data
path <- system.file("extdata/au/bio_5.tif", package = "blockCV")
covar <- terra::rast(path)

knndm <- cv_knndm(x = pa_data,
                  column = "occ", # optional
                  r = covar,
                  k = 5,
                  num_sample = 5000)



blockCV documentation built on Aug. 20, 2026, 5:10 p.m.