Skip to content

Repository files navigation

CandyCrunch

DOI License

What is CandyCrunch?

CandyCrunch is a package for predicting glycan structure from LC-MS/MS data. It contains the CandyCrunch model, along with the rest of the inference pipeline and and downstream spectrum processing tools. These are further described in our manuscript Urban et al. (2024) – Predicting glycan structure from tandem mass spectrometry via deep learning published in Nature Methods.

Install CandyCrunch

Development version:

pip install git+https://github.com/BojarLab/CandyCrunch.git

PyPI:

pip install candycrunch

CandyCrunch.ipynb Open In Colab

If you are looking for a convenient and easy-to-run version of the code that does not require any local installations, we have also created a Google Colaboratory notebook.
The notebook contains an example pipeline ready to run, which can be copied, executed, and customised in any way.
The example file included in the notebook is the same as in examples/ and is ready for use in the notebook workflow.

Using CandyCrunch – Command line interface:

If you would like to run our main inference function from the command line, you can do so using the candycrunch_predict command included in this repository.

Requires at a minimum:

--spectra_filepath, type=string(s): one or more filepaths to mzML/mzXML/mgf files or .xlsx files, and/or folders containing mzML/mzXML/mgf files; several files are processed together via `wrap_inference_batch` 
--glycan_class, type=string: the glycan class measured ("N", "O", "lipid"/"free")
--output, type=string: an output filepath ending with `.csv` or `.xlsx`

Optional arguments:

--mode, type=string: mass spectrometry mode; options are 'negative' or 'positive'; default: 'negative' 
--max_charge, type=int: maximum precursor charge to consider; default:3
--sample_prep, type=string: options are 'underivatized', 'permethylated', and 'peracetylated'; default:'underivatized'
--modification, type=string: chemical derivatization of glycans; options are “reduced”, “permethylated”, “2AA”, “2AB” or “custom”; default:”reduced” | |--mass_tag, type=float: only if modification = "custom", mass of custom reducing end tag ; default:None
--lc, type=string: type of liquid chromatography; options are 'PGC', 'C18', and 'other'; default:'PGC'
--trap, type=string: type of mass detector used; options are 'linear', 'orbitrap', 'amazon', and 'other'; default:'linear'
--rt_min, type=float: whether only spectra from a minimum retention time (in minutes) onward should be considered; default:0
--rt_max, type=float: whether only spectra up to a maximum retention time (in minutes) should be considered; default:0
--rt_diff, type=float: maximum retention time difference (in minutes) to peak apex that can be grouped with that peak; default:1.0
--spectra, type=bool: whether to also output the actual spectra used for prediction, as a peak_d column; default:False
--get_missing, type=bool: whether to also organize spectra without a matching prediction but a valid composition; default:False | |--filter_out, type=set: only if get_missing = "True", set of monosaccharide or modification types that is used to filter out compositions (e.g., if you know there is no Pen); default:{'Kdn', 'P', 'HexA', 'Pen', 'HexN', 'Me', 'PCho', 'PEtN'}
--ppm_thresh, type=float: mass tolerance in ppm, used for grouping spectra, matching compositions, and filtering by ppm error; default:300
--pred_thresh, type=float: prediction confidence threshold used for filtering; default:0.01
--crumbs_thresh, type=float: minimum CandyCrumbs annotation score to keep a prediction; default:3
--extra_thresh, type=float: prediction confidence threshold at which to allow cross-class predictions; default:0.2
--frag_num, type=int: number of top fragments to show per spectrum; default:100
--supplement, type=bool: whether to impute observed biosynthetic intermediaries from biosynthetic networks; default:True
--experimental, type=bool: whether to impute missing predictions via database searches etc.; default:True | |--taxonomy_level, type=string: taxonomic level used to restrict the glycan database; default:'Class'
|--taxonomy_filter, type=string: taxon at taxonomy_level used to restrict the glycan database; default:'Mammalia'
--intra_cat_thresh, type=float: required for several files, minutes the RT of a structure can differ from the mean of its group across files
--top_n_isomers, type=int: several files only, number of different isomer groups at each composition to retain; default:5
--n_jobs, type=int: several files only, number of files to process in parallel; default:1
--plot_glycans, type=bool: whether to write the output as an .xlsx file that contains SNFG images of all top1 predictions (a `.csv` output additionally gets an `.xlsx` of the same name); default:False

