MRManalyzeR processes reviewed targeted LC-MS/MS results, assesses data quality and generates reports. A curated workbook and a YAML configuration define the analysis, so a run is described by two files and can be repeated from them.
It reads Waters TargetLynx exports, Skyline exports, and plain sample-by-analyte matrices from anything else. It does not do peak detection, integration or calibration-curve fitting: those decisions stay with you, in the vendor software.
Everything is carried in a struct
DatasetExperiment: the assay matrix, the sample metadata
and the feature metadata, kept aligned in one object. Any package that
produces or consumes that class can exchange data with this one — see Working with other packages.
if(!requireNamespace("BiocManager", quietly = TRUE))
install.packages("BiocManager")
BiocManager::install("MRManalyzeR")Then load it:
The bundled example of oxylipins in mouse lung writes to a directory you choose:
#> [1] "example_config.yml" "example_data_ng_mL__measured.RDS"
#> [3] "example_data_ng_mL_.RDS" "example_data_ng_mL_.txt"
#> [5] "example_data_ng_mL_.xlsx"
This is what a successful run produces. render = FALSE
skips the two HTML reports, which is what makes it quick here; plain
mrmRunExample() writes those too.
One workbook with three sheets:
| Sheet | One row per | Holds |
|---|---|---|
lcms_data |
see below | the vendor export, as reviewed |
sample_metadata |
injection | which injections to include, group, batch, amounts |
feature_metadata |
compound | which compounds to report, class and pathway annotation |
The data sheet’s layout depends on the data source, and the configuration names which reader to use:
| Source | lcms_data holds |
Configured under |
|---|---|---|
| Waters TargetLynx | the export as saved: stacked compound blocks, one per compound, each with its own header row | tl_data: |
| Skyline | one row per molecule, one column per injection | skyline_data: |
| Anything else | a plain matrix of samples by analytes, or its transpose | matrix_data:, with orientation: |
The two metadata sheets are user curated, MRManalyzeR never writes
them. sample_metadata needs an injection name matching the
export, an inclusion flag, and whatever factors you want to analyse
by:
| Name | Sample_ID | Include | Sample_type | Treatment |
|---|---|---|---|---|
| 20260505_07 | CTRL_1 | YES | Sample | PBS |
| 20260505_08 | CTRL_2 | YES | Sample | PBS |
| 20260505_09 | HDM_1 | YES | Sample | HDM |
Columns beyond those are needed only by the steps that use them, and
only when you switch those steps on: injection and vial volumes for
adjust_conc:, a protein or tissue-weight column for
normalize:, a batch column for
batch_correction:, an injection-order column for the drift
plots, and a sample-type column wherever QCs or blanks are involved. A
run with every processing step off needs only the columns above.
Class and pathway annotations in feature_metadata group
and colour compounds across plots.
newConfig() writes a skeleton YAML to fill for a
project. The short form carries the keys a run cannot do without;
minimal = FALSE writes the fully commented reference
instead, which documents every key the package reads.
#> project:
#> name: my_study
#> researcher: ""
#>
#> # Where the workbook is, and where results go. fn is the workbook name
#> # inside data_dir, without the .xlsx.
#> paths:
#> project_dir: &project_dir "C:/path/to/study"
#> data_dir: &data_dir !concat_path [*project_dir, "data"]
#> result_dir: &result_dir !concat_path [*project_dir, "results"]
#> fn: &fn my_workbook
#> suffix: _run1 # appended to every output name
#>
#> PeakMatrixProcessing:
#> execute: True # False = reuse the stored RDS, re-render only
#>
#> # 1. Data source
#> tl_data:
#> enabled: True
Open that file and work down this list:
paths: – project_dir,
data_dir, result_dir, and fn, the
workbook’s name without .xlsx.data_tab_names
must name your data sheet, and datatype: the value to
report (tl_data:), or signal_filter: the floor
to apply (skyline_data:).sample_meta_tab and its columns –
name_col, include_col,
include_value.feature_meta_tab and its columns –
compound_col, processing_name_col,
report_col, report_value.sample_type_head, qc_label and
sample_labels have to match your sample-type column.Nothing is guessed from the data, so a key that does not match stops the run with a message naming it – useful confirmation that you have the list above right, rather than a substitute for working through it.
Outputs of MRManalyzeR are stored at paths.result_dir,
named after the workbook, the datatype and your suffix:
| File | Holds |
|---|---|
<name>.xlsx |
processed matrix, both metadata sheets, and the compounds removed with the reason |
<name>_stats.xlsx |
tests, correlations, linear models, ratios, group summaries, and a
key sheet defining every column |
<name>.RDS |
the dataset, for re-rendering reports without reprocessing |
<name>_measured.RDS |
the same before imputation – written only when
replace_MVs: is set |
<name>.txt |
the parameters as they resolved for this run |
up to two .html reports |
data quality, and statistics, each written only if its block has
execute: True |
<name>_stats.xlsx likewise carries only the
analyses that were enabled: a run with comparisons: off and
correlations: on has no stats sheet.
The term measured means processed but not imputed: the values as they come out of the filters, batch correction, normalisation and concentration adjustment, with the gaps still gaps. It does not mean the untouched instrument output.
In <name>.xlsx, the two sheets to read first lead
with the answer. After the compound name, feature_metadata
gives Lowest_measured, and removed_features
gives Failed_step, naming what took the compound out
(User exclude, Detection filter,
Signal filter (LOD), Never measured, and so
on).
Lowest_measured is the smallest value for that compound
across the biological samples – QCs and blanks excluded
– taken after all processing but before imputation, in the units the run
reports (Area, ng/mL, and after normalisation
or concentration adjustment where those are on). It is rounded to the
nearest whole number.
The return value holds the same objects:
Four blocks, in the order they run.
project:
paths: # workbook in, results out
PeakMatrixProcessing: # filters, batch correction, normalisation, imputation
data_quality_report: # %RSD, drift, normality, QC PCA
stats_report: # comparisons, PCA, volcano, heatmaps, ratiosTwo points decide how a run reads, and both are recorded in the output:
Processing order is fixed. Therefore, blank filter, then detection filters, then batch correction, then normalisation and concentration adjustment, and imputation last: batch correction works on the instrument signal and imputation on the corrected signal.
Imputed values stay out of the statistics as
default. With replace_MVs: set, the exported
matrix is filled and the measured values are written beside it. The
tests, boxplots and CVs use the measured ones; only the PCA uses the
filled matrix, because a PCA cannot take a gap.
use_imputed: True in a report block changes that for the
whole report.
A compound reaching the reports carries its own record: how many
values were filled in, whether it could be batch-corrected, the fraction
of samples it was detected in. The vignette
vignette("processing-details", package = "MRManalyzeR")
goes through each step, what it decides and how to read what it
recorded.
Data quality answers whether the data is usable: %RSD for QCs and for samples and their ratio, signal against injection order, per-compound normality, a QC PCA coloured by batch, order and sample type, and every compound that was dropped with the reason.
Statistics answers what the study found: group comparisons with the test named per comparison, boxplots and summaries, PCA with loadings coloured by pathway, volcano, heatmaps blocked by pathway, feature correlations and metabolite ratios.
Both are single self-contained HTML files, and each states which matrix it used and how missing values were handled.
When a study runs more than one panel — oxylipins, phospholipids, sphingolipids — each is processed on its own, then merged by sample ID:
The merge reads each panel’s measured values where they were stored, prefixes the compounds by panel so names cannot collide, and runs one statistics report across the lot.
Three things decide what the merged dataset contains:
sample_id_col values present in every panel
survive; a sample one panel missed is dropped from the merge rather than
carried with gaps. No samples in common causes an error.duplicate_samples: first | mean | median resolves them
instead, and a re-injection is better marked Include: "NO"
before processing.feature_meta_cols: and sample_meta_cols:
default to the columns every panel shares; name them explicitly to keep
a particular set. Panels annotated differently therefore merge down to
their common columns.A DatasetExperiment is struct’s
container, which enables the MRManalyzeR output to be used in other
packages, for instance for statistics.
res$datasetExperiment is a DatasetExperiment,
and a DatasetExperiment is also a
SummarizedExperiment, so packages built on either reach the
same data.
Taking the processed dataset into structToolbox
for a PLS-DA. That model is fitted by pls, which
structToolbox suggests rather than requires, so the chunk
below runs only where both are installed:
library(structToolbox)
de <- loadDataset(system.file("extdata", "example_synthetic.RDS",
package = "MRManalyzeR"))
samples <- subsetDataset(de, conditions = list(Sample_type = "Sample"))
# structToolbox wants the response as a factor
sm <- as.data.frame(samples$sample_meta)
sm$Treatment <- factor(sm$Treatment)
samples$sample_meta <- sm
M <- model_apply(PLSDA(number_components = 2, factor_name = "Treatment"),
samples)
round(as.data.frame(M$scores$data)[1:3, ], 2)#> LV1 LV2
#> SYN_002 -12.65 -0.04
#> SYN_003 -20.05 9.41
#> SYN_004 -9.81 15.18
The scores come back as a DatasetExperiment too, so the
exchange runs both ways: anything holding one – a
structToolbox model output, a pmp matrix
wrapped in one – can be handed straight back to any step here, or to
runStats(). A plain matrix with its two metadata frames
goes through assembleDataset() first.
#> R version 4.6.1 (2026-06-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 26.04.1 LTS
#>
#> Matrix products: default
#> BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.32.so; LAPACK version 3.12.0
#>
#> locale:
#> [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
#> [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
#> [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
#> [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
#> [9] LC_ADDRESS=C LC_TELEPHONE=C
#> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
#>
#> time zone: Etc/UTC
#> tzcode source: system (glibc)
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] structToolbox_1.25.0 MRManalyzeR_0.99.7 struct_1.25.0
#> [4] BiocStyle_2.41.0
#>
#> loaded via a namespace (and not attached):
#> [1] SummarizedExperiment_1.43.0 gtable_0.3.6
#> [3] xfun_0.61 bslib_0.12.0
#> [5] ggplot2_4.0.3 httr2_1.3.0
#> [7] htmlwidgets_1.6.4 Biobase_2.73.2
#> [9] lattice_0.23-1 vctrs_0.7.3
#> [11] tools_4.6.1 generics_0.1.4
#> [13] stats4_4.6.1 tibble_3.3.1
#> [15] pkgconfig_2.0.3 Matrix_1.7-6
#> [17] RColorBrewer_1.1-3 S7_0.2.2
#> [19] S4Vectors_0.51.10 lifecycle_1.0.5
#> [21] farver_2.1.2 compiler_4.6.1
#> [23] stringr_1.6.0 janitor_2.2.1
#> [25] Seqinfo_1.3.2 snakecase_0.11.1
#> [27] htmltools_0.5.9 sys_3.4.3
#> [29] buildtools_1.0.0 sass_0.4.10
#> [31] yaml_2.3.12 pillar_1.11.1
#> [33] jquerylib_0.1.4 tidyr_1.3.2
#> [35] DT_0.34.0 DelayedArray_0.39.8
#> [37] cachem_1.1.0 abind_1.4-8
#> [39] pls_2.9-0 tidyselect_1.2.1
#> [41] zip_3.0.2 digest_0.6.39
#> [43] stringi_1.8.9 dplyr_1.2.1
#> [45] purrr_1.2.2 ggthemes_7.0.0
#> [47] maketools_1.3.2 fastmap_1.2.0
#> [49] grid_4.6.1 cli_3.6.6
#> [51] SparseArray_1.13.4 magrittr_2.0.5
#> [53] S4Arrays_1.13.2 withr_3.0.3
#> [55] scales_1.4.0 sp_2.2-4
#> [57] lubridate_1.9.5 timechange_0.4.0
#> [59] rmarkdown_2.32 XVector_0.53.0
#> [61] matrixStats_1.5.0 otel_0.2.0
#> [63] gridExtra_2.3.1 openxlsx_4.2.9
#> [65] evaluate_1.0.5 knitr_1.52
#> [67] GenomicRanges_1.65.4 IRanges_2.47.5
#> [69] rlang_1.3.0 Rcpp_1.1.2
#> [71] glue_1.8.1 BiocManager_1.30.27
#> [73] BiocGenerics_0.59.12 jsonlite_2.0.0
#> [75] R6_2.6.1 MatrixGenerics_1.25.0