--- title: "Plotting xcms results using lcmsPlot" package: xcms output: BiocStyle::html_document: toc_float: true includes: in_header: xcms-plotting-using-lcmsPlot.bioschemas.html vignette: > %\VignetteIndexEntry{Plotting xcms results using lcmsPlot} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} %\VignetteDepends{xcms,lcmsPlot,faahKO,RColorBrewer,BiocStyle} %\VignettePackage{xcms} %\VignetteKeywords{mass spectrometry, metabolomics} bibliography: references.bib csl: biomed-central.csl --- ```{r biocstyle, echo = FALSE, results = "asis"} BiocStyle::markdown() ``` **Package**: `r Biocpkg("xcms")`
**Authors**: Ossama Edbali, Johannes Rainer
**Modified**: `r file.info("xcms-plotting-using-lcmsPlot.Rmd")$mtime`
**Compiled**: `r date()` ```{r init, message = FALSE, warning = FALSE, echo = FALSE, results = "hide"} ## Silently loading all packages library(BiocStyle) library(xcms) library(lcmsPlot) register(SerialParam()) ``` # Introduction Visualization of mass spectrometry data is an important component of LC-MS data analysis, both for quality control and for interpretation and communication of results. Throughout a metabolomics or proteomics workflow, visualization can help identify problems with data acquisition and processing, such as retention-time shifts, signal drift, differences in signal intensity between samples, and incorrect or poorly defined chromatographic peaks. It also provides an essential means of inspecting detected features and communicating results. *xcms* provides a range of visualization functions for inspecting LC-MS data and the results of chromatographic peak detection, retention-time correction, and feature definition. These functions cover many common quality-control and diagnostic use cases and are closely integrated with the *xcms* data structures and processing workflow. For more complex visualizations, however, users may benefit from the flexibility of the *ggplot2* grammar of graphics, particularly when combining multiple samples, annotations, experimental metadata, or several graphical layers into a single figure. `r Biocpkg("lcmsPlot")` has been developed to facilitate the visualization of LC-MS raw and processed data in a consistent and flexible manner across different software tools and processing workflows. By providing a common *ggplot2*-based framework for LC-MS visualization, it enables users to apply a consistent graphical approach to data generated or processed by different packages and at different stages of an analysis workflow. This vignette illustrates how *lcmsPlot* can be used together with *xcms* to generate flexible, reproducible, and publication-ready visualizations of LC-MS data. For a more thorough description of the package, including its support for raw files and for the outputs of other LC-MS software, see the [lcmsPlot vignette][lcmsplot-vignette]. [lcmsplot-vignette]: https://bioconductor.org/packages/release/bioc/vignettes/lcmsPlot/inst/doc/lcms_data_plotting.html # Load the packages *lcmsPlot* is a Bioconductor package and can be installed with `r CRANpkg("BiocManager")`: ```{r install-lcmsPlot, eval = FALSE} if (!requireNamespace("BiocManager", quietly = TRUE)) install.packages("BiocManager") BiocManager::install("lcmsPlot") ``` ```{r load-packages, message = FALSE} library(xcms) library(lcmsPlot) ``` # Load the data Throughout this vignette we use the preprocessing result that is shipped with *xcms* and that is described in detail in the main [xcms vignette][xcms-vignette]. It is derived from a subset of the data from [@Saghatelian04]; the raw files are provided by the _faahKO_ package. [xcms-vignette]: https://bioconductor.org/packages/release/bioc/vignettes/xcms/inst/doc/xcms.html Using `loadXcmsData()` we get the fully preprocessed object directly, so that chromatographic peak detection, alignment and correspondence do not have to be repeated here. ```{r load-data, message = FALSE} xdata <- loadXcmsData("xdata") ``` The object contains eight samples assigned to the two sample groups `"KO"` and `"WT"`: ```{r sample-data} Biobase::pData(xdata) ``` # Plot samples summaries Base peak chromatograms, total ion chromatograms, and total ion current plots are useful in LC-MS because they simplify complex datasets, highlight the most intense signals, and enable rapid detection and comparison of chromatographic features. In the examples below we plot the base peak and total ion chromatograms for the eight samples (faceted): ```{r plot-bpc, fig.height=6, fig.width=10} lcmsPlot(xdata, sample_id_column = "sample_name") + lp_chromatogram(aggregation_fun = "max") + lp_facets(facets = "sample_name", ncol = 4) ``` ```{r plot-tic, fig.height=6, fig.width=10} lcmsPlot(xdata, sample_id_column = "sample_name") + lp_chromatogram(aggregation_fun = "sum") + lp_facets(facets = "sample_name", ncol = 4) ``` We can also plot multiple chromatograms in the same plot, overlapped. In the example below, base peak chromatograms are computed for each sample, grouped by sample group, and displayed together in one panel. ```{r bpc-overlapped} lcmsPlot(xdata, sample_id_column = "sample_name") + lp_chromatogram(aggregation_fun = "max") + lp_arrange(group_by = "sample_group") + lp_labels(title = "Base peak chromatograms", legend = "Sample group") + lp_legend(position = "bottom") ``` Below, we generate a total ion current plot for the eight samples under consideration: ```{r total-ion-current} lcmsPlot(xdata, sample_id_column = "sample_name") + lp_total_ion_current(type = "violin") + lp_arrange(group_by = "sample_group") + lp_labels(title = "Total ion current", legend = "Sample group") ``` These plots show the overall amount of ion signal entering the detector at any moment, widely used for quality control and method assessment. # Plot extracted ion chromatograms ```{r plot-xics} lcmsPlot(xdata, sample_id_column = "sample_name") + lp_chromatogram(features = rbind(c( mzmin = 334.9, mzmax = 335.1, rtmin = 2700, rtmax = 2900))) + lp_arrange(group_by = "sample_group") + lp_labels(legend = "Sample group") ``` The above plot can be gridded on metadata factors; in the plot below we arrange `feature_id` along the rows and `sample_id` along the columns with `lp_grid()`, and detected chromatographic peaks are highlighted using the `highlight_peaks` parameter: ```{r plot-xic-faceted, fig.height=5, fig.width=14} lcmsPlot(xdata, sample_id_column = 'sample_name') + lp_chromatogram( features = rbind( mz334 = c( mzmin = 334.9, mzmax = 335.1, rtmin = 2700, rtmax = 2900), mz278 = c( mzmin = 278.99721, mzmax = 279.00279, rtmin = 2740, rtmax = 2840) ), highlight_peaks = TRUE, highlight_peaks_color = '#f00') + lp_grid(rows = 'feature_id', cols = 'sample_id', free_y = TRUE) ``` ## The same plot with base R graphics The same figure can be produced with the plotting functions of *xcms* itself. The extracted ion chromatograms are first created with the `chromatogram()` function, which returns an `XChromatograms` object with one chromatogram per feature (row) and sample (column), and the detected chromatographic peaks attached to each of them. ```{r plot-xic-base-r, fig.height=5, fig.width=12} mzr_xic <- rbind(c(334.9, 335.1), c(278.99721, 279.00279)) rtr_xic <- rbind(c(2700, 2900), c(2740, 2840)) chrs_xic <- chromatogram(xdata, mz = mzr_xic, rt = rtr_xic) ## Define one color per sample group group_colors <- RColorBrewer::brewer.pal(3, "Set1")[1:2] names(group_colors) <- c("KO", "WT") sample_colors <- group_colors[chrs_xic$sample_group] ## Label the rows the same way lcmsPlot names its features feature_ids <- sprintf("M%dT%d", round(rowMeans(mzr_xic)), round(rowMeans(rtr_xic))) ## One panel per feature (row) and sample (column) par(mfrow = c(nrow(chrs_xic), ncol(chrs_xic)), mar = c(3, 3, 1.8, 0.4), mgp = c(1.9, 0.6, 0), cex = 0.7) for (i in seq_len(nrow(chrs_xic))) { for (j in seq_len(ncol(chrs_xic))) { plot(chrs_xic[i, j], col = sample_colors[j], peakType = "polygon", peakCol = paste0(sample_colors[j], 60), peakBg = paste0(sample_colors[j], 40), main = paste(feature_ids[i], chrs_xic$sample_name[j])) } } ``` The result is equivalent to the `lp_grid()` figure above, but the layout has to be set up explicitly: `par(mfrow = ...)` defines the grid, the two nested loops address the individual cells `chrs_xic[i, j]` of the `XChromatograms` object, and the panel titles and per-sample colors have to be assembled by hand. Each panel also keeps its own y axis, which corresponds to `free_y = TRUE` in `lp_grid()`. In *lcmsPlot*, you can also plot directly from an `XChromatograms` object. In the example below, two features are defined by their m/z ranges and the corresponding retention time windows. The resulting plot is arranged as a grid with features as rows and samples as columns. ```{r plot-XChromatograms, message=FALSE, fig.height=4, fig.width=10} mzr <- matrix(c(344, 344, 360, 360), ncol = 2, byrow = TRUE) rtr <- matrix(c(2500, 2880, 2600, 2880), ncol = 2, byrow = TRUE) chrs <- chromatogram(xdata, mz = mzr, rt = rtr) rownames(chrs) <- c("mz344", "mz360") lcmsPlot(chrs) + lp_chromatogram(na.rm = TRUE) + lp_grid(rows = "feature_id", cols = "sample_id") ``` # Quality control of peak detection `lp_peak_count_image()` bins the retention-time axis and shows how many chromatographic peaks each sample yielded in each bin, with samples on the y axis and one tile per sample and bin. It is the counterpart of `xcms::plotChromPeakImage()`. ```{r peak-count-image, fig.height=4, fig.width=9} lcmsPlot(xdata, sample_id_column = "sample_name") + lp_peak_count_image(bin_size = 30) + lp_labels(title = "Chromatographic peaks per 30 s bin") ``` `lp_chrom_peak_rects()` draws one rectangle per detected peak, spanning its retention-time limits by its m/z limits, and shows where in the m/z / retention-time plane the peak detection actually placed its boundaries. ```{r chrom-peak-rects-standalone, fig.height=5, fig.width=9} lcmsPlot(xdata, sample_id_column = "sample_name") + lp_chrom_peak_rects( sample_ids = c("ko15", "wt15"), rt_range = c(3200, 3700), mz_range = c(480, 540), fill = "#c0392b60") ``` # Plot grouped peaks across samples (features) The correspondence analysis performed on this data set grouped the chromatographic peaks of the individual samples into *features*, i.e. groups of peaks that likely originate from the same ion. Each feature is identified by the corresponding row name of `featureDefinitions(xdata)`, by default `"FT001"`, `"FT002"`, and so on. These identifiers can be passed to the `features` parameter of `lp_chromatogram()` to extract and plot the corresponding chromatograms across all samples without having to specify m/z and retention time ranges manually. The `ppm` and `rt_tol` parameters define how far around the feature's consensus m/z and retention time the signal is extracted. ```{r plot-features} lcmsPlot(xdata, sample_id_column = 'sample_name') + lp_chromatogram( features = c('FT002', 'FT004', 'FT019', 'FT027'), ppm = 10, rt_tol = 80, highlight_peaks = TRUE, highlight_peaks_factor = "sample_group") + lp_arrange(group_by = 'sample_group') + lp_facets(facets = 'feature_id', ncol = 2, free_x = TRUE, free_y = TRUE) + lp_labels(title = "Four selected features", legend = "Sample") + lp_legend(position = "bottom") ``` ## Plot peak density alongside chromatograms Peak density plots show how the detected chromatographic peaks of an m/z window are distributed along the retention time axis. Each detected peak is drawn as a point at the y position of its sample and the kernel density estimate of the peak apex retention times is overlaid as a line. This is the diagnostic used by the peak density correspondence method and makes it easy to judge whether the peaks of the different samples are well aligned and whether a feature is tightly defined. It is the *lcmsPlot* counterpart of `xcms::plotChromPeakDensity()`. When `lp_peak_density()` follows `lp_chromatogram()`, the `features` argument is inherited from the chromatogram layer and does not have to be repeated. The `bw` parameter should match the bandwidth used during correspondence, and when `min_fraction` is given the grouping is simulated so that the retention time regions that would be defined as features are drawn as semi-transparent rectangles. ```{r peak-density} lcmsPlot(xdata, sample_id_column = "sample_name") + lp_chromatogram( sample_ids = c("ko15", "ko16", "wt16", "wt21"), features = rbind(c( mzmin = 334.9, mzmax = 335.1, rtmin = 2700, rtmax = 2900)), highlight_peaks = TRUE ) + lp_peak_density( bw = 30, min_fraction = 0.5 ) + lp_labels(legend = "Sample ID") ``` # Plot spectra Spectra are added to a plot with the `lp_spectra()` function. Its `mode` parameter determines which scan a spectrum is extracted from. ## Select the closest scan to a specified retention time With `mode = "closest"` the spectrum is taken from the scan closest to the retention time given by the `rt` parameter. The retention time at which the spectrum was acquired is marked with a vertical dashed line on the chromatogram, so that the spectrum can be related to the chromatographic peak it belongs to. ```{r plot-spectra-closest-rt, fig.height=5, fig.width=9} lcmsPlot(xdata, sample_id_column = "sample_name") + lp_chromatogram( features = "FT034", sample_ids = "ko15", ppm = 20, rt_tol = 60, highlight_peaks = TRUE) + lp_spectra(rt = 2789, mode = "closest", ms_level = 1) + lp_labels(title = "Feature FT034", legend = "Sample") + lp_legend(position = "bottom") ``` ## Select the scan closest to a detected peak apex With `mode = "closest_apex"` the retention time of the peak maximum is determined from the chromatographic peaks detected by *xcms* and the nearest scan is used, which removes the need to specify a retention time manually. In the example below, chromatograms are extracted for two features in one sample and the MS1 spectrum at the apex of each is displayed below its chromatogram. The `lp_layout()` function is used to give the spectra more vertical space than the chromatograms. ```{r plot-spectra-closest-apex, fig.height=5, fig.width=10} lcmsPlot(xdata, sample_id_column = "sample_name") + lp_chromatogram( features = c("FT219", "FT250"), sample_ids = "ko15", ppm = 20, rt_tol = 60, highlight_peaks = TRUE) + lp_spectra(mode = "closest_apex", ms_level = 1) + lp_facets(facets = "feature_id", ncol = 2) + lp_labels(legend = "Sample") + lp_legend(position = "bottom") + lp_layout(design = "C\nS\nS") ``` Each spectrum is dominated by the m/z of its own feature, accompanied by the corresponding isotope peaks. ## Select multiple scans across a detected peak To follow how the spectral composition changes over the duration of a chromatographic peak, several scans can be extracted across it with `mode = "across_peak"`. The `interval` parameter defines the retention time spacing (in seconds) between the selected scans. ```{r plot-spectra-across-peak, fig.height=10, fig.width=6} lcmsPlot(xdata, sample_id_column = "sample_name") + lp_chromatogram( features = "FT083", sample_ids = "ko15", ppm = 20, rt_tol = 60, highlight_peaks = TRUE) + lp_spectra(mode = "across_peak", interval = 15, ms_level = 1) + lp_labels(legend = "Sample") + lp_legend(position = "bottom") + lp_layout(design = "C\nS\nS") ``` This view makes co-elution visible: the ion of the feature dominates the spectrum at the beginning of the peak, while towards its end other ions become the most intense signals of the scan. ## Plot standalone spectra Spectra can also be plotted on their own, without an accompanying chromatogram, which is useful for a detailed comparison of isotope patterns or relative ion intensities between samples. Standalone spectra are created by using `lp_spectra()` without `lp_chromatogram()`. ```{r plot-standalone-spectra, fig.height=5, fig.width=9} lcmsPlot(xdata, sample_id_column = "sample_name") + lp_spectra( sample_ids = c("ko15", "wt15"), rt = 3323, mode = "closest", ms_level = 1) + lp_labels(title = "MS1 spectra at 3323 seconds") + lp_legend(position = "bottom") ``` # Session information ```{r sessionInfo} sessionInfo() ```