This document illustrates how Mandala connects a single-stage MET analysis with a two-stage workflow. The same breeding-analysis grammar is retained while the analysis moves from plot-level observations to environment-level genotype BLUEs and their stage-1 covariance matrix.
library(mandala)
path <- system.file("extdata", "fullrep_MET_n1000.csv", package = "mandala", mustWork = TRUE)
met <- read.csv(path, stringsAsFactors = FALSE, check.names = FALSE)
for (v in c("geno", "env", "rep", "block", "row", "col")) met[[v]] <- factor(met[[v]])
met$yld <- as.numeric(met$yld)fit_single <- mandala(
fixed = yld ~ env,
random = ~ geno + geno:env + env:rep:block,
data = met,
verbose = FALSE
)
summary(fit_single)
#> Model statement:
#> mandala(fixed = yld ~ env, random = ~geno + geno:env + env:rep:block,
#> data = met, verbose = FALSE)
#>
#> Variance Components:
#> component estimate std.error z.ratio bound %ch
#> geno 0.43593219 0.09472589 4.602038 P NA
#> geno:env 0.07591478 0.02730378 2.780376 P NA
#> env:rep:block 0.08022915 0.02319411 3.459032 P NA
#> R.sigma2 0.48914215 0.03214592 15.216304 P NA
#>
#> Fixed Effects (BLUEs) [first 5]:
#> effect estimate std.error z.ratio
#> (Intercept) 7.0907140 0.1699720 41.716954
#> envY1_L2 -0.2670984 0.2008860 -1.329602
#> envY1_L3 -0.7784500 0.2008736 -3.875323
#> envY1_L4 -1.1050189 0.2008708 -5.501142
#> envY1_L5 -0.4542703 0.2008850 -2.261345
#>
#> Converged: TRUE | Iterations: 10
#>
#> Model Notes:
#> - Selected prediction SEs are available through mandala_predict().
#> - Selected fixed-effect tests are available with mandala_fixed_tests(type = 'selected').
#>
#> Random Effects (BLUPs) [first 5]:
#> random level estimate std.error z.ratio
#> geno G1 -0.8617860 0.1967470 -4.3801743
#> geno G10 0.1274719 0.1967575 0.6478631
#> geno G11 1.3460707 0.1965576 6.8482268
#> geno G12 0.2550975 0.1971240 1.2940964
#> geno G13 1.0727427 0.1966296 5.4556516
#>
#> logLik: -1235.493 AIC: 2478.987 BIC: 2498.577 logLik_Trunc: -325.744
pred_single <- mandala_predict(fit_single, "geno", verbose = FALSE)
head(pred_single)
#> geno predicted_value std_error
#> 1 G1 5.773522 0.1590994
#> 2 G10 6.780007 0.1591189
#> 3 G11 8.019827 0.1590331
#> 4 G12 6.909855 0.1592230
#> 5 G13 7.741739 0.1590373
#> 6 G14 6.515366 0.1590225Two-stage analysis is useful when a large MET is naturally analyzed in pieces. In stage 1, each environment is analyzed separately to produce adjusted genotype BLUEs and their covariance matrix. In stage 2, those adjusted means are analyzed across environments. This can be efficient, transparent, and practical for large breeding trial networks.
First define the per-environment model. Here genotype is fixed in stage 1 so that Mandala can extract adjusted genotype BLUEs for each environment.
s1_prep <- stage1_prep(
df = met,
env_col = "env",
s1_fixed = yld ~ geno,
s1_random = ~ rep:block,
s1_classify_term = "geno",
response_var = "yld"
)
stage1_audit(met, s1_prep)
#>
#> Stage-1 audit
#> -------------
#> Environments: 10
#> Total rows: 1000
#> Response: yld
#> Classify term: geno
#> Stage-1 fixed: yld ~ geno
#> Stage-1 random: ~rep:block
#>
#> env n_rows n_nonmissing_y n_classify_levels
#> 1 Y1_L1 100 100 50
#> 2 Y1_L2 100 100 50
#> 3 Y1_L3 100 100 50
#> 4 Y1_L4 100 100 50
#> 5 Y1_L5 100 100 50
#> 6 Y2_L1 100 100 50
#> 7 Y2_L2 100 100 50
#> 8 Y2_L3 100 100 50
#> 9 Y2_L4 100 100 50
#> 10 Y2_L5 100 100 50Now fit the first-stage models quietly across environments. Each environment is fit separately, so quiet output is important when many environments are present.
s1 <- mandala_stage1(
df = met,
prep = s1_prep,
verbose = FALSE
)
stage1_report_check(s1)
#>
#> Stage-1 report check
#> --------------------
#> Successful environments: 10
#> Failed environments : 0
#> env n_rows n_BLUEs has_vcov vcov_dim
#> 1 Y1_L1 100 50 TRUE 50x50
#> 2 Y1_L2 100 50 TRUE 50x50
#> 3 Y1_L3 100 50 TRUE 50x50
#> 4 Y1_L4 100 50 TRUE 50x50
#> 5 Y1_L5 100 50 TRUE 50x50
#> 6 Y2_L1 100 50 TRUE 50x50
#> 7 Y2_L2 100 50 TRUE 50x50
#> 8 Y2_L3 100 50 TRUE 50x50
#> 9 Y2_L4 100 50 TRUE 50x50
#> 10 Y2_L5 100 50 TRUE 50x50The stage-1 object stores one fitted model per environment. For example, inspect the first few genotype BLUEs carried forward from stage 1.
stage1_blues <- do.call(rbind, lapply(s1$results, function(x) {
out <- x$BLUEs[, c("geno", "predicted_value", "std_error")]
out$env <- x$env
out[, c("env", "geno", "predicted_value", "std_error")]
}))
head(stage1_blues)
#> env geno predicted_value std_error
#> Y1_L1.1 Y1_L1 G1 5.314903 0.5489310
#> Y1_L1.2 Y1_L1 G10 6.320838 0.5489249
#> Y1_L1.3 Y1_L1 G11 8.869945 0.5489375
#> Y1_L1.4 Y1_L1 G12 7.033992 0.5489310
#> Y1_L1.5 Y1_L1 G13 8.770005 0.5489310
#> Y1_L1.6 Y1_L1 G14 7.436263 0.5489310Stage-1 heritability is a useful trial-quality summary. Mandala estimates it from separate per-environment models where genotype is fitted as random. Poor or unstable environments can then be reviewed before stage 2.
s1_h2 <- mandala.h2(
df = met,
out = s1,
env_col = "env",
genotype_col = "geno",
response_col = "yld",
h2_fixed = yld ~ 1,
h2_random = ~ geno + rep:block,
verbose = FALSE
)
head(s1_h2)
#> env n_obs n_genotype sigma_g2 sigma_e2 n_rep_arithmetic n_rep_harmonic mean_PEV mean_PEVD H2_plot H2_standard H2_entrymean_PEV H2_Cullis status
#> 1 Y1_L1 100 50 0.5449739 0.5996125 2 2 0.3013242 0.6016202 0.4761318 0.6451074 0.4470852 0.4480284 ok
#> 2 Y1_L2 100 50 0.6659502 0.4879891 2 2 0.2590033 0.5108521 0.5771102 0.7318578 0.6110771 0.6164488 ok
#> 3 Y1_L3 100 50 0.6405839 0.4140495 2 2 0.2588452 0.4507066 0.6073996 0.7557543 0.5959230 0.6482064 ok
#> 4 Y1_L4 100 50 0.4442142 0.4921793 2 2 0.2591018 0.4967718 0.4743884 0.6435053 0.4167188 0.4408420 ok
#> 5 Y1_L5 100 50 0.6177487 0.4315737 2 2 0.2285999 0.4481608 0.5887120 0.7411186 0.6299468 0.6372629 ok
#> 6 Y2_L1 100 50 0.4628072 0.5312939 2 2 0.2799074 0.5484042 0.4655534 0.6353278 0.3951964 0.4075241 okmandala_stage2_prep() returns a data frame of stage-1
BLUEs and an RMAT covariance matrix aligned to that data frame.
s2 <- mandala_stage2_prep(s1)
stage2_df <- s2$data
RMAT <- s2$RMAT
head(stage2_df)
#> env geno yld SE_stage1
#> 1 Y1_L1 G1 5.314903 0.5489310
#> 2 Y1_L1 G10 6.320838 0.5489249
#> 3 Y1_L1 G11 8.869945 0.5489375
#> 4 Y1_L1 G12 7.033992 0.5489310
#> 5 Y1_L1 G13 8.770005 0.5489310
#> 6 Y1_L1 G14 7.436263 0.5489310
dim(RMAT)
#> [1] 500 500The first rows and columns of RMAT show the covariance
among the first-stage BLUEs passed into stage 2.
round(as.matrix(RMAT[1:5, 1:5]), 4)
#> Y1_L1|G1 Y1_L1|G10 Y1_L1|G11 Y1_L1|G12 Y1_L1|G13
#> Y1_L1|G1 0.3013 0.0007 0.0000 0.0014 0.0014
#> Y1_L1|G10 0.0007 0.3013 0.0007 0.0007 0.0007
#> Y1_L1|G11 0.0000 0.0007 0.3013 0.0000 0.0000
#> Y1_L1|G12 0.0014 0.0007 0.0000 0.3013 0.0014
#> Y1_L1|G13 0.0014 0.0007 0.0000 0.0014 0.3013fit_two <- mandala_stage2(
data = stage2_df,
RMAT = RMAT,
fixed = yld ~ env,
random = ~ geno + geno:env,
R_formula = ~ vcov(RMAT),
env = "env",
id = "geno",
verbose = FALSE
)
summary(fit_two)
#> Model statement:
#> mandala(fixed = yld ~ env, random = ~geno + geno:env, data = data,
#> matrix_list = matrix_list, R_formula = ~vcov(RMAT), verbose = FALSE,
#> method = "sparse", engine = "mme")
#>
#> Variance Components:
#> component estimate std.error z.ratio bound %ch
#> geno 0.42911835 9.355504e-02 4.586801e+00 P NA
#> geno:env 0.04026488 2.097152e+06 1.919979e-08 P NA
#> R.sigma2 0.05086729 2.097152e+06 2.425541e-08 P NA
#>
#> Fixed Effects (BLUEs) [first 5]:
#> effect estimate std.error z.ratio
#> (Intercept) 7.0853885 0.1301334 54.447126
#> envY1_L2 -0.2795267 0.1372235 -2.037018
#> envY1_L3 -0.7529652 0.2190914 -3.436763
#> envY1_L4 -1.1002689 0.1605382 -6.853625
#> envY1_L5 -0.4612663 0.1383428 -3.334227
#>
#> Converged: TRUE | Iterations: 11
#>
#> Model Notes:
#> - Model size class: complex (n=500, q=550, MME dim=560).
#>
#> Random Effects (BLUPs) [first 5]:
#> random level estimate std.error z.ratio
#> geno G1 -0.8355748 0.1984764 -4.2099466
#> geno G10 0.1335397 0.1984965 0.6727559
#> geno G11 1.3643650 0.1981645 6.8850122
#> geno G12 0.2506781 0.1989430 1.2600499
#> geno G13 1.0502786 0.1983903 5.2940020
#>
#> logLik: -511.109 AIC: 1028.217 BIC: 1040.801 logLik_Trunc: -60.829
pred_two <- mandala_predict(fit_two, "geno", verbose = FALSE)
head(pred_two)
#> geno predicted_value std_error
#> 1 G1 5.805882 0.1717629
#> 2 G10 6.784090 0.1717769
#> 3 G11 8.026464 0.1716367
#> 4 G12 6.902327 0.1719026
#> 5 G13 7.709430 0.1717100
#> 6 G14 6.512140 0.1716185cmp <- merge(
pred_single[, c("geno", "predicted_value")],
pred_two[, c("geno", "predicted_value")],
by = "geno",
suffixes = c("_single", "_two")
)
r_single_two <- cor(
cmp$predicted_value_single,
cmp$predicted_value_two,
use = "complete.obs"
)
r_single_two
#> [1] 0.9994991
plot(
cmp$predicted_value_single,
cmp$predicted_value_two,
pch = 16,
xlab = "Single-stage genotype mean",
ylab = "Two-stage genotype mean",
main = "Single-stage vs two-stage genotype means"
)
abline(0, 1, col = "gray40", lty = 2)
legend(
"topleft",
legend = paste0("r = ", round(r_single_two, 3)),
bty = "n"
)rel_two <- stage2_reliability(fit_two, classify_term = "geno", preds = pred_two)
head(rel_two)
#> geno predicted_value std_error PEV reliability accuracy weight_invPEV
#> 29 G35 6.147453 0.1716135 0.02945118 0.9313682 0.9650742 33.95450
#> 33 G39 6.835278 0.1716142 0.02945142 0.9313676 0.9650739 33.95422
#> 6 G14 6.512140 0.1716185 0.02945291 0.9313641 0.9650721 33.95250
#> 15 G22 6.966521 0.1716205 0.02945360 0.9313625 0.9650713 33.95171
#> 26 G32 6.159648 0.1716294 0.02945667 0.9313554 0.9650676 33.94817
#> 46 G50 6.150468 0.1716299 0.02945682 0.9313550 0.9650674 33.94800
stage2_variance_summary(fit_two, stage2_df, RMAT)
#> Component Variance PVE
#> 1 env NA NA
#> 2 genetic_effect 0.42911835 0.57526136
#> 3 homogeneous_GxE 0.05086729 0.06819095
#> 4 Stage1_error 0.26596807 0.35654769Single-stage and two-stage analyses are both standard mixed-model strategies for multi-environment trial analysis. Single-stage models analyze plot-level data directly, while two-stage methods first estimate environment-specific genotype effects and then combine those estimates with appropriate weighting or covariance information. The literature below provides useful background on REML mixed models, MET covariance modeling, and efficient stage-wise analysis.