Computational Provenance & Reproducibility Record

RAISINS - R and AI Solutions for INferential Statistics · Online Statistical Analysis Platform for Agricultural Research

Computational Provenance & Reproducibility Record Pooled Analysis - Two-Factor CRD · 1.0.0 · DOI 10.5281/zenodo.23081354

Computational Provenance & Reproducibility Record

RAISINS· Pooled Two-Factor CRD Analysis Module

This Computational Provenance Record documents the statistical computing environment, software dependencies, computational provenance, and bibliographic references associated with the RAISINS Pooled Analysis - Two-Factor CRD module (a two-factor factorial experiment laid out in a Completely Randomized Design and pooled across several locations, seasons or years). It is intended to support computational reproducibility and software transparency. Detailed statistical methodology, mathematical derivations, and user guidance are provided separately in the official module tutorial.

Any issues or updates required please comment here

Go to the App from here

1 Module Metadata

Parameter Specification
Module Pooled Analysis - Two-Factor CRD
Module Version 1.0.0
DOI 10.5281/zenodo.23081354
Document Type Computational workflow
Statistical Engine R
R Version 4.5.2
Reproducibility Execution Environment: Posit Connect · GCR · renv-locked

2 Statistical Dependency Manifest

Package Version Repository Core Statistical Functions
car 3.1-5 CRAN Anova() (Type II sums of squares)
agricolae 1.3-7 CRAN LSD.test(), HSD.test(), duncan.test()
gtools 3.9.5 CRAN mixedsort() (ordering of grouping letters)
stats 4.5.2 Base R lm(), aov(), bartlett.test(), pf(), resid(), df.residual(), interaction()

3 Statistical Function Registry

Analytical Role Primary Function(s)
Per-location error variances (for homogeneity test) stats::aov(), stats::resid(), stats::df.residual()
Homogeneity of error variances across locations stats::bartlett.test()
Variance stabilisation when heterogeneous Aitken transformation (RAISINS native, built on the per-location MSE)
Pooled factorial ANOVA stats::lm() with car::Anova(type = "II")
Model-dependent F-tests of main effects stats::pf() on model-specific error terms
Mean comparison agricolae::LSD.test(), agricolae::HSD.test(), agricolae::duncan.test()
CV, SEm, SEd, CD agricolae::LSD.test() statistics + RAISINS native formulae
Ordering of letter groupings gtools::mixedsort()

Note : the pooling-model F-test logic, the Aitken transformation and the summary-statistic assembly are implemented by the RAISINS team.

4 Default Methods & Parameters

Analysis Step / Parameter Default Method / Value
Input &
Design
Input structure One column identifying the Location / Season / Year, one column for Factor A, one for Factor B, followed by one column per trait; all traits are analysed in a single run. The replications within each Location × A × B cell are the repeated rows (no separate replication column is required)
Design Two-factor factorial in a Completely Randomized Design (CRD), repeated over several environments and analysed jointly (pooled)
Interaction columns The A×B, A×L, B×L and A×B×L combination labels are formed internally from the Factor and Location columns
Homogeneity
& Transformation
Bartlett test For each trait, a separate aov(Y ~ FactorA + FactorB) is fitted within every location and bartlett.test() is applied to the list of fitted models to test equality of the per-location error variances
Aitken transformation Applied automatically to a trait only when its Bartlett test is significant (p ≤ 0.05): each observation is divided by the square root of its own location's error mean square (√MSEloc). Traits with homogeneous variances are left untransformed
Pooled
ANOVA
Model lm(Y ~ L + A + B + A:B + L:A + L:B + L:A:B) with sums of squares from car::Anova(type = "II"); sources are Location, Factor A, Factor B, A×B, L×A, L×B, L×A×B and Pooled Error
Mean squares Each mean square is SS / df; the Pooled Error mean square is the residual of the fitted model
Pooling Model
(F-test error terms)
Model 1 - A, B, Location all Random Location, Factor A and Factor B are each tested against the L×A×B mean square; the interactions are tested against the Pooled Error
Model 2 - Factors Fixed, Location Random Location is tested against the Pooled Error; Factor A and Factor B are tested against the L×A×B mean square
Model 3 - Location Fixed, Factors Random Location is tested against the L×A×B mean square; Factor A and Factor B are tested against the Pooled Error
Model 4 - A, B, Location all Fixed (default) Every source, including Location, Factor A and Factor B, is tested against the Pooled Error mean square
Significance
& Rounding
Level (α) 0.05 (default) or 0.01
Star codes & rounding ** p ≤ 0.01, * 0.01 < p ≤ 0.05, NS otherwise; results rounded to 2 decimal places by default
Mean
Comparison
Method LSD (default) via agricolae::LSD.test(), TUKEY via agricolae::HSD.test(), or DMRT via agricolae::duncan.test(); each is supplied the error degrees of freedom and mean square that match the selected pooling model for that source
P-value adjustment Offered for LSD only, and passed straight through to the p.adj argument of agricolae::LSD.test(): none (default), bonferroni (FWER), holm (Holm–Bonferroni, FWER) or BH (Benjamini–Hochberg, FDR). TUKEY and DMRT carry their own multiplicity control and take no adjustment argument
Letter grouping Compact-letter groupings are produced for a source only when its level count is within the display limit (30 for single factors, 50 for interactions) and letter grouping is enabled; groups are ordered with gtools::mixedsort()
Summary
Statistics
SEm, SEd SE(mean) = √(MSE / r) and SE(difference) = √(2 · MSE / r), where r is the number of observations behind each mean of the source (from the LSD.test() means table) and MSE is the model-matched error mean square
CV, CD The coefficient of variation and the critical difference are taken from the agricolae test statistics for each source at the chosen α

