
Genomic Selection Pipeline with brapiR2
Source:vignettes/genomic-selection-pipeline.Rmd
genomic-selection-pipeline.RmdOverview
This vignette demonstrates a complete genomic selection (GS) workflow using brapiR2 to pull phenotypic and genotypic data directly from a BrAPI-compliant server into R, then running GS models with rrBLUP.
The brapi_get_dosage_matrix() function returns a
standard numeric matrix (samples × markers) that is directly compatible
with the major GS packages:
| Package | Usage |
|---|---|
| rrBLUP | mixed.solve(y, Z = dosage) |
| BGLR | ETA = list(MRK = list(X = dosage, model = "BRR")) |
| sommer | mmer(yield ~ 1, random = ~ vsr(germplasmName, Gu = A)) |
| AGHmatrix | Gmatrix(dosage, method = "VanRaden") |
Step 1: Connect and Fetch Phenotypic Data
library(brapiR2)
library(dplyr)
con <- brapi_connection("https://my-breedbase.org")
con <- brapi_login(con, "username", "password")
# Optional: enable caching so re-runs don't hit the server again
con <- brapi_cache_enable(con, ttl = 7200)
# Get phenotype data in wide format (one row per plot, one column per trait)
pheno_raw <- brapi_study_data(con, "my_study_id")
pheno_raw
#> # A tibble: 250 x 8
#> observationUnitDbId germplasmDbId germplasmName studyDbId
#> <chr> <chr> <chr> <chr>
#> 1 ou001 g001 Variety_001 study_01
#> 2 ou002 g002 Variety_002 study_01
#> # ... with 246 more rows, and 4 more variables:
#> # Grain_Yield_t_ha <chr>, Days_to_Heading <chr>,
#> # Plant_Height_cm <chr>, Thousand_Grain_Weight <chr>
# Convert trait columns to numeric and drop missing
pheno <- pheno_raw |>
mutate(across(Grain_Yield_t_ha:Thousand_Grain_Weight, as.numeric)) |>
filter(!is.na(Grain_Yield_t_ha))
nrow(pheno)
#> [1] 241Step 2: Fetch Genotypic Data
# List available variant sets to find the right genotyping dataset
vsets <- brapi_variant_sets(con)
vsets
#> # A tibble: 3 x 8
#> variantSetDbId variantSetName callSetCount variantCount
#> <chr> <chr> <int> <int>
#> 1 vs001 SNP50K_2022 250 45230
#> 2 vs002 GBS_v2_2022 250 82100
#> 3 vs003 DArTseq_panel 250 14500
# Get dosage matrix: rows = samples (callSetDbIds),
# cols = markers (variantDbIds). Values: 0 (hom ref), 1 (het), 2 (hom alt),
# NA (missing)
dosage <- brapi_get_dosage_matrix(con, "vs001")
#> i Fetching allele matrix for variant set 'vs001'...
#> i Encoding 11307500 genotype calls as allele dosages...
#> v Dosage matrix ready: 250 samples x 45230 markers.
dim(dosage)
#> [1] 250 45230
# Preview dosage values
dosage[1:4, 1:6]
#> SNP_chr1_1234 SNP_chr1_5678 SNP_chr2_234 SNP_chr2_890 ...
#> Variety_001 0 1 2 0
#> Variety_002 2 0 0 1
#> Variety_003 1 1 1 1
#> Variety_004 0 0 2 0
# Get marker map for downstream QTL / Manhattan plots
markers <- brapi_get_marker_map(con, "vs001")
markers
#> # A tibble: 45230 x 4
#> variantDbId variantName referenceName start
#> <chr> <chr> <chr> <int>
#> 1 var001 SNP_chr1_1234 1A 1234
#> 2 var002 SNP_chr1_5678 1A 5678
#> # ... with 45228 more rowsStep 3: Align Phenotypes and Genotypes
# Match on germplasmName (rownames of dosage = callSetDbId = germplasmName
# on most servers; adjust if your server uses a different identifier)
common <- intersect(rownames(dosage), pheno$germplasmName)
length(common)
#> [1] 237
pheno_aligned <- pheno |>
filter(germplasmName %in% common) |>
arrange(match(germplasmName, common))
geno_aligned <- dosage[common, ]
# Quick missing-data summary
mean(is.na(geno_aligned))
#> [1] 0.0312 # ~3.1% missing calls — impute before modellingStep 5: Run Genomic Selection with rrBLUP
library(rrBLUP)
# Mixed model: phenotype ~ overall mean + random marker effects
result <- mixed.solve(
y = pheno_aligned$Grain_Yield_t_ha,
Z = geno_aligned
)
# Estimated marker effects
length(result$u)
#> [1] 45230
# GEBVs (Genomic Estimated Breeding Values)
gebvs <- geno_aligned %*% result$u + result$beta
head(gebvs)
#> [,1]
#> Variety_001 4.82
#> Variety_002 5.14
#> Variety_003 4.67
#> Variety_004 5.31
#> Variety_005 4.91
#> Variety_006 5.09
hist(gebvs,
main = "Distribution of GEBVs — Grain Yield",
xlab = "GEBV (t/ha)", col = "steelblue"
)Step 6: Alternative GS Packages
# ---- BGLR (Bayesian regression) ----
library(BGLR)
fm_bglr <- BGLR(
y = pheno_aligned$Grain_Yield_t_ha,
ETA = list(MRK = list(X = geno_aligned, model = "BRR")),
nIter = 6000, burnIn = 1000,
verbose = FALSE
)
gebvs_bglr <- geno_aligned %*% fm_bglr$ETA$MRK$b
# ---- sommer (mixed model with G matrix) ----
library(sommer)
library(AGHmatrix)
# Build genomic relationship matrix
G <- Gmatrix(geno_aligned, method = "VanRaden")
#> Gmatrix computation: VanRaden method
#> Markers: 45230 Individuals: 237
fm_sommer <- mmer(
Grain_Yield_t_ha ~ 1,
random = ~ vsr(germplasmName, Gu = G),
rcov = ~units,
data = pheno_aligned
)
gebvs_sommer <- randef(fm_sommer)$`u:germplasmName`Step 7: Cross-Validation
set.seed(42)
n <- nrow(pheno_aligned)
folds <- sample(rep(1:5, length.out = n))
cors <- numeric(5)
for (k in 1:5) {
train <- folds != k
test <- folds == k
fit <- mixed.solve(
y = pheno_aligned$Grain_Yield_t_ha[train],
Z = geno_aligned[train, ]
)
pred <- geno_aligned[test, ] %*% fit$u + fit$beta
cors[k] <- cor(as.numeric(pred), pheno_aligned$Grain_Yield_t_ha[test])
}
cat(sprintf(
"5-fold CV prediction accuracy: %.3f ± %.3f\n",
mean(cors), sd(cors)
))
#> 5-fold CV prediction accuracy: 0.612 ± 0.054