Basic usage

Important

Users must install CandyCrunch using pip before running the commands below

/Users/xurbja $ candycrunch_predict --spectra_filepath path_to_my_files/file.mzML --glycan_class 'O' --output path_to_my_outputs/output_file.csv 

Several files, or a folder of mzML/mzXML/mgf files, are harmonized across runs with wrap_inference_batch. The output file then holds a feature table with one row per isomer group (top1 prediction, consensus m/z and RT, number of files with MS2 evidence) and its relative abundance and evidence (MS2 or MS1-only) in every file, and each file's predictions are written next to it as output_file_<file name>.csv:

/Users/xurbja $ candycrunch_predict --spectra_filepath path_to_my_files/ --glycan_class 'O' --intra_cat_thresh 1.75 --top_n_isomers 2 --output path_to_my_outputs/output_file.csv

Using CandyCrunch – LC-MS/MS glycan annotation

wrap_inference (in CandyCrunch.prediction)

Wrapper function to predict glycan structures from raw LC-MS/MS spectra using CandyCrunch

Requires at a minimum:

- spectra_filepath, type = string: a filepath to an mzML/mzXML/mgf file or a .xlsx file 
- glycan_class,type = string: the glycan class measured ("N", "O", "lipid"/"free")

mzML/mzXML/mgf files are internally processed into extracted spectra. xlsx files need to be already extracted in the format as the example file in examples/. extract_spectra(spectra_filepath) (in CandyCrunch.prediction) writes such an xlsx from an mzML/mzXML/mgf file, with precursor m/z already refined from MS1, so it gives the same predictions as the raw file at a fraction of the size (only quantification falls back to precursor intensity, as MS1 scans are not kept).

Optional arguments:

model, type=Pytorch object: loaded from a checkpoint of a trained CandyCrunch model  
glycans, type=list: ordered list of glycans used to train CandyCrunch which can be predicted by the model
bin_num, type=list: number of bins to separate the ms2 spectrum into
frag_num, type=list: number of top fragments to show in df_out per spectrum; default:100
max_charge, type=int: maximum signed charge to consider; the sign sets the mass spectrometry mode (negative or positive); default:-3
modification, type=string: chemical derivatization of glycans; options are “reduced”, “permethylated”, “2AA”, “2AB” or “custom”; default:”reduced” | |--mass_tag, type=float: only if modification = "custom", mass of custom reducing end tag ; default:None
lc, type=string: type of liquid chromatography; options are 'PGC', 'C18', and 'other'; default:'PGC'
trap, type=string: type of mass detector used; options are 'linear', 'orbitrap', 'amazon', and 'other'; default:'linear'
rt_min, type=float: whether only spectra from a minimum retention time (in minutes) onward should be considered; default:0
rt_max, type=float: whether only spectra up to a maximum retention time (in minutes) should be considered; default:0
rt_diff, type=float: maximum retention time difference (in minutes) to peak apex that can be grouped with that peak; default:1.0
pred_thresh, type=float: prediction confidence threshold used for filtering; default:0.01
temperature, type=float: the temperature factor used to calibrate logits; default:1.15
spectra, type=float: whether to also output the actual spectra used for prediction; default:False
get_missing, type=bool: whether to also organize spectra without a matching prediction but a valid composition; default:False | |--filter_out, type=set: only if get_missing = "True", set of monosaccharide or modification types that is used to filter out compositions (e.g., if you know there is no Pen); default:{'Kdn', 'P', 'HexA', 'Pen', 'HexN', 'Me', 'PCho', 'PEtN'}
ppm_thresh, type=float: mass tolerance in ppm, used for grouping spectra, matching compositions, and filtering by ppm error; default:300
crumbs_thresh, type=float: minimum CandyCrumbs annotation score to keep a prediction; default:3
sample_prep, type=string: options are 'underivatized', 'permethylated', and 'peracetylated'; default:'underivatized'
extra_thresh, type=float: prediction confidence threshold at which to allow cross-class predictions (e.g., predicting N-glycans in O-glycan samples); default:0.2
supplement, type=bool: whether to impute observed biosynthetic intermediaries from biosynthetic networks; default:True
experimental, type=bool: whether to impute missing predictions via database searches etc.; default:True | |--mass_dic, type=dict: only if experimental = "True", dictionary of form mass : list of glycans; will be generated internally | |--taxonomy_level, type=string: taxonomic level used to restrict the glycan database; default:'Class' | |--taxonomy_filter, type=string: taxon at taxonomy_level used to restrict the glycan database; default:'Mammalia' | |--df_use, type=DataFrame: only if experimental = "True", sugarbase-like database of glycans with species associations etc.; default: use glycowork-stored df_glycan
plot_glycans, type=bool: whether you want to save an output.xlsx file that contains SNFG images of all top1 predictions, will be saved in the same folder as spectra_filepath; default:False

