glycowork is the Python package for glycan data science. Glycans are branched, non-linear sequences with a dozen competing text representations and no shared tooling, which is why most glycomics analysis stalls before it starts. glycowork removes that step and gives you the rest of the stack on top of it.
Reads everything. IUPAC-condensed is native to glycowork. IUPAC-extended, GlycoCT, GlycoCT-XML, WURCS, LinearCode, Oxford, GLYCAM, CSDB-linear, GlycoWorkBench, GlyTouCan IDs, KCF, SMILES, pGlyco, and GlySeeker are canonicalized on the way in. You never need a parser again.
Graph-native. Every glycan is a directed graph, so motif search is subgraph isomorphism with wildcards (Hex, b1-3/4, ?), terminal/internal constraints, and negation. There is also a full regular-expression engine for glycans.
Statistics that comparative glycomics actually needs. Compositional transforms, MNAR-aware imputation, two-stage FDR across motif families, effect sizes, ANOVA, time series, and biodiversity, each in one call.
Data included. 50,461 curated glycan sequences with species/tissue/disease annotations, >790,000 protein-glycan binding measurements, 72 analysis-ready glycomics, glycoproteomics, and lectin-array datasets, and four pretrained deep learning models.
Publication-ready figures. SNFG drawings from a sequence, dropped straight into your volcano plots, heatmaps, and Excel sheets.
Not familiar with Python? Try our no-code, graphical user interface (glycoworkGUI.exe, can be downloaded at the bottom of the latest Release page) for accessing some of the most useful glycowork functions
Or try our web interface for sequence format conversion/cleaning and drawing glycan SNFG structures!
Note that we have optional extra installs for specialized use (even further instructions can be found in the Examples tab; on Mac you might need to use "glycowork[ml]"), such as: deep learning pip install glycowork[ml] analyzing atomic/chemical properties of glycans pip install glycowork[chem] everything pip install glycowork[all]
Whole workflows in one call
An entire differential glycomics analysis, on real data that ships with the package.
#matched tumor and healthy skin from 10 squamous cell carcinoma patients; one of 72 bundled datasetsfrom glycowork.glycan_data.loader import glycomics_data_loaderfrom glycowork.motif.analysis import get_differential_expressiondf = glycomics_data_loader.human_skin_O_PMC5871710_SCCprint(df.groups)#groups, pairing, and the alpha level are read off the dataset itselfres = get_differential_expression(df)print(res.sort_values('corr p-val')[['Glycan', 'Log2FC', 'corr p-val', 'Effect size']].head())#the same result object, drawn: every dot past the thresholds gets its SNFG structure instead of a text labelfrom glycowork.motif.analysis import get_volcanoget_volcano(res, annotate_volcano =True, title ='O-glycans altered in squamous cell carcinoma')
Glycowork currently contains the following main datasets that are freely available to everyone:
df_glycan
contains ~50,500 unique glycan sequences, including labels such as ~39,500 species associations, ~20,000 tissue associations, and ~1,000 disease associations
glycan_binding
contains >790,000 protein-glycan binding interactions, from >2,000 unique glycan-binding proteins
72 curated, analysis-ready datasets from the literature, each shipping with its own sample groups, pairing, and provenance, so every analysis function below runs on them with no arguments
These are not flat tables; they query themselves:
#which glycans of odd-toed ungulates carry a3-sialylated LacNAc, and where were they described?from glycowork.glycan_data.loader import df_glycan, glycomics_data_loaderhits = df_glycan.meta_filter(Order ='Perissodactyla').glyco_filter('Sia(a2-3)Gal')print(len(hits), hits.glycans[:3])#which bundled O-glycomics datasets came from primary tissue or body fluid?print(glycomics_data_loader.filter(glycan_class ='O', source_type = ['primary tissue', 'body fluid']))
Additionally, we store these trained deep learning models for easy usage, which can be retrieved with the prep_model function:
LectinOracle
can be used to predict glycan-binding specificity of a protein, given its ESMC representation; from Lundstrom et al., 2021
LectinOracle_flex
operates the same as LectinOracle but can directly use the raw protein sequence as input (no ESMC representation required)
SweetNet
a graph convolutional neural network trained to predict species from glycan, can be used to generate learned glycan representations; from Burkholz et al., 2021
NSequonPred
given the ESM-1b representation of an N-sequon (+/- 20 AA), this model can predict whether the sequon will be glycosylated
How to use
Four modules: glycan_data for datasets and loaders, motif for processing, drawing, and analyzing sequences, network for biosynthetic and evolutionary networks, and ml for representations and models.
A few of the things people use them for, with the full set in the examples:
#six different sequence formats in the same list; no conversion step, no bookkeepingglycans = ["Neu5Ac(a2-3)Gal(b1-4)[Fuc(a1-3)]GlcNAc(b1-2)Man(a1-3)[Gal(b1-3)[Fuc(a1-4)]GlcNAc(b1-2)Man(a1-6)]Man(b1-4)GlcNAc(b1-4)[Fuc(a1-6)]GlcNAc","Ma3(Ma6)Mb4GNb4GN;N","α-D-Manp-(1→3)[α-D-Manp-(1→6)]-β-D-Manp-(1→4)-β-D-GlcpNAc-(1→4)-β-D-GlcpNAc-(1→","F(3)XA2","WURCS=2.0/5,11,10/[a2122h-1b_1-5_2*NCC/3=O][a1122h-1b_1-5][a1122h-1a_1-5][a2112h-1b_1-5][a1221m-1a_1-5]/1-1-2-3-1-4-3-1-4-5-5/a4-b1_a6-k1_b4-c1_c3-d1_c6-g1_d2-e1_e4-f1_g2-h1_h4-i1_i2-j1","""RES1b:b-dglc-HEX-1:52s:n-acetyl3b:b-dglc-HEX-1:54s:n-acetyl5b:b-dman-HEX-1:56b:a-dman-HEX-1:57b:b-dglc-HEX-1:58s:n-acetyl9b:b-dgal-HEX-1:510s:sulfate11s:n-acetyl12b:a-dman-HEX-1:513b:b-dglc-HEX-1:514s:n-acetyl15b:b-dgal-HEX-1:516s:n-acetylLIN1:1d(2+1)2n2:1o(4+1)3d3:3d(2+1)4n4:3o(4+1)5d5:5o(3+1)6d6:6o(2+1)7d7:7d(2+1)8n8:7o(4+1)9d9:9o(-1+1)10n10:9d(2+1)11n11:5o(6+1)12d12:12o(2+1)13d13:13d(2+1)14n14:13o(4+1)15d15:15d(2+1)16n"""]from glycowork.motif.annotate import annotate_dataset#feature_set also takes 'terminal', 'exhaustive', 'chemical', 'graph', 'size_branch', and your own motifs via 'custom'out = annotate_dataset(glycans, feature_set = ['known'], condense =True)
#using graphs, you can easily check whether a glycan contains a specific motif; how about internal Lewis A/X motifs?from glycowork.motif.graph import subgraph_isomorphismprint(subgraph_isomorphism('Neu5Ac(a2-3)Gal(b1-4)[Fuc(a1-3)]GlcNAc(b1-6)[Gal(b1-3)]GalNAc','Fuc(a1-?)[Gal(b1-?)]GlcNAc', termini_list = ['terminal', 'internal', 'flexible']))print(subgraph_isomorphism('Neu5Ac(a2-3)Gal(b1-3)[Fuc(a1-4)]GlcNAc(b1-6)[Gal(b1-3)]GalNAc','Fuc(a1-3/4)[Gal(b1-3/4)]GlcNAc', termini_list = ['t', 'i', 'f']))print(subgraph_isomorphism('Gal(b1-3)[Fuc(a1-4)]GlcNAc(b1-6)[Gal(b1-3)]GalNAc','dHex(a1-?)[Hex(b1-?)]GlcNAc', termini_list = ['t', 'i', 'f']))#or you could find the terminal epitopes of a glycanfrom glycowork.motif.annotate import get_terminal_structuresprint("\nTerminal structures:")print(get_terminal_structures('Man(a1-3)[Man(a1-6)]Man(b1-4)GlcNAc(b1-4)[Fuc(a1-6)]GlcNAc'))
#glycans have a regular expression engine, with quantifiers, alternation, negation, and anchorsfrom glycowork.motif.regex import get_match, motif_to_regexglycan ="GalNAc(b1-4)GlcNAc(b1-2)Man(a1-3)[Neu5Gc(a2-6)GalNAc(b1-4)[Fuc(a1-3)]GlcNAc(b1-2)Man(a1-6)]Man(b1-4)GlcNAc(b1-4)[Fuc(a1-6)]GlcNAc"print(get_match(".-.-([Hex|Fuc])+-.", glycan))#anchors and negated character classes work tooprint(get_match(".-HexNAc$", glycan))print(get_match("Fuc-([^Gal])+-GlcNAc", "Fuc(a1-3)[GlcNAc(b1-4)]GlcNAc"))#and any motif can be turned into its expression automaticallyprint(motif_to_regex("Fuc(a1-3)[Gal(b1-4)]GlcNAc(b1-?)"))
#glycans are not an unordered list; they are the output of a biosynthetic pathway, which glycowork reconstructsfrom glycowork.network.biosynthesis import construct_network, plot_networkglycans = ["Gal(b1-4)Glc-ol", "GlcNAc(b1-3)Gal(b1-4)Glc-ol", "GlcNAc6S(b1-3)Gal(b1-4)Glc-ol","Gal(b1-4)GlcNAc(b1-3)Gal(b1-4)Glc-ol", "Fuc(a1-2)Gal(b1-4)Glc-ol","Neu5Ac(a2-3)Gal(b1-4)GlcNAc(b1-3)[Gal(b1-3)GlcNAc(b1-6)]Gal(b1-4)Glc-ol"]network = construct_network(glycans)#the intermediates you did not measure are inferred, and every edge is labeled with the enzymatic reactionprint(len(glycans), "measured glycans ->", len(network.nodes()), "nodes and", len(network.edges()), "reactions")plot_network(network, draw_glycans =True)
6 measured glycans -> 14 nodes and 19 reactions
#straight off the instrument: m/z to composition to candidate structures, constrained by glycan class and taxonomyfrom glycowork.motif.tokenization import mz_to_composition, compositions_to_structures#max_charge is signed: the sign picks the ion mode, the magnitude the highest charge state consideredprint(mz_to_composition(1315.48, max_charge =-2, glycan_class ='N'))print(compositions_to_structures([{'Hex':3, 'HexNAc':4}], glycan_class ='N')['glycan'].head())#or we could calculate the mass of this compositionfrom glycowork.motif.tokenization import composition_to_massprint("\nMass of the composition Hex3HexNAc4")print(composition_to_mass({'Hex':3, 'HexNAc':4}))print(composition_to_mass("H3N4"))print(composition_to_mass("Hex3HexNAc4"))
[{'Hex': 3, 'HexNAc': 4}]
0 GlcNAc(b1-2)Man(a1-3)[GlcNAc(b1-2)Man(a1-6)]Ma...
1 GlcNAc(b1-2)Man(a1-3)[GlcNAc(b1-4)][Man(a1-6)]...
2 GlcNAc(b1-2)[GlcNAc(b1-4)]Man(a1-3)[Man(a1-6)]...
3 GalNAc(b1-4)GlcNAc(b1-2)Man(a1-3)[Man(a1-6)]Ma...
4 GalNAc(b1-3/4)GlcNAc(b1-2)Man(a1-3)[Man(a1-6)]...
Name: glycan, dtype: object
Mass of the composition Hex3HexNAc4
1316.4865545999999
1316.4865545999999
1316.4865545999999
#glycans to chemistry and back, with no cheminformatics toolkit requiredfrom glycowork.motif.smiles import glycan_to_smiles, smiles_to_iupacsmiles = glycan_to_smiles("Gal(b1-4)[Fuc(a1-3)]GlcNAc")print(smiles)print(smiles_to_iupac(smiles))#the molecular graph also keeps track of which monosaccharide every atom came fromfrom glycowork.motif.smiles import glycan_to_moleculemol = glycan_to_molecule("Neu5Ac(a2-3)Gal(b1-4)Glc")print(len(mol.atoms), "atoms,", len(mol.rings), "rings,", len(set(mol.atom_monos)), "residues")