library(MsExperiment)
library(xcms)
library(tidyverse)
library(plotly)
The objective of this demo is to show how xcms can be
used to read the results of fragmentation experiments and using of them
to perform annotation.
Annotation is the process of going back from features to compounds and it can be considered one (or may be the bigger) challenge in metabolomics. In real life world, annotation is an effort which integrate a lot of different expertise ranging from chemistry, informatics, chemoinformatics,….
Annotation in MS is particularly challenging due to the fact that a mass spectrometer is basically a tool able to measure only mass to charge ratios … I mean can you imagine how difficult would be to distinguish people on the bases only of their weight?
Fragmentation experiments, often called MS/MS, are an essential tool for this task. They can be used either to deduce information on the chemical characteristics of unknown compounds, or to confirm the annotation when authentic chemical standards are available.
In this second case, the typical workflow is to inject the standards with the same analytical pipeline used for the samples, and then use m/z, rt and MS/MS to confirm the presence of a given compound inside my samples.
The importance of having reliable and efficient databases of chemical standards cannot be overlooked and the community is putting a lot of effort on that in terms of data organization, standardization and development of specific software libraries.
Let’s suppose that having a library of standards in our lab, we injected them with the same chromatography used for the wines. Among the other compounds, our library contains 3 types of procyanidins B1, B2, B3. They are rather large polyphenols widely present in many matrices of plant origin.
Suppose that our analytical facility told us that they were analyses (with other compounds) in three injections:
We also know that in negative ion mode all these three molecules, will mainly yield the [M-H]- ion in negative ion mode MS (m/z 577.1352).
Our task now is to confirm (or not) the presence of the three of them in the wine samples. Let’s start with B1 …
First of all we have to identify the retention time of the standard. This could be done manually, or automatically. The manual way is to look for peaks in the extracted ion trace of the (577.1352) ion and identify the position of the peak.
The automatic way is to do peak picking … but this will be part of your assignment ;-)
std_files <- list.files("../data/wines/", pattern = "mix", full.names = TRUE)
std_raw <- readMsExperiment(
spectraFiles = std_files, mode = "ondisk",
msLevel. = 1 ## only full scan
)
Since we know what we are looking for let’s get the extracted ion trace
proc_chrom <- chromatogram(std_raw, mz = 577.1352 + 0.02*c(-1, 1))
plot(proc_chrom, col = seq(3)+1)
So three large peaks are visible in the three files, just to have an idea and spot the right retention time let’s look to the three profiles in interactive mode:
ggplotly(ggplot() +
geom_line(aes(x = rtime(proc_chrom[1,1]), intensity(proc_chrom[1,1])), col = "red") +
geom_line(aes(x = rtime(proc_chrom[1,2]), intensity(proc_chrom[1,2])), col = "green") +
geom_line(aes(x = rtime(proc_chrom[1,3]), intensity(proc_chrom[1,3])), col = "blue") +
theme_bw())
So the tentative peak position turns out to be
B3: 242
B2: 267
B1: 234
Let’s find the spectrum on the top of the blue trace:
blue_id <- which(rtime(std_raw) == 233.9871)
We extract the spectra from the MsExperiment object
std_raw_spectra <- spectra(std_raw)
Let’s plot the spectrum
ggplotly(
ggplot() +
geom_segment(aes(x = mz(std_raw_spectra[blue_id])[[1]],
xend = mz(std_raw_spectra[blue_id])[[1]],
y = 0,
yend = intensity(std_raw_spectra[blue_id])[[1]])) +
theme_bw()
)
So the full scan does not give a clear fingerprint of the fragmentation spectrum, to understand that we have to query MS2 spectra:
std_MS_MS <- readMsExperiment(
spectraFiles = std_files, mode = "ondisk",
msLevel. = 2 ## only fragmentation
)
The question to be addressed, now is: do we have fragmentation spectra of the ion around 577?
The list of precursor ions can be directly accessed with
std_MS_MS %>%
spectra() %>%
precursorMz() %>%
.[1:10]
## [1] NA 330.8706 312.8599 NA 330.8705 312.8599 NA 194.9464
## [9] 176.9360 NA
But two handy methods can be used to filter the list of precursors
focus_MS_MS <- std_MS_MS %>%
spectra() %>%
filterPrecursorMzValues(577.1352, ppm = 10) %>%
filterRt(rt = c(224,280))
focus_MS_MS
## MSn data (Spectra) with 4 spectra in a MsBackendMzR backend:
## msLevel rtime scanIndex
## <integer> <numeric> <integer>
## 1 2 243.048 530
## 2 2 269.175 578
## 3 2 232.841 506
## 4 2 237.791 515
## ... 34 more variables/columns.
##
## file(s):
## x015_mix02_2_NEG_DDA.mzML
## x016_mix03_2_NEG_DDA.mzML
## x017_mix04_2_NEG_DDA.mzML
## Processing:
## Filter: select spectra with precursor m/z matching 577.1352 [Mon Nov 10 14:51:14 2025]
## Filter: select retention time [224..280] on MS level(s) [Mon Nov 10 14:51:14 2025]
So here we have four spectra compatible with the rules …Let’s look to their retention times
rtime(focus_MS_MS)
## [1] 243.0483 269.1749 232.8409 237.7909
The first two are OK (File1 and File2), for the third file we have two fragmentation. This is not unreasonable since the blue peak is large, the ion at m/z 577 was, most likely fragmented in two consecutive scans. To understand which is the good one one should rely on DBs or interpretation of mass spectra.
Let’s now make a nice plot with the MS/MS spectra:
mz_focusMSMS <- mz(focus_MS_MS)
i_focus_MSMS <- intensity(focus_MS_MS)
plot(mz_focusMSMS[[1]],
i_focus_MSMS[[1]]/max(i_focus_MSMS[[1]]),
type = "h",
xlab = "mz", col = "red",
ylab = "normalized Intensity")
points(mz_focusMSMS[[2]]+2, i_focus_MSMS[[2]]/max(i_focus_MSMS[[2]]), col = "green", type = "h")
points(mz_focusMSMS[[4]]-2, i_focus_MSMS[[4]]/max(i_focus_MSMS[[4]]), col = "blue", type = "h")
So, as expected the three procyanidins are giving substantially the same fragmentation spectrum and are eluting at three different retention times.
Try to check if these compounds are visible inside one of our wine samples. Hints,