sneumann / xcms

This is the git repository matching the Bioconductor package xcms: LC/MS and GC/MS Data Analysis
Other
177 stars 81 forks source link

plot chromatograms - Stacking EICs #612

Closed CarlBrunius closed 2 years ago

CarlBrunius commented 2 years ago

Hi guys, When examining multiple EIC for a particular feature, I find it hard to visualize more than a rather low number of samples. In lots of conventional software, there is frequently an option of stacking multiple spectra in the y axis by adding a pseudo-offset between subsequent injections.

Let's say I've identified a feature of interest and want to confirm that the peak looks good. I'd do something like this:

fd <- featureDefinitions(xdata)

# let's say you're interested in features close to mz=201.113 and rt=222.95
target_mz <- 193.131
target_rt <- 258.7440
whichFt <- which(abs(fd$mzmed - target_mz) < 0.001 & abs(fd$rtmed - target_rt) < 3)
ft1 <- fd[whichFt,]

# Plot feature peaks
par(mfrow = c(2, 1))
feature_chroms <- featureChromatograms(xdata, features = whichFt, expandRt = 20, adjustedRtime = F)
plot(feature_chroms, peakType = 'rectangle', peakBg = NA)
feature_chroms <- featureChromatograms(xdata, features = whichFt, expandRt = 20, adjustedRtime = T)
plot(feature_chroms, peakType = 'rectangle', peakBg = NA)

image In this particular example, the peaks look really good. In fact, it looks like the retention adjustment is even making RTs more dissimilar in this particular region :)

But if I'm in a more crowded region with not too good retention time adjustment, or potentially in a region with a lot of isomers, peaks may be a little all over the place. It would be awesome to see them stacked to get a better overview of peaks in the individual injections.

Moreover, the options for expanding mz and rt with custom ranges would be awesome also for feature peaks.

To customize a bit more I tried to do this:

# Extract the chromatographic peak numbers corresponding to your Feature of Interest (FoI)
ft1_peaks <- ft1$peakidx[[1]]

# Extract all chromatographic peaks
cp <- chromPeaks(xdata)

# Restrict chromatographic peaks to the FoI
cp1 <- cp[ft1_peaks,]

# Extract chromatograms
mzr <- target_mz + c(-0.005, 0.005)
rtr <- target_rt + c(-40, 40)
par(mfrow = c(2, 1))
chr <- chromatogram(xdata, mz = mzr, rt = rtr, adjustedRtime = F)
plot(chr, peakType = 'rectangle', peakBg = NA)
chr <- chromatogram(xdata, mz = mzr, rt = rtr, adjustedRtime = T)
plot(chr, peakType = 'rectangle', peakBg = NA)

image

This figure is a bit more tweakable, but I don't get how I could restrict peak boundaries to cp1 only: Let's say there are multiple peaks in the region, where several don't "survive" correspondence. Or I don't want to be visually disturbed by other features in trying to assess the quality of this particular feature. Or RT adjustment is a bit dodgy and I just want the plot to be as clean as possible.

In such cases, i'd like to confirm which were the peaks belonging to a specific feature - and only those - are with rectangles or polygons or whatever. I.e. no other nearby peaks. This would help me to get hands-on quality assessment of the integration and retention adjustment exclusively of the peaks belonging to a specific feature.

Sorry if anything was unclear and thanks for any help / tips you could give me!

jorainer commented 2 years ago

Hi Carl,

Regarding the stacking plot - yes, that should be doable. We have the plotChromatogramsOverlay plot already available (see for example the Compounding of LC-MS features vignette for how that works/looks). At present it stacks however only columns of a XChromatograms and not rows (in other words, it allows you to create an overlay plot of EICs from different ions, but not to create an overlay plot of EICs of the same ion in different samples). Also, at present the stacking is done on the m/z of the EIC. But that's something we could work on.

Regarding the possibility to expand also the m/z range in addition to the retention time range in featureChromatograms. Originally I allowed that possibility. The problem is that if you expand the m/z range the data you're looking at does no longer represent the actual feature you're interested in. The EIC is created by aggregating the intensities in each spectrum for the given m/z range. If you use the m/z of the feature then all is fine, if you increase that m/z range than you're actually no longer looking at the feature you're interested in. This means that the chrom peak rectangles might no longer fit the EIC which is shown.

You can however do all that manually like you've done above, i.e. simply extract a chromatogram specifying a larger m/z. Chrom peaks can be colored in plot,XChromatograms using the peakCol and peakBg parameters of plot. This needs to be of length equal to the number of chrom peaks in a XChromatograms. You could now for example have that parameter containing only NA except for chromatographic peaks of your feature. Code could look like:

chr <- chromatogram(xdata, mz = mzr, rt = rtr, adjustedRtime = F)
peak_col <- rep(NA, nrow(chromPeaks(chr)))
## Assume the Id of your feature is "FT001"
idx <- featureDefinitions(chr)["FT001", "peakidx"][[1L]]
peak_col[idx] <- "red"
plot(chr, peakType = 'rectangle', peakBg = NA, peakCol = peak_col)
CarlBrunius commented 2 years ago

Hi Jo, Clever trick to suppress the non-feature peaks - should've thought of that myself :)

For the stacking plot, I think it would be useful and hopefully if you get around to doing it, I would be very happy :)

For the expansion of mz: You're of course right that it makes sense to limit to the actual feature with the actual mz range.

However, if I want to easily investigate whether a corresponding MS2 would be uniquely attibuted to the feature of interest, I could compare the actual peak to an EIC of a larger mzr (corresponding to the isolation width of the quadrupole in the QTOF) - with the same integration borders as the actual peak.

I could of course achieve this by the manual approach that you suggest, but a call to featureChromatograms with an expandMz argument would be much quicker :)

Sort of:

par(mfrow = c(4, 1))
feature_chroms <- featureChromatograms(xdata, features = whichFt, expandRt = 20, adjustedRtime = T)
plot(feature_chroms, peakType = 'rectangle', peakBg = NA)
feature_chroms <- featureChromatograms(xdata, features = whichFt, expandRt = 20, expandMz = 0.1, adjustedRtime = T)
plot(feature_chroms, peakType = 'rectangle', peakBg = NA)
feature_chroms <- featureChromatograms(xdata, features = whichFt, expandRt = 20, expandMz = 0.25, adjustedRtime = T)
plot(feature_chroms, peakType = 'rectangle', peakBg = NA)
feature_chroms <- featureChromatograms(xdata, features = whichFt, expandRt = 20, expandMz = 0.5, adjustedRtime = T)
plot(feature_chroms, peakType = 'rectangle', peakBg = NA)

In some regions, we're seeing a lot of co-eluting analytes contaminating the MS2. Since we're running DDA, we have limited possibilities of filtering out such contaminating fragments. Being able to easily check the MS1 in such a simple way would be beneficial. Thus, I'd opt to re-open the possibility to expandMz.

jorainer commented 2 years ago

OK, sure, I can add expandMz to featureChromatograms. I've also opened an issue for the stacking plot.

CarlBrunius commented 2 years ago

Thx Jo!

jorainer commented 2 years ago

Just an update: the expandMz has now be merged into the master branch and will be included in the upcoming BioC release.