Basic usage

annotated_spectra_df = wrap_inference("C:/myfiles/my_spectra.mzML", glycan_class)

This is what a truncated example of annotated_spectra_df would look like

predictions composition num_spectra charge RT top_fragments adduct evidence
384.157 [('Gal(b1-3)GalNAc', 0.9625)] {'Hex': 1, 'HexNAc': 1} 8 -1 6.75 [204.0202, 222.1731, 156.0888, 179.031, 160.7594, ...] nan strong
425.036 [('GalNAc(a1-3)GalNAc', 0.7947394540942927), ('GlcNAc(b1-3)GalNAc', 0.17965260545905706), ('HexNAc(?1-3)GalNAc', 0.025607940446650122)] {'HexNAc': 2} 2 -1 15.88 [381.005, 389.9802, 406.871, 326.8488, 212.01, ...] nan strong
... ... ... ... ... ... ... ... ...

wrap_inference_batch (in CandyCrunch.prediction)

Wrapper function to predict glycan structures from multiple LC-MS/MS files using CandyCrunch.
This function works similarly to wrap_inference except a list of filenames is provided and a tuple is returned: a feature table and a dictionary of output DataFrames, one for each input file, keyed by their filenames.
The feature table has one row per isomer group with its top1 prediction, consensus m/z, RT, charge, composition, GlyTouCan ID and the number of files with MS2 evidence, followed by its relative abundance in each file and an evidence_<file> column per file ('strong'/'weak' from MS2, 'ms1_only' if it was gap-filled from MS1). The first column holds the glycans, so the table can go straight into glycowork's get_differential_expression with the file columns as groups.
Precursors are clustered by m/z across files within the mass tolerance, and glycan predictions are assigned to groups based on the most common prediction in the group across files. Useful for retention time correction but cannot correct LC runs in cases where noise exceeds signal.

The algorithm operates under the assumption that the same structures should elute at a given RT ± intra_cat_threshold.
At each composition, the top_n_isomers isomer groups are kept, preferring groups seen in more files and then more abundant ones. For mzML files, an isomer group without MS2 in a file is filled in from MS1 if the file shows a genuine chromatographic peak for it within intra_cat_threshold that is at least as intense as the weakest precursor that got MS2 in that file

Requires at a minimum:

- spectra_filepath_list, type = list: list of filepaths to mzML/mzXML/mgf files and/or .xlsx files 
- glycan_class, type = string: the glycan class measured ("N", "O", "lipid"/"free")
- intra_cat_threshold, type = float: minutes the RT of a structure can differ from the mean of a group.

Optional arguments:

  • top_n_isomers, type = int: number of different isomer groups at each composition to retain; default:5
  • n_jobs, type = int: number of files to process in parallel (in separate processes); default:1
    See wrap_inference

```python spectra_filepath_list = ["C:/myfiles/my_spectra_exp1.mzML","C:/myfiles/my_spectra_exp2.mzML", "C:/myfiles/my_spectra_exp3.mzML","C:/myfiles/my_spectra_exp4.mzML"] combined_batch, results_dict = wrap_inference_batch(spectra_filepath_list, 'O', 1.75, 2) ```

