| Title: | Fuzzy Spectral Clustering with Variable-Weighted Adjacency Matrices |
|---|---|
| Description: | Implementation of the FVIBES, the Fuzzy Variable-Importance Based Eigenspace Separation algorithm as described in the paper by Ghashti, J.S., Hare, W., and J.R.J. Thompson (2025). Variable-Weighted Adjacency Constructions for Fuzzy Spectral Clustering. Submitted. |
| Authors: | Jesse S. Ghashti [aut, cre], John R.J. Thompson [aut] |
| Maintainer: | Jesse S. Ghashti <[email protected]> |
| License: | GPL-2 |
| Version: | 1.0.0 |
| Built: | 2026-06-07 06:51:19 UTC |
| Source: | https://github.com/ghashti-j/fuzzyspec |
Computes the fraction of correctly classified observations between two label vectors after optimally matching cluster labels using Thresher::matchLabels.
clustering.accuracy(A, B)clustering.accuracy(A, B)
A |
An integer or character vector of cluster labels of length |
B |
An integer or character vector of cluster labels of length |
The function creates the contingency table table(A, B), permutes columns to best align labels using matchLabels,
and returns the sum of the diagonal divided by .
Inputs must have equal length and the same number of unique labels; otherwise an error is given.
A single numeric value in : the accuracy after optimal label matching.
K. R. Coombes (2025). Thresher: Threshing and Reaping for Principal Components. R package version 1.1.5.
fuzzy.spectral.clustering, gen.fuzzy, plot.fuzzy, matchLabels
set.seed(1) n <- 200 k <- 3 A <- sample.int(k, n, replace = TRUE) # assumed true clustering labels perm <- sample.int(k) # assumed predicted labels (sampled by permutating) B <- perm[A] flips <- sample.int(n, 20) # add some error a few errors B[flips] <- sample.int(k, length(flips), replace = TRUE) clustering.accuracy(A, B)set.seed(1) n <- 200 k <- 3 A <- sample.int(k, n, replace = TRUE) # assumed true clustering labels perm <- sample.int(k) # assumed predicted labels (sampled by permutating) B <- perm[A] flips <- sample.int(n, 20) # add some error a few errors B[flips] <- sample.int(k, length(flips), replace = TRUE) clustering.accuracy(A, B)
Derives pointwise scale parameters from a distance matrix,
based on the -th nearest neighbour distances. This is useful for
constructing the adaptive similarity graphs proposed by Zelnik-Manor and Perona (2004).
compute.sigma(distance, r = NULL)compute.sigma(distance, r = NULL)
distance |
An |
r |
Integer valued neighbourhood radius. If |
For each observation , the function sorts the distances distance[i, ], excludes the zero self-distance, and takes the smallest value, where reflects the distance to the -th nearest neighbour for observation .
A list with components:
sigma |
A numeric vector of length |
radius |
The neighborhood radius |
Ghashti, J. S., Hare, W., and J. R. J. Thompson (2025). Variable-weighted adjacency constructions for fuzzy spectral clustering. Submitted.
Zelnik-Manor, L. and P. Perona (2004). Self-tuning spectral clustering. Advances in Neural Information Processing Systems, 17.
make.adjacency, gen.fuzzy, plot.fuzzy, rNN.dist, find.radius,
compute.SNN, fuzzy.spectral.clustering
set.seed(1) X <- matrix(rnorm(50), nrow = 10) D <- as.matrix(dist(X)) res <- compute.sigma(D) # automatically determine r res$sigma res$radius res2 <- compute.sigma(D, r = 3) # user-specified r res2$sigmaset.seed(1) X <- matrix(rnorm(50), nrow = 10) D <- as.matrix(dist(X)) res <- compute.sigma(D) # automatically determine r res$sigma res$radius res2 <- compute.sigma(D, r = 3) # user-specified r res2$sigma
Builds a Shared-Nearest-Neighbours (SNN) similarity matrix from an input similarity matrix similarity. For each pair of observations, the SNN score is the fraction of shared indices among their top- neighbour lists.
compute.SNN(similarity, r)compute.SNN(similarity, r)
similarity |
An |
r |
Integer value number of nearest neighbours per observation used to compute SNN overlap. |
For each observation , the function forms its neighbour set by ordering
similarity[i, ] in decreasing order, dropping i itself, and
retaining the first indices. For a pair , the SNN similarity
is
i.e., the size of
the intersection of their neighbour sets divided by . The result is
symmetric with ones on the diagonal.
An symmetric numeric matrix SNN.S with entries in .
Ghashti, J. S., Hare, W., and J. R. J. Thompson (2025). Variable-weighted adjacency constructions for fuzzy spectral clustering. Submitted.
Jarvis, R. A., and A. E. Patrick (1973). Clustering using a similarity measure based on shared near neighbors. IEEE Transactions on Computers, 22(11), 1025-1034.
make.adjacency, gen.fuzzy, plot.fuzzy, rNN.dist, find.radius,
compute.sigma, compute.SNN, fuzzy.spectral.clustering
set.seed(1) X <- matrix(rnorm(50), nrow = 10) D <- as.matrix(dist(X)) r <- 3 S <- exp(-D^2) SNN <- compute.SNN(S, r) head(SNN, 5) # inspect average SNN similarity to nearest neighbour rowMeans(SNN - diag(diag(SNN)))set.seed(1) X <- matrix(rnorm(50), nrow = 10) D <- as.matrix(dist(X)) r <- 3 S <- exp(-D^2) SNN <- compute.SNN(S, r) head(SNN, 5) # inspect average SNN similarity to nearest neighbour rowMeans(SNN - diag(diag(SNN)))
Computes fuzzy generalizations of the Adjusted Rand Index based on Frobenius inner products of membership matrices. These measures extends the Adjusted Rand Index to compare fuzzy partitions.
fari(a, b)fari(a, b)
a |
An |
b |
An |
A single numeric value
fari |
The Frobenius Adjusted Rand index between |
Andrews, J.L., Browne, R. and C.D. Hvingelby (2022). On Assessments of Agreement Between Fuzzy Partitions. Journal of Classification, 39, 326–342.
J.L. Andrews, FARI (2013). GitHub repository, https://github.com/its-likeli-jeff/FARI
set.seed(1) a <- matrix(runif(600), nrow = 200, ncol = 3) a <- a / rowSums(a) b <- matrix(runif(600), nrow = 200, ncol = 3) b <- b / rowSums(b) fari(a, b)set.seed(1) a <- matrix(runif(600), nrow = 200, ncol = 3) a <- a / rowSums(a) b <- matrix(runif(600), nrow = 200, ncol = 3) b <- b / rowSums(b) fari(a, b)
Implements a Natural Neighbour search to adaptively determine a neighbourhood
radius from a general distance matrix. The algorithm increases
until the number of points with zero in-degree in the -nearest-neighbour
graph no longer decreases, and returns this radius .
find.radius(D)find.radius(D)
D |
An |
This procedure is adapted from the Natural Neighbor (NaN) algorithm by Zhu, Feng and Huang (2016). The algorithm works as follows:
For each integer , build the directed -nearest-neighbour graph from D.
Compute the in-degree counts, i.e., how many times each observation
appears among others' first neighbours.
Track the number of zero in-degree points; if this number stops
decreasing when increases, the algorithm stops and returns the
current , interpreted as the natural neighbour radius.
This provides a parameter-free way to adaptively set the neighbourhood size.
A single integer , the selected neighbourhood radius.
Zhu, Q., Feng, J., and J. Huang (2016). Natural neighbor: A self-adaptive neighborhood method without parameter K. Pattern recognition letters, 80, 30-36.
make.adjacency, gen.fuzzy, plot.fuzzy, rNN.dist,
compute.sigma, compute.SNN, fuzzy.spectral.clustering
set.seed(1) X <- matrix(rnorm(100), nrow = 20) D <- as.matrix(dist(X)) r <- find.radius(D) # Estimate the natural neighbour radius r rNN.dist(D, r) # use selected r for rNN.distset.seed(1) X <- matrix(rnorm(100), nrow = 20) D <- as.matrix(dist(X)) r <- find.radius(D) # Estimate the natural neighbour radius r rNN.dist(D, r) # use selected r for rNN.dist
Implementation of the FVIBES algorithm by Ghashti, Hare, and Thompson (2025). Performs spectral clustering on a similarity (adjacency) matrix and returns either fuzzy c-means memberships or Gaussian mixture posterior probabilities computed on the leading normalized eigenvectors.
fuzzy.spectral.clustering(W = NULL, k = NULL, m = NULL, method = "CM", nstart = 10, max.iter = 1000)fuzzy.spectral.clustering(W = NULL, k = NULL, m = NULL, method = "CM", nstart = 10, max.iter = 1000)
W |
A nonnegative |
k |
Integer number of clusters. Required. |
m |
Fuzzy parameter for c-means, only used when |
method |
Clustering method applied to the spectral embedding with |
nstart |
Number of random starts for |
max.iter |
Maximum number of iterations for |
Let be the diagonal degree matrix with . The routine forms the symmetrically normalized similarity
(Ng, Jordan, and Weiss, 2001) computes its top eigenvectors, stacks them in , and row-normalizes to with
. Clustering is then performed in the rows of .
When method = "CM", clustering uses c-means (Bezdek, 1981) with fclust::FKM on with fuzzy parameter m, number of starts nstart, and maximum iterations max.iter.
When method = "GMM", clustering uses Gaussian mixture models (see McLachlan and Krishnan, 2008) with mclust::Mclust with G = k on .
A list with components:
cluster |
An integer vector of length |
u |
An |
evecs |
The |
centers |
Cluster centers for the embedding matrix |
J.C. Bezdek (1981). Pattern Recognition with Fuzzy Objective Function Algorithms. Plenum Press, New York.
Ferraro, M.B., Giordani, P., and A. Serafini (2019). fclust: An R Package for Fuzzy Clustering. The R Journal, 11.
Ghashti, J. S., Hare, W., and J. R. J. Thompson (2025). Variable-weighted adjacency constructions for fuzzy spectral clustering. Submitted.
McLachlan, G. and T. Krishnan (2008). The EM algorithm and extensions, Second Edition. John Wiley & Sons.
Ng, A., Jordan, M., and Y. Weiss (2001). On spectral clustering: Analysis and an algorithm. Advances in Neural Information Processing Systems, 14.
Scrucca, L., Fraley, C., Murphy, T.B., and A. E. Raftery (2023). Model-Based Clustering, Classification, and Density Estimation Using mclust in R. Chapman & Hall.
make.adjacency, gen.fuzzy, plot.fuzzy, rNN.dist, find.radius,
compute.sigma, compute.SNN, npudensbw, FKM, Mclust
set.seed(1) d <- gen.fuzzy(n = 300, dataset = "spirals", noise = 0.18) plot.fuzzy(d) # visualize data generating process adj <- make.adjacency(data = d$X, method = "vw", isLocWeighted = TRUE, isModWeighted = FALSE, isSparse = FALSE, ModMethod = NULL, scale = FALSE, sig = 1, radius = NULL, cv.method = "cv.ls") # vwla-id from paper spectRes <- fuzzy.spectral.clustering(W = adj, k = 3, m = 1.5, method = "CM", nstart = 50, max.iter = 1000) head(spectRes$u) # first 6 rows of U plotDf <- list( X = d$X, y = factor(spectRes$cluster), U = spectRes$u, k = 3 ) plot.fuzzy(plotDf) # visualize results clustering.accuracy(d$y, spectRes$cluster) # compare resultsset.seed(1) d <- gen.fuzzy(n = 300, dataset = "spirals", noise = 0.18) plot.fuzzy(d) # visualize data generating process adj <- make.adjacency(data = d$X, method = "vw", isLocWeighted = TRUE, isModWeighted = FALSE, isSparse = FALSE, ModMethod = NULL, scale = FALSE, sig = 1, radius = NULL, cv.method = "cv.ls") # vwla-id from paper spectRes <- fuzzy.spectral.clustering(W = adj, k = 3, m = 1.5, method = "CM", nstart = 50, max.iter = 1000) head(spectRes$u) # first 6 rows of U plotDf <- list( X = d$X, y = factor(spectRes$cluster), U = spectRes$u, k = 3 ) plot.fuzzy(plotDf) # visualize results clustering.accuracy(d$y, spectRes$cluster) # compare results
Simulates several 2D datasets together with fuzzy cluster memberships . The memberships are defined by analytic density/curve proximity rules (detailed below) so they can be used as "ground truth" for fuzzy clustering and visualization (see plot.fuzzy).
gen.fuzzy(n = 500, dataset = c("gaussian", "hyperbolas", "spirals", "wedges", "rings", "worms", "random"), k = NULL, noise = 0.1, covType = c("spherical", "diagonal", "rotated", "correlated"), seed = NULL)gen.fuzzy(n = 500, dataset = c("gaussian", "hyperbolas", "spirals", "wedges", "rings", "worms", "random"), k = NULL, noise = 0.1, covType = c("spherical", "diagonal", "rotated", "correlated"), seed = NULL)
n |
Total number of observations. |
dataset |
Which data generator to use, with options |
k |
Number of clusters for |
noise |
Additive noise or curve-thickness parameter for applicable generators (see Details). |
covType |
Covariance structure for |
seed |
Optional seed for reproducibility. |
Let be the simulated observations and the fuzzy memberships. For each dataset, memberships are defined below and row-normalized to sum to 1.
gaussian (k = 3).
Three Gaussian components with means , , and covariances , and .
If component sizes are , then .
hyperbolas (k = 5).
One Gaussian near and four hyperbola branches
and its rotated or flipped analogues, sampled along
with noise. For observation , , and
, where
is minimum distance to branch for curve . We set .
spirals (k = 3).
Three spirals generated by
with shifts , with additive noise. For each spiral ,
, where is the parameterized spiral curve described above, and .
Note, if , set with and
normalize after.
wedges (k = 8).
Eight angular wedges with inner/outer radii and , respectively, with small gaps between wedges. For observation with radius and angle ,
membership to wedge is ,
where is a wrapped angular distance to the wedge centre angle .
rings (k = 3).
For and , there are three concentric rings with radii
with widths for . Let
, then .
worms (k = 4).
Each worm is a sinusoidal curve parameterized on by with , , with amplitudes ,
frequencies , phases , and vertical offsets .
For observation , the distance to worm is
. Then
, and .
random (k is user-specified).
Mixture of Gaussians with common covariance determined by covType with random centres in and random cluster sizes. With mixture weights ,
A list with components:
X |
An |
U |
An |
y |
A vector length |
k |
Number of clusters. |
centres, clusSz, covMatrix
|
Returned only for |
The noise argument is used by "gaussian", "hyperbolas", "spirals", "rings", and "worms"; it is ignored by "wedges".
set.seed(1) g <- gen.fuzzy(n = 600, dataset = "gaussian", seed = 1) plot.fuzzy(g, plotFuzzy = TRUE, colorCluster = TRUE) s <- gen.fuzzy(n = 450, dataset = "spirals", noise = 0.2, seed = 1) plot.fuzzy(s, plotFuzzy = TRUE, colorCluster = FALSE) r <- gen.fuzzy(n = 800, dataset = "random", k = 15, covType = "rotated", seed = 1) plot.fuzzy(r, plotFuzzy = TRUE, colorCluster = TRUE)set.seed(1) g <- gen.fuzzy(n = 600, dataset = "gaussian", seed = 1) plot.fuzzy(g, plotFuzzy = TRUE, colorCluster = TRUE) s <- gen.fuzzy(n = 450, dataset = "spirals", noise = 0.2, seed = 1) plot.fuzzy(s, plotFuzzy = TRUE, colorCluster = FALSE) r <- gen.fuzzy(n = 800, dataset = "random", k = 15, covType = "rotated", seed = 1) plot.fuzzy(r, plotFuzzy = TRUE, colorCluster = TRUE)
Builds an adjacency matrix from data using Euclidean or variable-weighted distances, with optional locally-adaptive scalings and optional variable weighting by shared-nearest-neighbours (SNN), similarity ranks (SIM), or both. The options reproduce a family of adjacency matrices including the variants described by Ghashti, Hare and Thompson (2025).
make.adjacency(data, method = "vw", isLocWeighted = FALSE, isModWeighted = FALSE, isSparse = FALSE, ModMethod = NULL, scale = FALSE, sig = 1, radius = NULL, cv.method = "cv.ls")make.adjacency(data, method = "vw", isLocWeighted = FALSE, isModWeighted = FALSE, isSparse = FALSE, ModMethod = NULL, scale = FALSE, sig = 1, radius = NULL, cv.method = "cv.ls")
data |
Numeric matrix or data frame of size |
method |
Distance construction |
isLocWeighted |
Logical. If |
isModWeighted |
Logical. If |
isSparse |
Logical. If |
ModMethod |
One of |
scale |
Logical; standardize columns of |
sig |
Positive numeric value for global kernel width, used only when |
radius |
Integer |
cv.method |
Bandwidth selector for |
Step 1: Distance.
method="eu": .
method="vw": compute product-kernel bandwidths h via np::npudensbw, set feature-weights , rescale data as , then . (Variable-weighted metric.)
Step 2:Similarity kernel.
Locally-adaptive scaling from Zelnik-Manor: if isLocWeighted=TRUE, compute as the distance to the -th neighbour with compute.sigma and set
Global scale: if isLocWeighted=FALSE,
Step 3: Weighting Matrix (optional).
Let be the shared--NN overlap fraction (see compute.SNN) for observations and , and
be the -largest entry of observation in matrix . Define .
For isModWeighted=TRUE we have the following options
ModMethod="snn":
ModMethod="sim":
ModMethod="both":
The returned adjacency is when isModWeighted = TRUE, otherwise .
These choices align with the table of named adjacency matrices (see Mapping below).
An numeric adjacency matrix with ones on the diagonal.
Relating to the paper by Ghashti, Hare, and Thompson (2025), let "vw" denote the variable-weighted distance method="vw" and "eu" for the traditional squared Euclidean distance; "la" denotes locally-adaptive scaling when isLocWeighted=TRUE (Zelnik-Manor and Perona, 2004);
"id" denotes identity for isModWeighted = FALSE, and "sim", "snn" and "simsnn" denote weightings for described above.
To reproduce results from the 2025 paper, below are a few examples of adjacency construction:
vw-id: with method="vw", isLocWeighted=FALSE, isModWeighted=FALSE.
vwla-id: with method="vw", isLocWeighted=TRUE, isModWeighted=FALSE.
vw-sim: with method="vw", isLocWeighted=FALSE, isModWeighted=TRUE, ModMethod="sim", isSparse=FALSE.
vw-snns: with method="vw", isLocWeighted=FALSE, isModWeighted=TRUE, ModMethod="snn", isSparse=TRUE.
vw-simsnns: with method="vw", isLocWeighted=FALSE, isModWeighted=TRUE, ModMethod="both", isSparse=TRUE.
Also note that:
If radius is NULL, is chosen adaptively via find.radius on the constructed distance matrix.
method="vw" requires npudensbw for variable weighted bandwidths, with default np::npudensbw(data, bwmethod = cv.method, nmulti = 3) (Hayfield and Racine, 2008).
When r is determined by find.radius, we implement a modified version of the Natural Neighbors algorithm from Zhu et al. (2016).
SNN is a modified version of the Shared Nearest Neighbors algorithm from Jarvis and Patrick (1973).
More information on locally-adaptive scalings are seen in Zelnik-Manor and Perona (2004).
Ghashti, J. S., Hare, W., and J. R. J. Thompson (2025). Variable-weighted adjacency constructions for fuzzy spectral clustering. Submitted.
Hayfield, T., and J. S. Racine (2008). Nonparametric Econometrics: The np Package. Journal of Statistical Software 27(5).
Jarvis, R. A., and A. E. Patrick (1973). Clustering using a similarity measure based on shared near neighbors. IEEE Transactions on Computers, 22(11), 1025-1034.
Zelnik-Manor, L., and P. Perona (2004). Self-tuning spectral clustering. Advances in Neural Information Processing Systems, 17.
Zhu, Q., Feng, J., and J. Huang (2016). Natural neighbor: A self-adaptive neighborhood method without parameter K. Pattern Recognition Letters, 80, 30-36.
gen.fuzzy, plot.fuzzy, rNN.dist, find.radius,
compute.sigma, compute.SNN, fuzzy.spectral.clustering, npudensbw
set.seed(1) X <- scale(matrix(rnorm(200), 100, 2)) W1 <- make.adjacency(X, method = "eu", isLocWeighted = TRUE) # "eula-id" named adjacency W2 <- make.adjacency(X, method = "vw", isLocWeighted = TRUE) # "vwla-id" named adjacency # compare W(xi,xj) i,j = 1,...,5 for eu/vw pair W1 and W2 W1[1:5,1:5] W2[1:5,1:5] W3 <- make.adjacency(X, method = "eu", isLocWeighted = TRUE, isModWeighted = TRUE, ModMethod = "snn", isSparse = FALSE) # "eula-snn" named adjacency W4 <- make.adjacency(X, method = "vw", isLocWeighted = TRUE, isModWeighted = TRUE, ModMethod = "snn", isSparse = FALSE) # "vwla-snn" named adjacency # compare W(xi,xj) i,j = 1,...,5 for eu/vw pair W3 and W4 W3[1:5,1:5] W4[1:5,1:5]set.seed(1) X <- scale(matrix(rnorm(200), 100, 2)) W1 <- make.adjacency(X, method = "eu", isLocWeighted = TRUE) # "eula-id" named adjacency W2 <- make.adjacency(X, method = "vw", isLocWeighted = TRUE) # "vwla-id" named adjacency # compare W(xi,xj) i,j = 1,...,5 for eu/vw pair W1 and W2 W1[1:5,1:5] W2[1:5,1:5] W3 <- make.adjacency(X, method = "eu", isLocWeighted = TRUE, isModWeighted = TRUE, ModMethod = "snn", isSparse = FALSE) # "eula-snn" named adjacency W4 <- make.adjacency(X, method = "vw", isLocWeighted = TRUE, isModWeighted = TRUE, ModMethod = "snn", isSparse = FALSE) # "vwla-snn" named adjacency # compare W(xi,xj) i,j = 1,...,5 for eu/vw pair W3 and W4 W3[1:5,1:5] W4[1:5,1:5]
Creates a ggplot of 2D points with optional colouring by hard labels and optional observation-size mapping to fuzzy uncertainty.
## S3 method for class 'fuzzy' plot(x, plotFuzzy = TRUE, colorCluster = TRUE, ...)## S3 method for class 'fuzzy' plot(x, plotFuzzy = TRUE, colorCluster = TRUE, ...)
x |
A list as returned by |
plotFuzzy |
Logical; if |
colorCluster |
Logical; if |
... |
Additional arguments (currently unused). |
The plotting aesthetics can be modified as follows:
If plotFuzzy and colorCluster are both TRUE (default), the plot contains cluster coloured observations that are size scaled by uncertainty.
If only plotFuzzy is TRUE, the plot contains monochrome coloured observations that are size scaled by uncertainty.
If only colorCluster is TRUE, the plot contains cluster coloured observations with fixed size.
If plotFuzzy and colorCluster are both FALSE, the plot is monochrome coloured observations with fixed size.
A ggplot object.
H. Wickham (2016). ggplot2: Elegant Graphics for Data Analysis. Springer–Verlag New York.
set.seed(1) d1 <- gen.fuzzy(n = 600, dataset = "gaussian", seed = 1) p1 <- plot.fuzzy(d1) p1 # default p2 <- plot.fuzzy(d1, plotFuzzy = TRUE, colorCluster = FALSE) p2 # only uncertainty sizing, monochrome p3 <- plot.fuzzy(d1, plotFuzzy = FALSE, colorCluster = TRUE) p3 # only coloured by cluster, no uncertainty sizingset.seed(1) d1 <- gen.fuzzy(n = 600, dataset = "gaussian", seed = 1) p1 <- plot.fuzzy(d1) p1 # default p2 <- plot.fuzzy(d1, plotFuzzy = TRUE, colorCluster = FALSE) p2 # only uncertainty sizing, monochrome p3 <- plot.fuzzy(d1, plotFuzzy = FALSE, colorCluster = TRUE) p3 # only coloured by cluster, no uncertainty sizing
Given a symmetric distance matrix, returns the indices of the nearest neighbours for each observation.
rNN.dist(D, r)rNN.dist(D, r)
D |
An |
r |
Integer indicating the number of nearest neighbours to extract for each observation. |
For each row of D, the function orders the distances ,
excludes the self-distance, and returns the indices of the first r smallest distances.
This provides the indices of the r nearest neighbours of observation .
An integer matrix, where row contains the indices of the r nearest neighbours of observation .
Ghashti, J. S., Hare, W., and J. R. J. Thompson (2025). Variable-weighted adjacency constructions for fuzzy spectral clustering. Submitted.
make.adjacency, gen.fuzzy, plot.fuzzy, find.radius,
compute.sigma, compute.SNN, fuzzy.spectral.clustering
set.seed(1) X <- matrix(rnorm(20), nrow = 5) D <- as.matrix(dist(X)) rNN.dist(D, r = 2) # find 2 nearest neighbours for each rowset.seed(1) X <- matrix(rnorm(20), nrow = 5) D <- as.matrix(dist(X)) rNN.dist(D, r = 2) # find 2 nearest neighbours for each row