This Computational Provenance Record documents the statistical computing environment, software dependencies, computational provenance, and bibliographic references associated with the RAISINS Line × Tester Analysis module. 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
linetester(), built on stats::aov() and stats::anova()
F-tests of the combined table
stats::pf()
Combining ability effects (GCA, SCA)
linetester(), via stats::tapply() marginal means
Standard errors of effects
linetester()
Genetic components & contribution
linetester()
Mean comparison
agricolae::LSD.test(), agricolae::HSD.test()
Significance of effects & heterosis
stats::pt()
Critical difference values
stats::qt()
Note : linetester() function created by RAISINS team.
4 Default Methods & Parameters
Analysis
Step / Parameter
Default Method / Value
Line × Tester
ANOVA
(linetester())
Input structure
One column identifying the replication, one for the lines, one for the testers, followed by one column per trait; all traits are analysed in a single run. Parent entries are those rows in which the line or the tester is absent (NA); the remaining complete rows form the crosses. The cross label is formed internally from the line and tester factors
ANOVA with parents
aov(Y ~ Replications + Treatments) fitted to the full data set (parents and crosses), giving Replications, Treatments, and Error
Full ANOVA
Ten-line table assembled from three fitted models: Replications and Treatments from the model above, Parents from aov(Y ~ Parents) on the parent rows, Crosses from aov(Y ~ Crosses) on the cross rows, Lines / Testers / Lines × Testers from aov(Y ~ Lines * Testers) fitted to the cross rows, Error, and Total. Parents vs. Crosses is obtained by subtraction (Treatments − Parents − Crosses)
F-tests
Mean squares are recomputed as SS / df. Lines and Testers are tested against the Lines × Testers mean square; every other source is tested against the Error mean square. P-values from 1 - pf(F, df1, df2) with the denominator degrees of freedom matched accordingly
Significance
& Rounding
Level (α)
0.05 (default) or 0.01
Star codes & rounding
**p ≤ 0.01, * 0.01 < p ≤ 0.05, NS where the p-value is undefined; results rounded to 2 decimal places by default
Mean
Comparison
Method
LSD (default) via agricolae::LSD.test(), or TUKEY via agricolae::HSD.test(); both are supplied the Error degrees of freedom and mean square from the full ANOVA
Reporting rule
Critical difference (CD) or HSD values are displayed only when the treatment effect is significant at α; the coefficient of variation is taken from the LSD.test() statistics
Combining
Ability
Effects
Estimated from the cross means only. All effects are formed from the matrix of cross cell means. GCA of a line = its row mean − the mean of the cell means; GCA of a tester = its column mean − the mean of the cell means; SCA of a cross = cell mean − line row mean − tester column mean + the mean of the cell means. Effects are rounded to 3 decimals inside the engine
Standard errors
With Me the Error mean square, r replications, l lines and t testers: SE(gi) = √(Me/(r·t)) for lines and √(Me/(r·l)) for testers; SE(sij) = √(Me/r); the corresponding difference errors are √2 times these values
Significance
t = effect / SE, tested against the Error degrees of freedom as p = 2 × pt(-|t|, df); SE(gi), SE(gi − gj), SE(sij) and SE(sij − skl) are appended to the tables
Proportional
Contribution
Components
Percentage contribution of Lines, Testers, and Lines × Testers, each as 100 × its sum of squares / the Crosses sum of squares
Genetic
Components
Covariances
Half-sib covariance of lines = (MSlines − MSL×T)/(r·t) and of testers = (MStesters − MSL×T)/(r·l); the average half-sib covariance pools both against MSL×T; the average full-sib covariance is formed from the Lines, Testers and Lines × Testers mean squares each less the Error mean square
Additive & dominance variance
Var(A) = 4 × Cov(HS, average) / (1 + F) and Var(D) = 2 × (MSL×T − Me) / (r × (1 + F)). Both are computed at F = 0 and F = 1; the module reports the F = 1 estimates, appropriate to inbred parents
Heterosis
Mid-parent (relative)
100 × (F1 − MP) / MP, where MP = (P1 + P2) / 2; SE = √(1.5 × Me / r). Throughout the heterosis section Me is the Error mean square of the ANOVA with parents, since the comparison involves parent entries
Better-parent (heterobeltiosis)
100 × (F1 − BP) / BP, where BP = max(P1, P2); SE = √(2 × Me / r)
Standard heterosis
100 × (F1 − SC) / SC, where SC is the user-supplied standard check mean; SE = √(2 × Me / r). All three are tested as t = difference / SE against the Error degrees of freedom, with CD at 5% and 1% from qt(0.975, df) and qt(0.995, df)
Genetic
Parameters
Variances
σ²g = (MSt − MSe) / r, with negative estimates set to zero; σ²e = MSe; σ²p = σ²g + σ²e
Coefficients of variation
GCV, PCV, and ECV as 100 × √(σ²) / grand mean
Heritability & advance
Broad-sense heritability = 100 × σ²g / σ²p; genetic advance = k × √(σ²p) × h², with k = 2.06 at 5% selection intensity; GA as a percentage of the grand mean
5 R Code for Key Analytical Steps
The linetester function is provided below for reproducibility, followed by a single worked example on a 5 line × 3 tester dataset with parents.
For any issues in the function please comment here
5.1 The linetester() function
linetester <-function(replications, lines, testers, y) { replications <-as.factor(replications) lines <-as.factor(lines) testers <-as.factor(testers) datos <-data.frame(Replications = replications,Lines = lines,Testers = testers,Y = y ) n_r <-nlevels(datos$Replications) n_l <-nlevels(datos$Lines) n_t <-nlevels(datos$Testers)# ---- crosses (complete rows) and parents (line or tester absent) ---- datos2 <-na.omit(datos) # crosses datos3 <-subset(datos, is.na(datos$Lines) |is.na(datos$Testers)) # parents# ---- ANOVA with parents: Replications, Treatments, Error ---- Treatments <-as.factor(paste(datos$Lines, datos$Testers)) modelo1 <-aov(Y ~ Replications + Treatments, data = datos) matriz1 <-as.matrix(anova(modelo1))# ---- Lines, Testers, Lines x Testers: fitted on the CROSSES only ---- modelo4 <-aov(Y ~ Lines * Testers, data = datos2) matriz4 <-as.matrix(anova(modelo4))# ---- Parents ---- Parents <-as.factor(paste(datos3$Lines, datos3$Testers)) modelo2 <-aov(Y ~ Parents, data = datos3) matriz2 <-as.matrix(anova(modelo2))# ---- Crosses ---- Crosses <-as.factor(paste(datos2$Lines, datos2$Testers)) modelo3 <-aov(Y ~ Crosses, data = datos2) matriz3 <-as.matrix(anova(modelo3))# ---- cell means of the crosses ---- mm_cross <-tapply(datos2$Y, datos2[, c("Lines", "Testers")], mean, na.rm =TRUE) gm_cell <-mean(mm_cross, na.rm =TRUE)# ---- SCA: cell - line marginal - tester marginal + grand ---- SCA <-round( mm_cross -outer(rowMeans(mm_cross, na.rm =TRUE),colMeans(mm_cross, na.rm =TRUE), "+") + gm_cell,3 )# ---- GCA of lines and testers, from the cell means ---- GCA.lines <-round(rowMeans(mm_cross, na.rm =TRUE) - gm_cell, 3) GCA.testers <-round(colMeans(mm_cross, na.rm =TRUE) - gm_cell, 3)# ---- assemble the ten-line table ----# Parents vs. Crosses by subtraction: Treatments - Parents - Crosses.# Only df and SS are meaningful here; columns 3-5 are recomputed below. matriz5 <- matriz1[2, ] - matriz2[1, ] - matriz3[1, ] matriz <-rbind( matriz1[1:2, ], # Replications, Treatments matriz2[1, ], # Parents matriz5, # Parents vs. Crosses matriz3[1, ], # Crosses matriz4[1:3, ], # Lines, Testers, Lines x Testers matriz1[3, ] # Error ) matriz <-rbind(matriz, c(sum(matriz1[, 1]), sum(matriz1[, 2]), NA, NA, NA))rownames(matriz) <-c("Replications", "Treatments", "Parents", "Parents vs. Crosses","Crosses", "Lines", "Testers", "Lines x Testers", "Error", "Total" )# ---- F-tests: Lines (6) and Testers (7) against Lines x Testers (8);# every other source against Error (9) ----for (i in1:9) { matriz[i, 3] <- matriz[i, 2] / matriz[i, 1] matriz[i, 4] <- matriz[i, 3] / matriz[9, 3] matriz[i, 5] <-1-pf(matriz[i, 4], matriz[i, 1], matriz[9, 1])if (i ==6|| i ==7) { matriz[i, 4] <- matriz[i, 3] / matriz[8, 3] matriz[i, 5] <-1-pf(matriz[i, 4], matriz[i, 1], matriz[8, 1]) } }# ---- standard errors, with Me the Error mean square of the full table ---- Me <- matriz[9, 3] SE <-list(gca_line =sqrt(Me / (n_r * n_t)),gca_tester =sqrt(Me / (n_r * n_l)),sca =sqrt(Me / n_r),gi_gj_line =sqrt(2* Me / (n_r * n_t)),gi_gj_tester =sqrt(2* Me / (n_r * n_l)),sij_skl =sqrt(2* Me / n_r) )# ---- genetic components ---- MS_l <- matriz[6, 3] MS_t <- matriz[7, 3] MS_lt <- matriz[8, 3] cov1 <- (MS_l - MS_lt) / (n_r * n_t) # Cov HS, lines cov2 <- (MS_t - MS_lt) / (n_r * n_l) # Cov HS, testers cov3 <- (((n_l -1) * MS_l + (n_t -1) * MS_t) / (n_l + n_t -2) - MS_lt) / (n_r * (2* n_l * n_t - n_l - n_t)) # Cov HS, average cov4 <- ((MS_l - Me) + (MS_t - Me) + (MS_lt - Me)) / (3* n_r) # Cov FS, average Fcoef <-0 varA0 <- cov3 * (4/ (1+ Fcoef)) varD0 <- ((MS_lt - Me) / n_r) * (2/ (1+ Fcoef)) Fcoef <-1 varA1 <- cov3 * (4/ (1+ Fcoef)) varD1 <- ((MS_lt - Me) / n_r) * (2/ (1+ Fcoef)) Genetic <-list(Cov_HS_line = cov1,Cov_HS_tester = cov2,Cov_HS_avg = cov3,Cov_FS_avg = cov4,VarA_F0 = varA0, VarA_F1 = varA1,VarD_F0 = varD0, VarD_F1 = varD1 )# ---- proportional contribution to the Crosses sum of squares ---- Contributions <-list(lines = matriz[6, 2] *100/ matriz[5, 2],testers = matriz[7, 2] *100/ matriz[5, 2],lxt = matriz[8, 2] *100/ matriz[5, 2] )return(list(ANOVA_with_parents = matriz1,ANOVA_LxT = matriz4,ANOVA_full = matriz,GCA_lines = GCA.lines,GCA_testers = GCA.testers,SCA = SCA,SE = SE,Genetic = Genetic,Contribution = Contributions ))}
5.2 Worked Example: Input Data
Five lines (L1-L5) crossed with three testers (T6-T8) in four replications, together with the fifteen cross rows, the five line parents (tester absent) and the three tester parents (line absent). Parent entries are marked by NA.
5.3 Worked Example: ANOVA, Combining Ability, Genetic Components
A single call returns the ANOVA tables, the GCA and SCA effects, their standard errors, the proportional contributions and the genetic components.
out <-linetester(replications = Block,lines = A,testers = B,y = dataset[[trait_name]])parent_result <-as.data.frame(out$ANOVA_with_parents) # Replications, Treatments, Errorfull_result <-as.data.frame(out$ANOVA_full) # 9 sources + Totalout$GCA_lines # GCA effects of the five linesout$GCA_testers # GCA effects of the three testersout$SCA # SCA effects, 5 x 3 matrix of crossesout$SE # gca_line, gca_tester, sca, gi_gj_line, gi_gj_tester, sij_sklout$Contribution # lines, testers, lxt (% of the Crosses sum of squares)out$Genetic # Cov_HS_line/tester/avg, Cov_FS_avg, VarA_F0/F1, VarD_F0/F1# the module reports the F = 1 values# Error term driving the mean comparisons and the tests of effectsMSE <-as.numeric(full_result[9, 3])DFE <-as.numeric(full_result[9, 1])
5.4 Significance of GCA and SCA Effects
The effects and their standard errors come straight from out; only the t statistics, p values and star codes are formed here.
alpha <-0.05# --- GCA of lines ---t_gca_l <-as.numeric(out$GCA_lines) / out$SE$gca_line# --- GCA of testers ---t_gca_t <-as.numeric(out$GCA_testers) / out$SE$gca_tester# --- SCA of crosses ---t_sca <-as.numeric(out$SCA) / out$SE$sca
5.5 Heterosis, Heterobeltiosis and Standard Heterosis
The cross mean F1 and the two parent means P1 (line) and P2 (tester) are read off the data; the error term is the one from the ANOVA with parents, since the comparison involves parent entries.
datos <-data.frame(Replications = Block,Lines = A,Testers = B,Y = dataset[[trait_name]])# error mean square and df from the ANOVA WITH PARENTSMe_par <-as.numeric(parent_result[3, 3])df_par <-as.numeric(parent_result[3, 1])# cross means, line parent means, tester parent meanscross_mean <-tapply(datos$Y[!is.na(datos$Lines) &!is.na(datos$Testers)],paste(datos$Lines, datos$Testers)[!is.na(datos$Lines) &!is.na(datos$Testers)], mean, na.rm =TRUE)line_par <-subset(datos, !is.na(Lines) &is.na(Testers))P1_mean <-tapply(line_par$Y, droplevels(line_par$Lines), mean, na.rm =TRUE)test_par <-subset(datos, is.na(Lines) &!is.na(Testers))P2_mean <-tapply(test_par$Y, droplevels(test_par$Testers), mean, na.rm =TRUE)# one row per crossgrid <-expand.grid(line =levels(A),tester =levels(B),stringsAsFactors =FALSE)grid$cross <-paste(grid$line, grid$tester)F1 <- cross_mean[grid$cross]P1 <- P1_mean[grid$line]P2 <- P2_mean[grid$tester]MP <- (P1 + P2) /2# mid-parentBP <-pmax(P1, P2) # better parent# percentage heterosismid_parent <- ((F1 - MP) / MP) *100heterobelt <- ((F1 - BP) / BP) *100# standard errorsSE_H1 <-sqrt((1.5* Me_par) / r) # mid-parentSE_H2 <-sqrt((2* Me_par) / r) # better parentSE_SH <-sqrt((2* Me_par) / r) # standard check# t test, p value and critical differences (shown for heterobeltiosis)t_val <- (F1 - BP) / SE_H2p_val <-2*pt(-abs(t_val), df = df_par)CD5 <- SE_H2 *qt(0.975, df = df_par)CD1 <- SE_H2 *qt(0.995, df = df_par)
5.6 Genetic Parameters
MSt <-as.numeric(full_result[2, 3]) # Treatments mean squareMSe <-as.numeric(full_result[9, 3]) # Error mean squaregrand_mean <-mean(dataset[[trait_name]], na.rm =TRUE)sigma2_g <-max((MSt - MSe) / r, 0) # negative estimates set to zerosigma2_e <- MSesigma2_p <- sigma2_g + sigma2_eGCV <- (sqrt(sigma2_g) / grand_mean) *100PCV <- (sqrt(sigma2_p) / grand_mean) *100ECV <- (sqrt(sigma2_e) / grand_mean) *100h2_bs <-ifelse(sigma2_p >0, (sigma2_g / sigma2_p) *100, 0) # broad sense (%)k <-2.06# 5% selection intensityGA <- k *sqrt(sigma2_p) * (h2_bs /100) # genetic advanceGAM <- (GA / grand_mean) *100# GA as % of mean
Explore the entire Line × Tester Analysis module in preview mode using our demo datasets. 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
Kempthorne, O. (1957). An Introduction to Genetic Statistics. John Wiley & Sons, New York.
Singh, R. K., & Chaudhary, B. D. (1985). Biometrical Methods in Quantitative Genetic Analysis. Kalyani Publishers, New Delhi.
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.30). https://github.com/rstudio/rmarkdown