Custom QC Report
Source:vignettes/articles/recipe-02-custom-qc-report.Rmd
recipe-02-custom-qc-report.RmdRecipe
Level Intermediate · Output
Self-contained HTML report · Requires
gt, ggplot2, patchwork
(optional), quarto (for template rendering)
Goal
Generate a self-contained HTML QC report that includes:
- Summary statistics (feature counts, sample counts, batches)
- CV distribution plots
- Run-order scatter plots for key features
- Calibration curve summaries (if applicable)
- Feature pass/fail table
- Flagged samples
Part 1: Summary Table
summary_tbl <- tibble::tibble(
Metric = c("Total analyses", "Study samples (SPL)", "QC samples",
"Blanks", "Features (total)", "Features (passed QC)",
"Batches", "Drift corrected?", "Batch corrected?"),
Value = c(
get_analysis_count(mexp),
mexp@annot_analyses |> filter(qc_type == "SPL") |> nrow(),
mexp@annot_analyses |> filter(grepl("QC", qc_type)) |> nrow(),
mexp@annot_analyses |> filter(grepl("BLK", qc_type)) |> nrow(),
nrow(mexp@annot_features),
get_feature_count(mexp),
length(unique(mexp@annot_analyses$batch_id)),
ifelse(length(mexp@var_drift_corrected) > 0, "Yes", "No"),
ifelse(length(mexp@var_batch_corrected) > 0, "Yes", "No")
)
)
summary_tbl |> gt::gt() |> gt::tab_header(title = "Study Summary")Part 2: CV Distribution
cv_data <- mexp@metrics_qc |>
select(feature_id, cv = norm_intensity_cv_bqc) |>
filter(!is.na(cv))
ggplot(cv_data, aes(x = cv)) +
geom_histogram(binwidth = 5, fill = "#5B8FA8", colour = "white") +
geom_vline(xintercept = 30, linetype = "dashed", colour = "red") +
labs(
title = "QC CV Distribution",
subtitle = paste0(sum(cv_data$cv <= 30), "/", nrow(cv_data),
" features with CV \u2264 30%"),
x = "CV (%)", y = "Count"
) +
theme_minimal()Part 3: Run-Order Scatter (Worst Features)
# Plot run scatter for the 4 features with highest CV
top_cv_features <- cv_data |>
arrange(desc(cv)) |>
head(4) |>
pull(feature_id)
plots <- lapply(top_cv_features, function(feat) {
plot_runscatter(mexp,
variable = "norm_intensity",
include_feature_filter = feat,
qc_types = c("BQC", "SPL")) +
ggtitle(feat)
})
if (requireNamespace("patchwork", quietly = TRUE)) {
patchwork::wrap_plots(plots, ncol = 2)
}Part 4: Feature Pass/Fail Table
qc_table <- mexp@metrics_qc |>
select(feature_id, norm_intensity_cv_bqc, normint_dratio_sd_bqc) |>
mutate(
cv_pass = norm_intensity_cv_bqc <= 30,
dratio_pass = normint_dratio_sd_bqc <= 0.5,
status = case_when(
cv_pass & dratio_pass ~ "PASS",
!cv_pass & !dratio_pass ~ "FAIL (CV + D-ratio)",
!cv_pass ~ "FAIL (CV)",
!dratio_pass ~ "FAIL (D-ratio)"
)
) |>
arrange(desc(norm_intensity_cv_bqc))
qc_table |>
gt::gt() |>
gt::fmt_number(columns = c(norm_intensity_cv_bqc, normint_dratio_sd_bqc), decimals = 1) |>
gt::data_color(
columns = status,
fn = function(x) ifelse(x == "PASS", "#d4e8d4", "#f8d4d4")
) |>
gt::tab_header(title = "Feature QC Summary")Part 5: Calibration Summary (if applicable)
if (nrow(mexp@metrics_calibration) > 0) {
cal_summary <- get_calibration_metrics(mexp) |>
select(feature_id, r2, fit_model, fit_weighting, lowest_cal, highest_cal) |>
arrange(r2)
cal_summary |>
gt::gt() |>
gt::fmt_number(columns = c(r2, lowest_cal, highest_cal), decimals = 4) |>
gt::tab_header(title = "Calibration Curve Metrics")
}Part 6: Flagged Samples
if (length(mexp@analyses_excluded) > 0) {
tibble::tibble(
analysis_id = mexp@analyses_excluded,
reason = "Excluded during processing"
) |> gt::gt() |> gt::tab_header(title = "Excluded Analyses")
}Parameterized Quarto Template
Full template (click to expand)
Save as qc-report-template.qmd:
---
title: "QC Report"
date: today
format:
html:
self-contained: true
toc: true
params:
rds_path: "results/mexp_processed.rds"
---
Then include each Part above as a code chunk in the template.
Render the template:
quarto::quarto_render(
"qc-report-template.qmd",
execute_params = list(rds_path = "results/mexp_processed.rds")
)Tips
- Self-contained HTML — a single file that can be emailed or archived.
- Parameterize the RDS path so the same template works for any study.
-
Date stamp (
date: today) for an audit trail. - Session info at the end for reproducibility.
Next steps
- Basic MRMhub Workflow — full processing before reporting
- External Calibration & QC — calibration workflow
- Troubleshooting & FAQ — common issues