knitr::opts_chunk$set( collapse = TRUE, comment = "#>" )
This vignette is for users who want to understand how glyrepr stores glycan structures internally.
Some familiarity with graph theory and the igraph package will help.
If those concepts are new to you,
the igraph documentation is a useful companion reference.
Glycans are naturally represented as directed graphs.
In glyrepr, an ordinary glycan structure is stored as an outward-directed
tree, where each vertex represents a monosaccharide and each edge represents a
glycosidic linkage. A structure with floating parts is stored as an annotated
forest: one main tree plus one disconnected tree for each floating part.
Behind the scenes,
each glycan_structure() object is backed by an igraph object.
Most workflows can stay at the glycan_structure() level,
but the graph representation is useful when you need custom structure analysis.
library(glyrepr)
A glycan can carry many kinds of information: linear oriented C-atoms, basetype, substituents, configuration, anomeric center, ring size, linkage positions, and more.
Some packages, such as Python's glypy, store a very detailed glycan model.
That comprehensive strategy is useful for specialized tasks such as MS/MS spectra simulation,
but it can be overkill for everyday omics research.
glyrepr takes a more compact approach:
if a feature can be derived from an IUPAC-condensed text representation,
glyrepr stores it.
Details such as configuration and ring size are not stored directly,
because they are often predictable for common carbohydrates and are not needed for many glycomics workflows.
For a closer look at IUPAC-condensed notation, see the IUPAC-condensed vignette.
You cannot pass a glycan_structure() object directly to most igraph functions.
First, extract the underlying graph with get_structure_graphs():
glycan <- n_glycan_core() graph <- get_structure_graphs(glycan) graph
The printed graph contains several pieces of information.
First line: Directed Named ("DN") graph with 5 vertices (sugar units) and 4 edges (bonds).
Graph-level attributes:
anomer: the anomeric configuration of the reducing end.alditol: whether the reducing-end residue is an alditol.floating_parts, when present: virtual attachment metadata for disconnected
floating components.floating_substituents, when present: unresolved substituent tokens and
their candidate parent residues.Vertex attributes:
name: a unique ID for each monosaccharide.mono: the monosaccharide type, such as "Hex" or "HexNAc".sub: chemical decorations attached to the monosaccharide.Edge attributes:
linkage: how the monosaccharides are connected, including bond positions and configurations.Connection pattern: "1->2" means vertex 1 connects to vertex 2.
glyrepr treats bonds as arrows pointing from the core toward the branches.
The direction is a modeling choice that makes traversal and structure operations easier.
You can also plot the graph with igraph:
plot(graph)
Each vertex represents a monosaccharide with three key properties:
Names: These are auto-generated identifiers, usually simple integers, but they could be anything as long as they're unique:
igraph::V(graph)$name
Monosaccharides: These are IUPAC-condensed names like "Hex", "HexNAc", "Glc", "GlcNAc".
igraph::V(graph)$mono
For the full list of available monosaccharides,
check SNFG notation or run available_monosaccharides().
Substituents: Chemical decorations like "Me" (methyl), "Ac" (acetyl), "S" (sulfate), etc. Position matters. "3Me" = methyl at position 3, "?S" = sulfate at unknown position:
igraph::V(graph)$sub
Multiple decorations are comma-separated and sorted by position:
glycan2 <- as_glycan_structure("Glc3Me6S(a1-") graph2 <- get_structure_graphs(glycan2) igraph::V(graph2)$sub
Edges represent glycosidic bonds with a simple but powerful format:
<target anomeric config><target position> - <source position>
Here is an example where "Gal" has an "a" anomeric configuration, linking from position 3 of "GalNAc" to position 1 of "Gal":
glycan3 <- as_glycan_structure("Gal(a1-3)GalNAc(b1-") graph3 <- get_structure_graphs(glycan3) igraph::E(graph3)$linkage
glyrepr stores anomer information in edges rather than vertices.
This follows the way linkages are written in IUPAC-condensed notation,
for example "Neu5Ac with an a2-3 linkage".
Anomer: The anomeric configuration of the reducing end.
graph$anomer
Alditol: Whether the reducing-end residue has been reduced. The attribute
is always explicit on canonical graphs; legacy inputs without it are treated as
FALSE. In IUPAC-condensed strings, -ol follows the reducing-end residue.
alditol <- as_glycan_structure("Gal(b1-4)GlcNAc-ol(a1-") alditol_graph <- get_structure_graphs(alditol) alditol_graph$alditol get_alditol(alditol)
A floating structure remains one igraph, but it is weakly disconnected.
The floating-component vertices come first in complete IUPAC-condensed order,
followed by the main-tree vertices in their canonical order.
floating <- as_glycan_structure( "{Neu5Ac(a2-3)|2,3}Gal(b1-3)[Gal(b1-4)]GlcNAc(a1-" ) floating_graph <- get_structure_graphs(floating) igraph::components(floating_graph, mode = "weak")$no igraph::graph_attr(floating_graph, "floating_parts") structure_floating_parts(floating)
Each floating_parts entry has four fields:
root: the vertex index at the root of the floating component.nodes: every vertex index in the floating component.linkage: the virtual linkage from that root to its unresolved parent.parents: candidate vertex indices outside the component. integer() means
all feasible nodes outside that component, including nodes in other floating
parts. In a canonical structure, an explicit vector has at least two entries;
a singleton is normalized to an ordinary edge.These IDs follow complete IUPAC-condensed residue order: floating blocks are counted from left to right before the main glycan, and substituent blocks add no vertices. Component assignments must be acyclic and ultimately connect to the main tree.
The virtual attachment is not stored in igraph::E(floating_graph), because
its endpoint is unresolved. Use structure_floating_parts() when a tabular
representation must round-trip this metadata.
Floating substituents use the same candidate-parent convention without adding
dummy vertices. Each floating_substituents entry contains substituent, such
as "6S" or "?S", and parents. Empty parents means every feasible
residue node in the complete structure, including floating-part nodes;
otherwise the values are global graph vertex indices.
floating_sub <- as_glycan_structure( "{6S|1,2}Gal(a1-3)Glc(a1-3)Man(a1-" ) floating_sub_graph <- get_structure_graphs(floating_sub) igraph::graph_attr(floating_sub_graph, "floating_substituents") structure_floating_substituents(floating_sub)
igraphOnce you understand the graph structure,
you can use igraph functions for custom structure analysis.
Remember that tree-specific functions need special handling for a structure
with floating parts: get_structure_graphs() and smap() pass the complete
annotated forest, not only the main tree.
Example 1: Count branched structures (sugars with multiple children):
sum(igraph::degree(graph, mode = "out") > 1)
Example 2: Explore the structure with breadth-first search:
bfs_result <- igraph::bfs(graph, root = 1, mode = "out") bfs_result$order
smap FunctionsWorking with multiple glycans? You could use purrr:
library(purrr) glycans <- c(n_glycan_core(), o_glycan_core_1(), o_glycan_core_2()) graphs <- get_structure_graphs(glycans) # Extract graphs first map_int(graphs, ~ igraph::vcount(.x)) # Then analyze
For glycan structure vectors,
glyrepr's smap functions are usually a better fit:
smap_int(glycans, ~ igraph::vcount(.x)) # Direct analysis, no intermediate step.
The main advantage of smap functions is how they handle duplicates.
Real datasets often contain many repeated structures,
and smap optimizes by processing unique structures once,
then efficiently expanding results back to the original dimensions.
The smap vignette covers this workflow in more detail.
glymotifOne important application is identifying biologically meaningful motifs, or functional substructures.
The glymotif package,
built on this graph foundation,
specializes in exactly this task.
See the glymotif introduction for examples.
In this vignette, you saw:
igraph, smap, and glymotif can build on this representation.The graph representation might seem complex at first,
but it's this solid foundation that enables all the sophisticated glycan analysis capabilities in the glycoverse.
Most users will not need to manipulate the graph directly,
but understanding the model makes it easier to extend glyrepr when custom analysis is needed.
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.