
Group environments into mega-environments (clusters)
Source:R/env_prediction.R
cluster_environments.RdDelineates mega-environments by clustering the genetic correlation among environments, i.e. how similarly genotypes rank from one environment to another. Environments in which genotypes respond alike are placed in the same cluster, which is the operational definition of a mega-environment in a target population of environments (TPE).
The input is either a fitted kernel_model (carrying the
environment genetic-correlation matrix in $genetic_cor_env, produced
with keep_effects = TRUE) or any square environment correlation matrix
you already have (for example from env_cor or an external GxE
analysis).
Arguments
- object
a
kernel_modelfit carrying$genetic_cor_env(fit withkeep_effects = TRUE), or a square environment-by-environment correlation matrix.- k
integer or NULL. Number of clusters. When
NULL(default) it is chosen by maximum average silhouette width overk_range.- method
character. Clustering algorithm:
"hclust"(default),"pam"or"kmeans".- distance
character. Distance built from the genetic correlation:
"one_minus"(\(1 - r_g\), default) or"sqrt"(\(\sqrt{1 - r_g}\)).- hclust_linkage
character. Linkage passed to
hclustwhenmethod = "hclust". Default"ward.D2".- k_range
integer vector. Candidate numbers of clusters tested when
kisNULL. Default2:6.- seed
integer or NULL. Optional seed for reproducibility of the randomised starts in
"pam"and"kmeans".
Value
A list with:
- clusters
named integer vector giving the cluster of each environment.
- k
the number of clusters used (selected or supplied).
- method
the clustering algorithm used.
- distance
the symmetric distance matrix that was clustered.
- silhouette
per-environment silhouette widths plus their
average(higher is a tighter, better-separated solution).- medoids
representative environment of each cluster (
"pam"/"kmeans";NULLfor"hclust").- hclust
the
hclusttree formethod = "hclust", otherwiseNULL.- table
a data.frame (
env,cluster,sil_width) ordered by cluster and decreasing silhouette width.
Details
The genetic correlation \(r_g\) is turned into a distance and the chosen algorithm is run on that distance:
distance = "one_minus"uses \(d = 1 - r_g\) (default).distance = "sqrt"uses \(d = \sqrt{1 - r_g}\), which spreads out highly correlated environments.
Correlations are clamped to \([-1, 1]\) and the distance matrix is
symmetrised before clustering. Three algorithms are available through
method: agglomerative hierarchical clustering ("hclust",
controlled by hclust_linkage), partitioning around medoids
("pam"), and "kmeans" run on a classical MDS embedding of the
distance matrix.
When k is NULL the number of clusters is selected automatically
by maximising the average silhouette width over the candidate values
in k_range; supplying k skips this search. At least three
environments are required.
See also
kernel_model (fit with keep_effects = TRUE to
obtain $genetic_cor_env), kernel_model_clustered to fit
a model informed by the clusters, env_cluster for clustering
from an environmental covariable matrix, and env_cor.
Examples
## ------------------------------------------------------------------
## 1. Cluster a genetic-correlation matrix directly (no model needed)
## ------------------------------------------------------------------
## Six environments forming two obvious groups
R <- matrix(0.2, 6, 6)
R[1:3, 1:3] <- 0.9
R[4:6, 4:6] <- 0.85
diag(R) <- 1
dimnames(R) <- list(paste0("E", 1:6), paste0("E", 1:6))
## Automatic number of clusters via silhouette width
cl <- cluster_environments(R)
cl$k
#> [1] 4
cl$table
#> env cluster sil_width
#> 1 E1 1 0.875
#> 2 E2 1 0.875
#> 3 E3 1 0.875
#> 4 E4 2 1.000
#> 5 E5 3 1.000
#> 6 E6 4 1.000
cl$silhouette["average"]
#> average
#> 0.9375
## ------------------------------------------------------------------
## 2. Force a specific number of clusters
## ------------------------------------------------------------------
cluster_environments(R, k = 2)$clusters
#> E1 E2 E3 E4 E5 E6
#> 1 1 1 2 2 2
## ------------------------------------------------------------------
## 3. Try different algorithms and distances
## ------------------------------------------------------------------
cluster_environments(R, k = 2, method = "pam", seed = 1)$medoids
#> [1] "E1" "E4"
cluster_environments(R, method = "kmeans", k_range = 2:4, seed = 1)$clusters
#> E1 E2 E3 E4 E5 E6
#> 3 3 3 1 4 2
cluster_environments(R, distance = "sqrt",
hclust_linkage = "complete")$clusters
#> E1 E2 E3 E4 E5 E6
#> 1 1 1 2 3 4
# \donttest{
## ------------------------------------------------------------------
## 4. Full workflow from phenotypes, kernels and a fitted model
## ------------------------------------------------------------------
data("maizeYield"); data("maizeG"); data("maizeWTH")
ECs <- W_matrix(env.data = maizeWTH[maizeWTH$daysFromStart < 100, ],
var.id = c("FRUE", "PETP", "SRAD", "T2M_MAX"))
#> ---------------------------------------------------------------
#> W_matrix -- builds the environmental covariable (W) matrix
#> Last updated at 09/27/2026
#> ---------------------------------------------------------------
#> - summarising weather data into environmental covariables
#> - centring, scaling and quality-controlling W
K <- get_kernel(K_G = list(G = maizeG),
K_E = list(W = env_kernel(env.data = ECs)[[2]]),
data = maizeYield, model = "RNMM")
#> ---------------------------------------------------------------
#> env_kernel -- builds environmental relatedness kernels
#> Last updated at 09/27/2026
#> ---------------------------------------------------------------
#> - computing a single environmental kernel
## keep_effects = TRUE is required for $genetic_cor_env
fit <- kernel_model(y = "value", env = "env", gid = "gid",
data = maizeYield, random = K,
keep_effects = TRUE)
megaenv <- cluster_environments(fit)
megaenv$table
#> env cluster sil_width
#> 1 IP 1 1.0000
#> 2 NM 2 1.0000
#> 4 SE 3 0.9580
#> 3 PM 3 0.9566
#> 5 SO 4 1.0000
## 5. Feed the clusters into a cluster-aware model
fit_cl <- kernel_model_clustered(y = "value", data = maizeYield,
random = K, env = "env", gid = "gid",
clusters = megaenv, use = "block")
# }