A pattern is a short description of the feature you
want to find in a glycan. For example, Gal-GlcNAc asks for
Gal directly attached to GlcNAc. A regular expression,
often shortened to regex, adds symbols that express
choices, repetition, and conditions. You do not need prior regex
knowledge: this guide introduces those symbols one at a time.
Glyco-regex searches the connected residues of a glycan, rather than
letters in its written name. Gal means one galactose
residue; it does not match the letters “Gal” within GalNAc.
Different ways of writing the same branch arrangement do not turn it
into a different structural feature.
The Get Started guide introduces the biological motivation and basic workflow. This guide explains the syntax in more detail, including optional advanced features. The examples use small illustrative glycans so that you can check each answer by eye.
library(glyrex)
glycans <- c(
lacnac = "Gal(b1-4)GlcNAc(b1-",
sialylated = "Neu5Ac(a2-3)Gal(b1-4)GlcNAc(b1-",
fucosylated = "Gal(b1-4)[Fuc(a1-3)]GlcNAc(b1-",
both = "Neu5Ac(a2-3)Gal(b1-4)[Fuc(a1-3)]GlcNAc(b1-",
glcnac = "GlcNAc(b1-"
)
rex_detect(glycans, "Gal-GlcNAc")
#> lacnac sialylated fucosylated both glcnac
#> TRUE TRUE TRUE TRUE FALSEThe first argument is the collection of glycans; the second is the
pattern in quotation marks. TRUE means a match was found,
and FALSE means none was found. Each answer corresponds to
the input with the same name.
The inputs above use IUPAC-condensed notation to describe structures. The pattern uses glyco-regex notation to describe a search:
| Feature | Input structure notation | Pattern notation |
|---|---|---|
| Gal attached by β1–4 to GlcNAc | Gal(b1-4)GlcNAc |
Galb4-GlcNAc |
| Fuc branch on the same GlcNAc | Gal(b1-4)[Fuc(a1-3)]GlcNAc |
Galb4-(Fuca3-)GlcNAc |
| Reducing-end α-GlcNAc | GlcNAc(a1- |
GlcNAca$ |
The ending (b1- in our inputs records the reducing-end
anomer and donor position; it does not add another residue. Do not copy
IUPAC linkage parentheses into a pattern: Gal(b1-4)GlcNAc
is not a valid search pattern.
Patterns are case-sensitive. Write recognized residue names such as
Gal and GlcNAc. Spaces in patterns are
ignored, although the examples omit them.
Start with a residue name, a family, or a choice:
| Pattern | Meaning |
|---|---|
Gal |
One Gal residue |
Hex |
One residue in the Hex family, including Gal, Glc, and Man |
HexNAc |
One residue in the HexNAc family, including GlcNAc and GalNAc |
Sia |
One Neu5Ac, Neu5Gc, or Kdn residue |
. |
Any one residue |
[Gal|Man] |
Either Gal or Man |
[^Fuc] |
One residue other than Fuc |
[^Fuc|Xyl] |
One residue that is neither Fuc nor Xyl |
The vertical bar | means “or.” Square brackets collect
the alternatives. Each negated alternative (inside [^...])
must describe one residue, not a multi-residue path. The dot does not
mean “any number of residues.”
residues <- c("Gal", "GalNAc", "Man", "Fuc")
data.frame(
residue = residues,
gal = rex_detect(residues, "Gal"),
hex = rex_detect(residues, "Hex"),
gal_or_man = rex_detect(residues, "[Gal|Man]"),
not_fuc = rex_detect(residues, "[^Fuc]")
)
#> residue gal hex gal_or_man not_fuc
#> 1 Gal TRUE TRUE TRUE TRUE
#> 2 GalNAc FALSE FALSE FALSE TRUE
#> 3 Man FALSE TRUE TRUE TRUE
#> 4 Fuc FALSE FALSE FALSE FALSEResidue modifications matter. For example, Gal6S asks
for sulfated Gal, whereas plain Gal does not match that
modified residue:
Use residue names supported by glyrepr. A slash can also express
residue choices, as in Gal/Man; [Gal|Man]
makes the alternatives more explicit.
Read from left to right, towards the reducing end. A dash joins adjacent residues:
Gal-GlcNAc: Gal attaches directly to GlcNAc.Sia-Gal-GlcNAc: a three-residue path in that
order.Gal-.-GlcNAc: exactly one intervening residue between
Gal and GlcNAc.A path may occur inside a larger glycan. Extra residues and branches
are allowed unless you add a condition that excludes them. Finding
Gal-GlcNAc therefore does not establish that Gal is
terminal or that GlcNAc has no other branches.
Alternatives can describe whole paths too:
rex_detect(
c("Gal(b1-4)GlcNAc", "Gal(b1-3)GalNAc", "Man(a1-3)Man"),
"[Gal-GlcNAc|Gal-GalNAc]"
)
#> [1] TRUE TRUE FALSEThe choice belongs inside the square brackets. A bare
Gal|Man is not supported.
A linkage suffix belongs to the residue before the
dash. In Galb4-GlcNAc, b means beta and
4 is the acceptor position on GlcNAc. The donor position is
inferred as 1, or 2 for Neu5Ac, Neu5Gc, Kdn, and Sia. Thus
Neu5Aca3-Gal describes an α2–3 linkage.
| Pattern | Linkage requirement |
|---|---|
Gal-GlcNAc |
No linkage constraint |
Galb-GlcNAc |
Beta anomer; no position constraint |
Galb4-GlcNAc |
β1–4 |
Galb3/4-GlcNAc |
Beta, with acceptor position 3 or 4 |
Gal?4-GlcNAc |
Either anomer, with donor 1 and acceptor 4 |
Galb?-GlcNAc |
Beta, with donor 1 and any acceptor position |
linkages <- c(
beta4 = "Gal(b1-4)GlcNAc",
beta3 = "Gal(b1-3)GlcNAc",
alpha4 = "Gal(a1-4)GlcNAc",
unknown_position = "Gal(b1-?)GlcNAc"
)
rex_detect(linkages, "Galb4-GlcNAc")
#> beta4 beta3 alpha4 unknown_position
#> TRUE FALSE FALSE FALSE
rex_detect(linkages, "Galb3/4-GlcNAc")
#> beta4 beta3 alpha4 unknown_position
#> TRUE TRUE FALSE FALSEThe glycan must be at least as specific as the
pattern. Unknown fields in the pattern act as wildcards;
unknown input fields cannot satisfy a specified value. The
unknown-position example therefore does not match
Galb4-GlcNAc. Every possible input position must be allowed
by the pattern: input 4 matches pattern 3/4,
but input 3/4 does not match pattern 4. Equal
sets of possible positions match.
A linkage suffix is checked even on the final residue of a pattern.
For example, GlcNAcb3 requires that GlcNAc attach onwards
by β1–3, although that next residue is not included in the match. At the
reducing end there is no outgoing glycosidic linkage, so an acceptor
position cannot match there. Use GlcNAca$, rather than
GlcNAca4$, for reducing-end α-GlcNAc.
These symbols test position without adding residues to the result:
| Pattern | Position required |
|---|---|
^Gal |
Gal is at a non-reducing terminus |
GlcNAc$ |
GlcNAc is the reducing-end residue |
Gal% |
Gal is internal: neither terminal nor reducing-end |
rex_detect(glycans, "^Galb4-GlcNAc")
#> lacnac sialylated fucosylated both glcnac
#> TRUE FALSE TRUE FALSE FALSE
rex_detect(glycans, "Gal%")
#> lacnac sialylated fucosylated both glcnac
#> FALSE TRUE FALSE TRUE FALSE
rex_detect(glycans, "GlcNAcb$")
#> lacnac sialylated fucosylated both glcnac
#> TRUE TRUE TRUE TRUE TRUEOnly the uncapped Gal residues satisfy ^Gal. In the
sialylated examples, Gal is internal and satisfies Gal%
instead. The symbol ^ means something different
inside a negated choice: [^Fuc] excludes a
residue identity; it does not ask for a terminus.
Even ^Gal-GlcNAc$ need not describe the whole glycan:
extra branches on GlcNAc remain allowed. The two symbols constrain the
ends of the selected path, not the total number of residues in the
structure.
Put the unit in square brackets, then say how many copies are allowed. The symbol or braces after the group apply to the whole group.
| Form | Number of consecutive copies |
|---|---|
[Gal]? |
Zero or one |
[Gal]* |
Zero or more |
[Gal]+ |
One or more |
[Gal]{2} |
Exactly two |
[Gal]{2,4} |
Two to four |
[Gal]{2,} |
At least two |
[Gal]{,4} |
Zero to four |
For example, [Sia]?-Galb4-GlcNAc allows an optional Sia
before LacNAc:
rex_detect(glycans, "[Sia]?-Galb4-GlcNAc")
#> lacnac sialylated fucosylated both glcnac
#> TRUE TRUE TRUE TRUE FALSEOptional does not mean forbidden: both sialylated and unsialylated LacNAc can match. Because a match may start within a glycan, even the zero-copy choice can find a capped Gal. Add terminal or neighbouring-path conditions if you need to distinguish those cases.
A repeated group can contain several residues:
repeat_glycan <- "Gal(b1-4)GlcNAc(b1-3)Gal(b1-4)GlcNAc(b1-3)Gal(b1-"
rex_extract(repeat_glycan, "[Galb4-GlcNAcb3]{2}")
#> [1] "Gal(b1-4)GlcNAc(b1-3)Gal(b1-4)GlcNAc(b1-"This repeats the Gal–GlcNAc unit twice. The b3 on GlcNAc
constrains its attachment to the next residue, including the final
attachment beyond the selected two units. It is a connected path, not a
request for two copies anywhere in the glycan. Use
rex_count() for counting occurrences.
Use [Gal]+, not Gal+. The alternative
grouping syntax (?:Galb4-GlcNAcb3){2} has the same purpose
as square brackets; plain parentheses would mean a branch.
A branch can also be optional, as in
Galb4-(Fuca3-)?GlcNAc. It allows the Fuc branch to
participate in the match but does not exclude other branches or other
Fuc linkages when that optional branch is skipped.
By default, repetition tries longer runs first. This is called
greedy matching. Add ? after the
repetition symbol to try shorter runs first, called
lazy matching. The rest of the pattern must succeed
either way.
chain <- "Gal(b1-4)GlcNAc(b1-4)GlcNAc"
rex_extract(chain, "Hex-[HexNAc]+")
#> [1] "Gal(b1-4)GlcNAc(b1-4)GlcNAc(?1-"
rex_extract(chain, "Hex-[HexNAc]+?")
#> [1] "Gal(b1-4)GlcNAc(b1-"The first includes both GlcNAc residues; the second includes just
one. In [Gal]?, the question mark means optional; in
[Gal]+?, it changes which repetition length is tried first.
In Gal?4, it instead belongs to the linkage suffix. Use the
bracketed form [Gal]? to make optionality clear.
Even when zero copies are allowed, glyrex does not return an empty
match: [Gal]* on a glycan without Gal does not produce a
successful zero-residue result. Unbounded repetition is limited by the
residues in the input glycan.
Sometimes you want to extract Gal only when it has particular neighbours. A lookaround assertion checks a neighbouring path without adding those neighbours to the match. “Ahead” means towards the reducing end; “behind” means towards the non-reducing side.
| Pattern | Condition; only Gal is included |
|---|---|
Gal-(?=GlcNAc) |
The next residue towards the reducing end is GlcNAc |
Gal-(?!GlcNAc) |
That next residue is not GlcNAc, or there is none |
(?<=Sia-)Gal |
A Sia is directly attached on the non-reducing side |
(?<!Sia-)Gal |
No Sia is directly attached on the non-reducing side |
x <- "Neu5Ac(a2-3)Gal(b1-4)GlcNAc(a1-"
rex_extract(x, "Sia-Gal-GlcNAc")
#> [1] "Neu5Ac(a2-3)Gal(b1-4)GlcNAc(a1-"
rex_extract(x, "(?<=Sia-)Gal-(?=GlcNAc)")
#> [1] "Gal(b1-"
rex_detect(x, "(?<!Sia-)Gal")
#> [1] FALSEThe first extraction includes all three residues; the second returns
Gal only. Its output retains the Gal anomer in Gal(b1-. All
paths inside assertions are still written towards the reducing end: do
not reverse the order of a multi-residue path inside a lookbehind.
Unlike branch absence, negative lookbehind considers even residues
already included in the path. On Gal(b1-4)GlcNAc,
Gal-(!Gal-)GlcNAc succeeds (no additional Gal branch), but
Gal-(?<!Gal-)GlcNAc fails (Gal is attached).
A lookbehind immediately before a branch checks that branch. For
example, Gal-(?<!Sia-)(Gal-)GlcNAc requires an
additional Gal branch on GlcNAc whose Gal is not capped directly by Sia.
It does not test whether GlcNAc itself has Sia attached.
Assertions only check conditions. They cannot be repeated, and a
pattern consisting only of an assertion, such as (?=Gal),
returns no match because it includes no residues.
A named capture labels a selected part so you can
retrieve it separately. Write (?<name>...), replacing
name with a useful label:
rex_match(
glycans,
"(?<cap>Sia)-(?<unit>Galb4-GlcNAc)"
)
#> match cap
#> lacnac NA NA
#> sialylated "Neu5Ac(a2-3)Gal(b1-4)GlcNAc(b1-" "Neu5Ac(a2-"
#> fucosylated NA NA
#> both "Neu5Ac(a2-3)Gal(b1-4)GlcNAc(b1-" "Neu5Ac(a2-"
#> glcnac NA NA
#> unit
#> lacnac NA
#> sialylated "Gal(b1-4)GlcNAc(b1-"
#> fucosylated NA
#> both "Gal(b1-4)GlcNAc(b1-"
#> glcnac NAThe match column contains the full match;
cap and unit contain the labelled parts.
Inputs without a match have missing values. Captures include residues in
the full match, whereas lookarounds only test their presence. Ordinary
branch parentheses do not create a named column.
Names must start with a letter and contain only letters, digits, or
underscores. Use each name once, and do not use the reserved name
match. Use rex_match_all() to retrieve all
retained matches and their captures.
Start with the required residues, then add one condition at a time:
| Step | Pattern | Added requirement |
|---|---|---|
| 1 | Gal-GlcNAc |
Adjacent Gal and GlcNAc |
| 2 | Galb4-GlcNAc |
β1–4 linkage |
| 3 | Sia-Galb4-GlcNAc |
Sialic acid on Gal |
| 4 | ^Sia-Galb4-GlcNAc |
That sialic acid is terminal |
| 5 | ^Sia-Galb4-(!Fuc-)GlcNAc |
No remaining Fuc branch on that GlcNAc |
pattern <- rex_compile("^Sia-Galb4-(!Fuc-)GlcNAc")
rex_subset(glycans, pattern)
#> sialylated
#> "Neu5Ac(a2-3)Gal(b1-4)GlcNAc(b1-"rex_compile() checks the syntax and prepares a reusable
pattern. It does not establish that the pattern asks the biological
question you intended. Try known positive and negative structures, and
inspect extracted matches.
| Common mistake | Use instead |
|---|---|
IUPAC linkage in a pattern:
Gal(b1-4)GlcNAc |
Galb4-GlcNAc |
Attachment dash outside branch:
Gal-(Fuc)-GlcNAc |
Gal-(Fuc-)GlcNAc |
Ordinary parentheses for a repeated path:
(Gal)+ |
[Gal]+ |
Bare alternatives: Gal|Man |
[Gal|Man] |
Bare negation: !Fuc or
[!Fuc] |
[^Fuc] for a non-Fuc residue;
(!Fuc-) for branch absence |
Using . to skip an arbitrary path |
[.]* for zero or more connected
residues |
rex_detect() asks whether a match exists;
rex_subset() keeps matching inputs. Missing inputs give
NA in detection, and are omitted by subsetting.rex_extract() returns one retained match;
rex_extract_all() returns all retained matches. Extracted
strings use standardized IUPAC notation.rex_count() counts retained structural matches, not
glycan abundance. glyrex keeps the first successful match per starting
residue, then removes matches wholly contained in larger matches.
Remaining matches may overlap; it does not enumerate every possible
combination of alternatives or branches.rex_locate() and rex_locate_all() report
graph residue and edge IDs, not character positions in a string.The input must describe a supported connected structure. Composition
alone cannot answer connectivity questions, and floating parts or
unspecified branch placement are not supported. Invalid structures
produce errors rather than FALSE. Unspecified pattern
fields accept unknown input information, but specified constraints
require input information at least as specific.
See ?rex_compile for compiling reusable patterns and
?rex_detect for query functions and result conventions.