This is what results_dict would look like

{'my_spectra_exp1: pd.DataFrame(...),
 'my_spectra_exp2: pd.DataFrame(...),
 'my_spectra_exp3: pd.DataFrame(...),
 'my_spectra_exp4: pd.DataFrame(...)}

Using CandyCrumbs – MS2 fragment annotation

CandyCrumbs (in CandyCrunch.analysis)

Wrapper function to annotate MS2 fragments using CandyCrumbs

Requires at a minimum:

- glycan_string, type=string: a glycan in IUPAC-condensed format 
- fragment_masses, type=list: all observed masses which are to be annotated with a possible fragment names
- mass_threshold, type=float: the maximum tolerated mass difference betweem observed masses and possible fragments
condensed_iupac_glycan = 'Gal(a1-3)Gal(b1-4)GlcNAc(b1-6)[GalNAc(b1-4)GlcNAc(b1-3)]Gal(b1-4)Glc'
ms2_fragment_masses = [425.07, 443.07, 546.19, 1216.32]
annotated_fragments_dict = CandyCrumbs(condensed_iupac_glycan, fragment_masses = ms2_fragment_masses, mass_threshold = 1)

Optional arguments:

max_cleavages, type=int: maximum number of allowed concurrent cleavages per possible fragment; default:3 
simplify, type=bool: whether to select a single fragment for each mass based on mass difference, number of cleavages, and other fragments; default:True
charge, type=int: the charge state of the precursor ion (singly-charged, doubly-charged, etc.); default:-1
mass_tag, type=float: mass of the glycan label or reducing end modification; default:2.0156
iupac, type=bool: whether to also return the fragment sequence in IUPAC-condensed nomenclature; default:False
sample_prep, type=string: options are 'underivatized', 'permethylated', and 'peracetylated'; default:'underivatized'

This is what annotated_fragments_dict would look like

{425.07: {'Theoretical fragment masses': [425.12955],
  'Domon-Costello nomenclatures': [['02A_3_Alpha', 'M_H2O']],
  'Fragment charges': [-1]},
 443.07: {'Theoretical fragment masses': [443.1401],
  'Domon-Costello nomenclatures': [['02A_3_Alpha']],
  'Fragment charges': [-1]},
 546.19: {'Theoretical fragment masses': [546.18775],
  'Domon-Costello nomenclatures': [['Y_3_Beta', 'Y_2_Alpha']],
  'Fragment charges': [-1]},
 1216.32: {'Theoretical fragment masses': [1216.43105],
  'Domon-Costello nomenclatures': [['M_C2H4O2']],
  'Fragment charges': [-1]}}

It isn't always easy to quickly visualise the Domon-Costello nomenclature. Here is an example of how we can use GlycoDraw to visualise one of the outputs:

#This will calculate where on the glycan the fragments occured and return a valid GlycoDraw input
fragment_iupac = domon_costello_to_fragIUPAC('Gal(a1-3)Gal(b1-4)GlcNAc(b1-6)[GalNAc(b1-4)GlcNAc(b1-3)]Gal(b1-4)Glc', ['Y_3_Beta', 'Y_2_Alpha'])

#Then we can simply draw the result with GlycoDraw
GlycoDraw(fragment_iupac)

Modules

prediction

  • Includes all functions used in wrap_inference.
  • Contains process_mzML_stack and process_mzXML_stack to extract spectra from .mzML and .mzXML files

analysis

  • Includes all functions used in CandyCrumbs.
  • Contains functions to analyze and compare averaged spectra
  • Contains other functions to manipulate glycan string representations, e.g., domon_costello_to_fragIUPAC

model

  • Includes code for model definition, dataset handling, and data augmentation; only used in the back-end

examples

  • Includes the extracted spectra of an example mzML file from Kouka et al. 2022

Citation

If you use CandyCrunch or any of our datasets in your work, please cite Urban et al., Nat Methods 2024.
The data used to train CandyCrunch can be found at Zenodo, under doi:10.5281/zenodo.7940047

About

Predicting glycan structure from LC-MS/MS data

Topics

Resources

Stars

36 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages