Visualisation functions
Source:vignettes/articles/manual-08-visualization.Rmd
manual-08-visualization.RmdManual
MRMhub provides plotting functions covering every stage of the
workflow. All functions return ggplot2 objects (or, for
paged outputs, a list of ggplot2 objects), so the standard
ggplot2 grammar can be used to further customise titles, themes, scales,
and facets. The functions below are grouped by the workflow stage at
which they are most useful.
Overview
| Function | Stage | Purpose |
|---|---|---|
plot_runsequence() |
Acquisition design | Run-order overview of QC types and batches |
plot_runscatter() |
Drift / batch QC | Per-feature scatter of values vs analysis order |
plot_abundanceprofile() |
Abundance overview | Feature abundance distribution per class |
plot_rla_boxplot() |
Normalisation QC | Relative log-abundance boxplots per analysis |
plot_normalization_qc() |
Normalisation QC | Before/after ISTD normalisation comparison |
plot_pca() |
Multivariate QC | PCA score plot |
plot_pca_loading() |
Multivariate QC | PCA loadings |
plot_feature_correlations() |
Multivariate QC | Feature-feature correlation heatmap |
plot_qcmetrics_comparison() |
QC metrics | CV / bias comparison across QC types |
plot_qc_summary_byclass() |
QC filtering | Pass/fail summary by feature class |
plot_qc_summary_overall() |
QC filtering | Pass/fail summary across the whole dataset |
plot_calibrationcurves() |
External calibration | Calibration curves with fit and residuals |
plot_responsecurves() |
Response curves | Linearity check from a dilution series |
plot_rt_vs_chain() |
Method check (lipidomics) | RT vs chain length / unsaturation |
plot_matrixeffects() |
Method check | Matrix-effect QC |
plot_interference_correction() |
Interference correction | QC overview for interference annotations |
Acquisition design
plot_runsequence(): sequence overview
The run-sequence plot summarises the acquisition design: QC type positions, batch boundaries, and the analysis timeline. It is an experiment-level plot (no per-feature dimension).
plot_runsequence(mexp,
qc_types = NA,
show_batches = TRUE,
batch_zebra_stripe = TRUE,
show_timestamp = FALSE)Set show_timestamp = TRUE to use the acquisition
timestamp on the x-axis; this helps identify interruptions between or
within batches that would not be visible against the analysis sequence
number.
Drift, batch, and per-feature inspection
plot_runscatter(): values vs analysis order
The primary plot for drift and batch QC. Plots a feature variable (intensity, normalised intensity, concentration) against the analysis order, optionally with fitted drift trends from the correction functions. Returns one panel per feature (paged).
plot_runscatter(mexp,
variable = "norm_intensity",
qc_types = c("BQC", "SPL"),
rows_page = 1,
cols_page = 3,
show_trend = TRUE)Use variable = "intensity_before" /
"conc_before" to inspect pre-correction values, and
variable = "intensity" / "conc" for
post-correction. Setting output_pdf = TRUE and a
path writes a multi-page PDF, which is practical for large
feature lists.
plot_abundanceprofile(): feature abundance
distribution
Shows the abundance distribution of each feature, grouped by class. Useful for inspecting class coverage and identifying features at the limits of the dynamic range.
plot_abundanceprofile(mexp,
variable = "intensity",
qc_types = "SPL",
log_scale = TRUE)When use_qc_metrics = TRUE, the function reads from the
pre-computed metrics_qc table (much faster on large feature
lists) and qc_types must specify a single QC type.
Normalisation QC
plot_rla_boxplot(): relative log-abundance per
analysis
A boxplot per analysis of
log2(value / median_across_analyses). Width and centring of
the box reflect injection-level variability. Useful before and after
normalisation to confirm that ISTD correction removes injection-level
bias.
plot_rla_boxplot(mexp,
variable = "norm_intensity",
qc_types = c("BQC", "SPL"))
plot_normalization_qc(): before-vs-after
comparison
Compares pre- and post-normalisation values for QC samples in three
layouts (plot_type): "scatter" (point cloud),
"diff" (signed difference), "ratio" (after /
before). CV of QC samples should decrease after normalisation; an
increase typically indicates an incorrect ISTD pairing.
plot_normalization_qc(mexp,
before_norm_var = "intensity",
after_norm_var = "norm_intensity",
plot_type = "scatter",
qc_types = c("BQC", "SPL"),
facet_by_class = TRUE,
cv_threshold_value = 25)Multivariate QC
plot_pca(): score plot
Two-dimensional PCA score plot with confidence ellipses grouped by
qc_type, batch_id, or "none". See
QC exploration with PCA for
an interpretation walk-through.
plot_pca_loading(): loadings
Loadings on the first two PCs. Features at the extremes drive sample
separation; a single feature dominating PC1 should be inspected with
plot_runscatter() before being attributed to biology.
plot_pca_loading(mexp,
variable = "norm_intensity",
qc_types = c("BQC", "SPL"))
plot_feature_correlations(): correlation heatmap
Pairwise correlation matrix across features, useful for identifying redundant transitions and flagging candidate isobaric interferences.
plot_feature_correlations(mexp,
variable = "norm_intensity",
qc_types = "SPL")QC metrics and filtering
plot_qcmetrics_comparison(): pairwise QC-metric
scatter
Plots one QC metric against another, computed across features. The
variable names follow the pattern {variable}_cv_{qctype}
(e.g. intensity_cv_bqc, norm_intensity_cv_tqc)
and live in mexp@metrics_qc after
calc_qc_metrics() is run.
mexp <- calc_qc_metrics(mexp)
plot_qcmetrics_comparison(mexp,
plot_type = "scatter",
x_variable = "intensity_cv_bqc",
y_variable = "norm_intensity_cv_bqc",
equality_line = TRUE,
threshold_values = 25,
log_scale = FALSE)Points below the equality line indicate features for which CV was reduced by normalisation; points above indicate features made worse, typically a misassigned ISTD.
plot_qc_summary_byclass() /
plot_qc_summary_overall(): filter outcome
After filter_features_qc(), these two functions
summarise how many features passed each filter rule, broken down by
feature class (_byclass) or aggregated
(_overall).
plot_qc_summary_overall(mexp)
plot_qc_summary_byclass(mexp)External calibration and response curves
plot_calibrationcurves(): calibration fits
After quantify_by_calibration(), plots calibration
response vs concentration with the fitted model, the calibration points,
and points excluded from the fit. Returns a paged grid of features.
plot_calibrationcurves(mexp,
variable = "norm_intensity",
qc_types = NA)The fit model is taken from the calibration setup unless overridden
with fit_overwrite = "linear" or
"quadratic".
plot_responsecurves(): RQC linearity
Plots feature response across an RQC dilution series. The fitted
slope and R² values report on the linearity of the assay in the range of
the QC pool. See get_response_curve_stats() for the numeric
summary.
plot_responsecurves(mexp,
variable = "intensity")Method-specific checks
plot_rt_vs_chain(): retention time vs chain length
(lipidomics)
For lipidomics methods using class-based chromatography, plots RT against carbon number, separated by class. Outliers from the expected linear relationship within a class point to mis-identified features.
plot_rt_vs_chain(mexp)
plot_matrixeffects(): matrix-effect overview
Compares ISTD response in matrix-containing QCs against solvent-only injections to flag matrix-effect outliers.
plot_matrixeffects(mexp)
plot_interference_correction(): interference
annotations
QC overview for features with interference annotations (see Interference correction).
Customisation and export
Text size, legend placement and legend sizing are controlled directly
through shared arguments (font_base_size,
legend_position, legend_size, and, on the core
QC plots, strip_text_size, show_legend_title,
legend_bg_alpha); paged and faceted plots size their text
and points automatically from cols_page. See Customising plots for the full argument
reference.
Customising plots
Every plot_*() function returns a ggplot2
object built from a shared house theme (faint grey gridlines, a thin
panel border, dark-navy facet strips). A small set of arguments exposes
the common appearance choices, so a balanced figure usually comes from
the plotting call alone, without a trailing
+ theme(...).
The shared arguments
Every plotting function accepts the three common arguments. The
higher-use QC plots (plot_pca(),
plot_pca_loading(), plot_runscatter(),
plot_rla_boxplot(), plot_normalization_qc()
and plot_qcmetrics_comparison()) accept further arguments
for finer legend and facet control.
| Argument | Applies to | Effect |
|---|---|---|
font_base_size |
all plots | Base font size in points; all plot text scales proportionally |
legend_position |
all plots | Legend placement (keyword, corner, or coordinate) |
legend_size |
all plots | Scales the whole legend: text, title, key and plotted symbols |
show_legend_title |
all plots |
FALSE hides the legend title |
title |
all plots | A string sets the plot title; NULL/NA show
none |
strip_text_size |
faceted plots | Facet strip label size |
strip_bg_color |
faceted plots | Facet strip background fill (strip text auto-contrasts light/dark) |
legend_bg_alpha |
core QC plots | Opacity of a white background box behind an inside legend |
aspect_ratio |
plot_pca() |
Panel aspect ratio; default 1 (square score plot) |
Arguments left at their default (NULL, or the
per-function default) leave that aspect of the plot at the house
setting. Setting one overrides only that property.
Setting defaults for a whole notebook
The same appearance choices can be made once, for every subsequent
plot, with mrmhub_set_plot_defaults(), convenient when a
document targets a particular medium (a smaller base font and point size
for a dense multi-panel report, say) and the choice should apply
throughout without repeating it on each call.
mrmhub_set_plot_defaults(font_base_size = 8, point_size = 0.8)The resolution order for every argument is: a value passed explicitly
to a plotting function wins, then the global default set here, then the
function’s built-in default (including the automatic
cols_page sizing on faceted plots). A one-off call
therefore still overrides the global setting:
# uses the global font_base_size = 8 set above
plot_pca(mexp, variable = "norm_intensity")
# this single plot overrides it
plot_pca(mexp, variable = "norm_intensity", font_base_size = 11)mrmhub_get_plot_defaults() reports the active settings
and mrmhub_reset_plot_defaults() clears them. The defaults
are ordinary R options (mrmhub.font_base_size and so on),
so they can also be scoped to a single section with
withr::local_options() rather than set for the whole
session. The arguments that can be set globally are
font_base_size, point_size,
legend_position, legend_size,
show_legend_title, strip_bg_color, and the
units and dpi used by save_plot()
(see Setting the unit
and resolution once). Figure width and height are deliberately not
settable globally: the physical size of a saved figure should be
readable from the call that writes it.
Placing the legend
legend_position accepts the standard ggplot2 keywords,
four corner shortcuts for a legend drawn inside the panel, or a raw
coordinate.
| Value | Result |
|---|---|
"right", "left", "top",
"bottom"
|
Legend outside the panel on that side |
"none" |
No legend |
"inside-tr", "inside-tl",
"inside-br", "inside-bl"
|
Inside the panel, anchored to that corner |
c(x, y) |
Inside the panel at that coordinate
(0–1) |
All plots default to "right". For a legend drawn inside
the panel over the points, a translucent background box
(legend_bg_alpha) keeps it readable:
plot_normalization_qc(mexp,
before_norm_var = "intensity",
after_norm_var = "norm_intensity",
plot_type = "diff",
legend_position = "inside-br",
legend_bg_alpha = 0.6)Sizing text and symbols
font_base_size is the single text knob: increasing it
scales axis text, titles, strip labels and the legend together.
legend_size scales the legend independently: its text,
title, key box, and the plotted symbol (the glyph in the key, which a
plain legend.key.size does not resize). A value of
3 or less is read as a multiplier of
font_base_size; a larger value is an absolute point size.
strip_text_size follows the same convention for facet
labels.
plot_rt_vs_chain(mexp,
font_base_size = 6,
legend_position = "right",
legend_size = 0.8) # legend text and glyphs at 6 * 0.8 ptAutomatic sizing on paged and faceted plots
Plots that lay panels out in a grid (plot_runscatter(),
plot_responsecurves(),
plot_qcmetrics_comparison(),
plot_normalization_qc() and
plot_rt_vs_chain()) size their text and points from the
number of facet columns, since column count is the main driver of panel
size. With autoscale = TRUE (the default),
font_base_size and point_size left unset are
filled from cols_page:
cols_page |
font_base_size |
point_size |
|---|---|---|
| 1–2 | 9 | 1.0 |
| 3 | 7 | 0.7 |
| 4–5 | 6 | 0.5 |
| 6 or more | 5 | 0.4 |
A value passed explicitly always wins, so autoscaling only fills what is left unset:
# 3-column page: text and points sized automatically
plot_runscatter(mexp, variable = "norm_intensity", cols_page = 3)
# keep the automatic font, but set the point size by hand
plot_runscatter(mexp, variable = "norm_intensity", cols_page = 3,
point_size = 1.2)Setting autoscale = FALSE ignores the grid and uses the
single-plot defaults (font_base_size = 11,
point_size = 1.5) for anything left unset. Single-panel
plots such as plot_pca() and
plot_matrixeffects() use font_base_size = 11
by default.
ggplot2 layering
All functions return ggplot2 objects, so anything the
arguments above do not cover (bespoke themes, scales, titles) can be
appended in the usual way, and a trailing theme() overrides
the house theme.
plot_runscatter(mexp, variable = "norm_intensity", qc_types = c("BQC", "SPL")) +
ggplot2::theme_minimal(base_size = 9) +
ggplot2::labs(title = "Normalised intensity (BQC vs SPL)")Saving plots
save_plot() writes any plot to a file at a defined
physical size and resolution. Sizes are given in mm by
default, matching how journals specify figure widths, and
cm, in, pt and px
are also accepted.
p <- plot_pca(mexp,
variable = "norm_intensity",
qc_types = c("BQC", "SPL"),
ellipse_variable = "batch_id")
save_plot(p, "figures/pca_batch.pdf", width = 180, height = 120)The same figure can be written in several formats in one call. Give a path without an extension and list the formats; each file gets the matching extension.
The plot is returned visibly, so piping into save_plot()
still renders the figure in the notebook and saving needs no separate
line:
plot_pca(mexp, variable = "norm_intensity", ellipse_variable = "batch_id") |>
save_plot("figures/pca_batch.pdf", width = 180, height = 120)In a script or a loop, where re-drawing a dense figure is wasted
work, show_plot = FALSE skips it and returns the written
paths instead:
paths <- save_plot(p, "figures/pca_batch.pdf", width = 180, height = 120,
show_plot = FALSE)(With show_plot = TRUE the paths are still available, as
the "paths" attribute of the returned plot.)
save_plot() also accepts what the plot functions return
around a plot: the result list of plot_rla_boxplot() (it
picks up the plot element), a patchwork
composition, and a list of plots, which becomes a multi-page PDF.
pages <- plot_runscatter(mexp, variable = "norm_intensity", return_plots = TRUE)
save_plot(pages, "figures/runscatter_all.pdf", width = 280, height = 200,
show_plot = FALSE)Choosing a format
| Purpose | Format | Device used | Typical dpi
|
|---|---|---|---|
| Journal figure, vector (default choice) | "pdf" |
grDevices::cairo_pdf, else
grDevices::pdf
|
n/a |
| Figure for further editing (Illustrator, Inkscape) | "svg" |
svglite::svglite, else grDevices::svg
|
n/a |
| Slides, Quarto HTML, GitHub | "png" |
ragg::agg_png, else grDevices::png
|
150–300 |
| Journal requiring raster submission | "tiff" |
ragg::agg_tiff, else grDevices::tiff
|
300–600 |
Prefer a vector format for publication: text stays selectable and searchable, and lines stay sharp at any magnification.
Prefer a raster format when a plot draws very many
marks — a plot_runscatter() page covering several thousand
analyses, or a dense plot_pca() score plot. Every point
becomes a separate object in a PDF, so such figures produce very large
files that are slow to open and to typeset. Saving them at 300–600 dpi
instead keeps the file small with no visible loss.
The optional packages ragg and svglite are
used automatically when installed, giving better text rendering,
system-font support and smaller SVG files. Without them the equivalent
grDevices device is used and the output is still correct.
Installing both is recommended:
install.packages(c("ragg", "svglite")).
PDF output uses the cairo device wherever R was built with cairo
support, because plain grDevices::pdf() writes text in a
single-byte encoding and silently transliterates anything outside it —
an en dash becomes -, and ≥ becomes
>=. Unit labels such as µmol/L and
statistical annotations depend on those glyphs surviving.
Setting the unit and resolution once
units and dpi can be set for a whole
notebook through mrmhub_set_plot_defaults(), so only
width and height need repeating. The size
itself is always explicit at the call site.
mrmhub_set_plot_defaults(units = "mm", dpi = 600)
save_plot(p, "figures/pca_batch.png", width = 180, height = 120) # 600 dpiMulti-page PDFs
For paged outputs (plot_runscatter(),
plot_calibrationcurves(),
plot_responsecurves(),
plot_feature_correlations()), the built-in
output_pdf = TRUE and path arguments write a
multi-page PDF directly, rather than iterating in user code.
page_width and page_height set the page size;
without them an A4 page is used, oriented by
page_orientation.
plot_runscatter(mexp,
variable = "norm_intensity",
qc_types = c("BQC", "SPL"),
output_pdf = TRUE,
path = "figures/runscatter_all.pdf",
page_width = 297,
page_height = 210)Combining plots
patchwork composes multiple panels into a single
figure:
library(patchwork)
p_seq <- plot_runsequence(mexp)
p_pca <- plot_pca(mexp, variable = "norm_intensity",
qc_types = c("BQC", "SPL"), ellipse_variable = "batch_id")
p_rla <- plot_rla_boxplot(mexp, variable = "norm_intensity",
qc_types = c("BQC", "SPL"))
p_seq / (p_pca | p_rla)Next steps
-
Exploring QC: RunScatter and
PCA:
plot_runscatter(),plot_pca()and outlier screening -
Drift and batch
correction (tutorial):
plot_runscatter()in context - External calibration & QC: calibration plots in workflow