5 R Code for Key Analytical Steps

The steps below reproduce the module’s engine on the bundled demo dataset dataset1.csv - a two-factor CRD (Factor A: S1, S2; Factor B: C1, C2) grown at two locations (A, B) with four replications per cell, and the trait Yield. The same loop runs over every trait column (Yield, Char1…Char6).

For any issues in the workflow please comment here.

5.1 Worked Example: Input Data

# dataset1.csv : Location, FactorA, FactorB, Yield, Char1 ... Char6
data <- read.csv("dataset1.csv", header = TRUE, na.strings = c(""))

data$Location <- as.factor(data$Location)   # A, B      (2 environments)
data$FactorA  <- as.factor(data$FactorA)    # S1, S2
data$FactorB  <- as.factor(data$FactorB)    # C1, C2

trait <- "Yield"                            # analysis is repeated for every trait

# combination labels used by the interaction sources and mean comparisons
data$AxB   <- interaction(data$FactorA, data$FactorB, sep = "x")
data$AxL   <- interaction(data$FactorA, data$Location, sep = "x")
data$BxL   <- interaction(data$FactorB, data$Location, sep = "x")
data$AxBxL <- interaction(data$FactorA, data$FactorB, data$Location, sep = "x")

5.2 Homogeneity of Error Variances (Bartlett) and Aitken Transformation

Before pooling, the error variances of the individual locations are compared. A significant Bartlett test flags heterogeneity, and the trait is Aitken-transformed by dividing each observation by the square root of its own location’s error mean square.

# one within-location ANOVA per environment -> list of fitted models
models_by_loc <- lapply(split(data, data$Location), function(sub) {
  sub <- droplevels(sub)
  if (nlevels(factor(sub$FactorA)) < 2 || nlevels(factor(sub$FactorB)) < 2) return(NULL)
  aov(as.formula(paste(trait, "~ FactorA + FactorB")), data = sub)
})
models_by_loc <- Filter(Negate(is.null), models_by_loc)

# Bartlett test across the per-location residuals
bt <- bartlett.test(models_by_loc)
bt$statistic   # chi-square
bt$p.value     # significance of variance heterogeneity

# per-location error mean square
mse_by_loc <- sapply(models_by_loc, function(m) sum(resid(m)^2) / df.residual(m))

# Aitken transform ONLY when Bartlett is significant (p <= 0.05)
data_t <- data
if (bt$p.value <= 0.05) {
  for (loc in names(mse_by_loc)) {
    rows <- data$Location == loc
    data_t[rows, trait] <- data[rows, trait] / sqrt(mse_by_loc[loc])
  }
}
# data_t holds the (possibly transformed) response used in the pooled ANOVA

5.3 Pooled Factorial ANOVA

The pooled model is fitted on the (possibly transformed) response, and Type II sums of squares are obtained with car::Anova. The eight sources are Location, Factor A, Factor B, A×B, L×A, L×B, L×A×B and the Pooled Error (residual).

L <- factor(data_t$Location)
A <- factor(data_t$FactorA)
B <- factor(data_t$FactorB)
Y <- data_t[[trait]]

fit   <- lm(Y ~ L + A + B + A:B + L:A + L:B + L:A:B)
atab  <- car::Anova(fit, type = "II")   # rows 1..7 = sources, row 8 = Residuals

# mean squares = SS / df
MS <- atab[, 1] / atab[, 2]
names(MS) <- c("L", "A", "B", "AxB", "LxA", "LxB", "LxAxB", "Error")

MSABL <- MS["LxAxB"]    # three-way interaction mean square
MSP   <- MS["Error"]    # pooled error mean square

5.4 Model-Dependent F-Tests of the Main Effects

The interactions and the pooled error are always tested against the residual. The error term used for Location, Factor A and Factor B depends on the pooling model (Model 4, both factors and location fixed, is the default).

dfABL <- atab[7, 2]     # df of L x A x B
dfP   <- atab[8, 2]     # df of Pooled Error

model <- "model_4"      # "model_1", "model_2", "model_3", "model_4" (default)

