ndr() estimatesGeneralized network-based dimensionality reduction has three linked stages:
Squaring lets community detection use relationship strength. The unsquared association is retained for interpretation: positive edges are green, negative edges are red, and absolute magnitude controls width. Community membership is encoded by node colour only; indicators use one common shape by default. This visual grammar remains feasible when a model contains many communities.
The association method and conditioning type are independent choices.
cor_method supports Pearson, Spearman, Kendall, distance,
Gaussian-rank, and biweight associations. cor_type supports
full, partial, and semipartial forms.
fit_full <- ndr(
swiss, cor_method = "pearson", cor_type = "full",
centrality = "pagerank", seed = 42
)
fit_partial <- ndr(
swiss, cor_method = "pearson", cor_type = "partial",
centrality = "pagerank", seed = 42
)
fit_semipartial <- ndr(
swiss, cor_method = "pearson", cor_type = "semipartial",
centrality = "authority", seed = 42
)
comparison <- data.frame(
network = c("full", "partial", "semipartial"),
factors = c(fit_full$factors, fit_partial$factors,
fit_semipartial$factors),
retained = c(sum(fit_full$membership > 0),
sum(fit_partial$membership > 0),
sum(fit_semipartial$membership > 0)),
directed = c(fit_full$stats$directed,
fit_partial$stats$directed,
fit_semipartial$stats$directed)
)
comparison
#> network factors retained directed
#> 1 full 1 6 FALSE
#> 2 partial 2 6 FALSE
#> 3 semipartial 2 6 TRUEPartial association asks whether two indicators remain related after the others are controlled on both sides. Semipartial association controls one side and may be directed; hub and authority centralities can therefore answer different questions.
The full Pearson fit provides a compact example of the fitted object.
fit_full
#> Generalized network-based dimensionality reduction
#> Call:
#> ndr(r = swiss, cor_method = "pearson", cor_type = "full", seed = 42,
#> centrality = "pagerank")
#>
#> Factors: 1 | Observations: 47 | Modularity: 0
#> Association: full pearson | Centrality: pagerank | Null model: none
#> Community sizes: 6
#>
#> Loadings:
#> NDA1
#> Fertility 0.2349525
#> Agriculture -0.0149876
#> Examination 0.2113338
#> Education 0.3749632
#> Catholic 0.4953636
#> Infant.Mortality 0.4135369
#>
#> Communalities:
#> Fertility Agriculture Examination Education
#> 0.0552027 0.0002246 0.0446620 0.1405974
#> Catholic Infant.Mortality
#> 0.2453851 0.1710128
summary(fit_full)
#> Generalized network-based dimensionality reduction
#> Call:
#> ndr(r = swiss, cor_method = "pearson", cor_type = "full", seed = 42,
#> centrality = "pagerank")
#>
#> Factors: 1 | Observations: 47 | Modularity: 0
#> Association: full pearson | Centrality: pagerank | Null model: none
#> Community sizes: 6
#>
#> Loadings:
#> NDA1
#> Fertility 0.2349525
#> Agriculture -0.0149876
#> Examination 0.2113338
#> Education 0.3749632
#> Catholic 0.4953636
#> Infant.Mortality 0.4135369
#>
#> Communalities:
#> Fertility Agriculture Examination Education
#> 0.0552027 0.0002246 0.0446620 0.1405974
#> Catholic Infant.Mortality
#> 0.2453851 0.1710128
fit_full$membership
#> [1] 1 1 1 1 1 1
round(fit_full$loadings, 3)
#> NDA1
#> Fertility 0.235
#> Agriculture -0.015
#> Examination 0.211
#> Education 0.375
#> Catholic 0.495
#> Infant.Mortality 0.414
round(fit_full$communality, 3)
#> Fertility Agriculture Examination Education
#> 0.055 0.000 0.045 0.141
#> Catholic Infant.Mortality
#> 0.245 0.171
fit_full$stats$community_sizes
#>
#> 1
#> 6membership identifies the score to which an indicator
contributes. loadings connect indicators to the resulting
scores; communality describes how well each indicator is
represented. A zero membership means exclusion.
For a confirmatory illustration, a fixed two-community structure makes the network and biplot stable across community-detection backends.
visual_fit <- ndr(
swiss,
cor_method = "pearson",
membership = c(1, 1, 2, 2, 2, 0),
centrality = "eigenvector",
seed = 42
)
data.frame(
indicator = colnames(visual_fit$R),
community = unname(visual_fit$membership)
)
#> indicator community
#> 1 Fertility 1
#> 2 Agriculture 1
#> 3 Examination 2
#> 4 Education 2
#> 5 Catholic 2
#> 6 Infant.Mortality 0new_scores <- predict(visual_fit, swiss[1:5, , drop = FALSE])
round(new_scores, 3)
#> NDA1 NDA2
#> Courtelary -0.412 -0.344
#> Delemont 0.482 -0.568
#> Franches-Mnt 0.795 -0.797
#> Moutier 0.383 -0.654
#> Neuveville 0.137 -0.056
identical(predict(visual_fit), visual_fit$scores)
#> [1] TRUEPrediction reuses the training means, scales, indicator weights, centrality weights, latent centring, latent scaling, and rotation. Consequently, scores for new observations remain on the fitted model’s coordinate system.
With covar = TRUE, ndr() accepts a square
matrix directly. This makes the method usable beyond correlation
matrices. The example below follows the clustering use described in
Kosztyán (2023): Euclidean distances between
the standardized Swiss observations are converted to similarities.
scaled_swiss <- scale(swiss)
distance_matrix <- as.matrix(dist(scaled_swiss))
similarity_matrix <- 1 - normalize(distance_matrix)
diag(similarity_matrix) <- 1
cluster_fit <- ndr(
similarity_matrix,
covar = TRUE,
min_comm = 2,
centrality = "eigenvector",
seed = 42
)
cluster_fit$factors
#> [1] 2
table(cluster_fit$membership)
#>
#> 1 2
#> 30 17
head(cluster_fit$membership, 10)
#> [1] 1 2 2 2 1 2 2 2 2 2
is.null(cluster_fit$scores)
#> [1] TRUEHere the nodes are observations rather than indicators. The returned membership is therefore an observation clustering. Since an arbitrary matrix contains no original row-level transformation, latent scores for new rows are not available in this mode.
cluster_network <- plot(
cluster_fit,
cuts = 0.55,
interactive = FALSE,
show_weights = FALSE,
shape = "automatic",
vertex.label.size = 7,
layout = "fr"
)
cluster_networkObservation clusters obtained from a normalized Euclidean similarity matrix.
static_network <- plot(
visual_fit,
cuts = 0.3,
interactive = FALSE,
show_weights = TRUE,
shape = "automatic"
)
static_networkNDR association network: colour denotes community, edge colour denotes sign, and width denotes absolute association.
All observed indicators use the same shape. The two communities differ only by node colour. Solid green and red edges distinguish positive and negative associations; their thickness is scaled between the requested minimum and maximum widths.
Two-factor NDR biplot for the fixed Swiss-data communities.
Nearby observation points have similar score profiles. Loading arrows indicate the direction of increasing indicator values. The graph explains association structure; the biplot explains observations and indicators in latent-score space. Neither should be read as a causal model.
The optional style controls can be demonstrated on the same fitted network. All observed indicators remain rectangles, and community uses colour only. Shapes are supplied in association-matrix column order.
custom_network <- plot(
visual_fit, interactive = FALSE,
shape = rep("rectangle", ncol(visual_fit$R)),
positive.color = "#007A53", negative.color = "#B71C1C",
positive.lty = 1, negative.lty = 1,
edge.width.range = c(1, 5), show_weights = TRUE
)
custom_networkExplicit common node shape, custom signed colours, and bounded edge widths.
For a four-node graph, an individual shape vector could instead be
c("circle", "circle", "square", "square"); shapes are not
assigned by community. Optional positive.lty and
negative.lty control line types. Both default to 1 (solid).
Widths are scaled within the displayed graph; separate graphs require a
common scale for direct width comparisons.
Association tests and bootstrap intervals can be executed on a small
example without dominating build time. The low replicate count below
illustrates the returned structure; final inference should use a larger
n_boot.
inference_fit <- ndr(
swiss,
cor_method = "pearson",
test = TRUE,
p_adjust = "BH",
alpha = 0.10,
bootstrap = TRUE,
n_boot = 30,
conf = 0.90,
seed = 42
)
round(inference_fit$correlation_p, 3)
#> Fertility Agriculture Examination Education Catholic
#> Fertility 0.000 0.022 0.000 0.000 0.002
#> Agriculture 0.022 0.000 0.000 0.000 0.009
#> Examination 0.000 0.000 0.000 0.000 0.000
#> Education 0.000 0.000 0.000 0.000 0.377
#> Catholic 0.002 0.009 0.000 0.377 0.000
#> Infant.Mortality 0.007 0.684 0.514 0.543 0.325
#> Infant.Mortality
#> Fertility 0.007
#> Agriculture 0.684
#> Examination 0.514
#> Education 0.543
#> Catholic 0.325
#> Infant.Mortality 0.000
head(inference_fit$stats$bootstrap$lower)
#> NDA1
#> Fertility -0.15056415
#> Agriculture -0.32327584
#> Examination -0.01199384
#> Education 0.23361287
#> Catholic 0.26218893
#> Infant.Mortality 0.03305719
head(inference_fit$stats$bootstrap$upper)
#> NDA1
#> Fertility 0.3687283
#> Agriculture 0.2347789
#> Examination 0.5330512
#> Education 0.6611118
#> Catholic 0.6178982
#> Infant.Mortality 0.5202130The principal filtering controls are min_R,
min_evalue, min_communality,
com_communalities, and min_comm. The
weight argument supplies one finite score weight per
indicator. These filters act at different stages: network formation,
centrality screening, representation, loading separation, and minimum
module size.
sparse_fit <- ndr(
swiss,
min_R = 0.15,
sparsecalc = TRUE,
seed = 42
)
inherits(sparse_fit$R, "sparseMatrix")
#> [1] TRUE
sparse_fit$factors
#> [1] 2
sparse_fit$stats$community_sizes
#>
#> 1 2
#> 3 3Sparse storage is helpful after thresholding a large network.
Parallel workers mainly benefit pairwise distance association and
bootstrap calculations; small Pearson examples usually run faster
sequentially. A fixed seed makes the stochastic parts
reproducible.
The cited literature covers the original NDA method, the generalized association-matrix formulation, the clustering interpretation, the software interface, and the supervised extension (Kosztyán et al. 2022, 2024; Kosztyán 2023; Kosztyán 2026b, 2026a).
ndr() estimates