# choose the denominator MS and df for L, A and B under each pooling model
denom <- switch(model,
  # A, B, Location all random: test all three against L x A x B
  model_1 = list(L = c(MSABL, dfABL), A = c(MSABL, dfABL), B = c(MSABL, dfABL)),
  # factors fixed, location random: L vs Error; A, B vs L x A x B
  model_2 = list(L = c(MSP,  dfP),   A = c(MSABL, dfABL), B = c(MSABL, dfABL)),
  # location fixed, factors random: L vs L x A x B; A, B vs Error
  model_3 = list(L = c(MSABL, dfABL), A = c(MSP,  dfP),   B = c(MSP,  dfP)),
  # all fixed (default): everything vs Pooled Error
  model_4 = list(L = c(MSP,  dfP),   A = c(MSP,  dfP),   B = c(MSP,  dfP))
)

F_and_p <- function(ms, df_num, ms_err, df_err) {
  Fv <- ms / ms_err
  c(F = Fv, p = pf(Fv, df_num, df_err, lower.tail = FALSE))
}

resL <- F_and_p(MS["L"], atab[1, 2], denom$L[1], denom$L[2])
resA <- F_and_p(MS["A"], atab[2, 2], denom$A[1], denom$A[2])
resB <- F_and_p(MS["B"], atab[3, 2], denom$B[1], denom$B[2])

# interactions are always tested against the Pooled Error (car::Anova default F, p)
# star codes: ** p <= 0.01, * 0.01 < p <= 0.05, NS otherwise
star <- function(p) ifelse(p <= 0.01, "**", ifelse(p <= 0.05, "*", "NS"))

5.5 Mean Comparison, CV, SEm, SEd and CD

Each source is compared with the model-matched error term. LSD.test is shown; HSD.test (TUKEY) and duncan.test (DMRT) take the same DFerror / MSerror arguments. SEm and SEd are formed from the replication count returned in the means table.

alpha <- 0.05
padj  <- "none"     # "none" (default), "bonferroni", "holm" or "BH"; LSD only

# error term for a main effect follows the pooling model chosen above;
# interaction sources use the Pooled Error (MSP, dfP)
outA <- agricolae::LSD.test(Y, A, DFerror = denom$A[2], MSerror = denom$A[1],
                            alpha = alpha, p.adj = padj)

statA <- outA$statistics        # includes CV and the CD (LSD) column
rA    <- outA$means[1, 3]       # replications behind each Factor A mean

CV_A  <- statA[1, 4]                       # coefficient of variation (%)
CD_A  <- statA[1, 6]                       # critical difference at alpha (p.adj = "none")
SEm_A <- sqrt(statA[1, 1] / rA)            # standard error of a mean
SEd_A <- sqrt(2 * statA[1, 1] / rA)        # standard error of a difference

# compact-letter grouping (ordered naturally); shown only when significant
groupsA <- outA$groups[gtools::mixedsort(rownames(outA$groups)), ]

# --- interaction example: A x B against the Pooled Error ---
AB   <- factor(data_t$AxB)
outAB <- agricolae::LSD.test(Y, AB, DFerror = dfP, MSerror = MSP,
                             alpha = alpha, p.adj = padj)

Explore the entire Pooled Two-Factor CRD module in preview mode using our demo datasets (dataset1.csv, dataset2.csv, dataset3.csv). To submit suggestions or report a workflow issue, please use the discussion section below, or visit the official RAISINS website.

6 RAISINS Native Statistical Framework

RAISINS uses R for all its statistical computations. Every package used to generate major results is listed and demonstrated with examples, so results can be reproduced independently. These results are then organized and formatted on the RAISINS website along with visualisation to make them easier to use and interpret. RAISINS also has its own custom-built statistical tools for managing workflows, validating results, and generating reports. Details of these are not fully covered here, they’re shared with outside researchers only on request, and are subject to licensing terms.

7 Package References

R Core Team. (2025). R: A language and environment for statistical computing. R Foundation for Statistical Computing, Vienna, Austria. https://www.R-project.org/

de Mendiburu, F. (2023). agricolae: Statistical Procedures for Agricultural Research (R package version 1.3-7). https://doi.org/10.32614/CRAN.package.agricolae

Fox, J., & Weisberg, S. (2019). An R Companion to Applied Regression (3rd ed.). Sage, Thousand Oaks, CA. https://doi.org/10.32614/CRAN.package.car

Warnes, G. R., Bolker, B., & Lumley, T. (2023). gtools: Various R Programming Tools (R package version 3.9.5). https://doi.org/10.32614/CRAN.package.gtools

Aitken, A. C. (1935). On least squares and linear combinations of observations. Proceedings of the Royal Society of Edinburgh, 55, 42–48.

Gomez, K. A., & Gomez, A. A. (1984). Statistical Procedures for Agricultural Research (2nd ed.). John Wiley & Sons, New York.

Allaire, J. J., Xie, Y., Dervieux, C., McPherson, J., Luraschi, J., Ushey, K., Atkins, A., Wickham, H., Cheng, J., Chang, W., & Iannone, R. (2025). rmarkdown: Dynamic Documents for R (R package version 2.31). https://github.com/rstudio/rmarkdown

Feedback & Discussion