R/monosaccharide.R

Defines functions is_known_monosaccharide get_anomer_pos infer_anomer_pos available_monosaccharides .match_unusual_configuration_monosaccharide .natural_configuration_monosaccharide .ringless_monosaccharide

Documented in available_monosaccharides get_anomer_pos infer_anomer_pos is_known_monosaccharide

# This table referred to https://www.ncbi.nlm.nih.gov/glycans/snfg.html
natural_monosaccharide_definitions <- tibble::tribble(
  ~generic  , ~concrete  , ~anomer_pos ,
  # Hexose
  "Hex"     , "Glc"      , 1L          ,
  "Hex"     , "Man"      , 1L          ,
  "Hex"     , "Gal"      , 1L          ,
  "Hex"     , "Gul"      , 1L          ,
  "Hex"     , "Alt"      , 1L          ,
  "Hex"     , "All"      , 1L          ,
  "Hex"     , "Tal"      , 1L          ,
  "Hex"     , "Ido"      , 1L          ,
  # HexNAc
  "HexNAc"  , "GlcNAc"   , 1L          ,
  "HexNAc"  , "GalNAc"   , 1L          ,
  "HexNAc"  , "ManNAc"   , 1L          ,
  "HexNAc"  , "GulNAc"   , 1L          ,
  "HexNAc"  , "AltNAc"   , 1L          ,
  "HexNAc"  , "AllNAc"   , 1L          ,
  "HexNAc"  , "TalNAc"   , 1L          ,
  "HexNAc"  , "IdoNAc"   , 1L          ,
  # Hexosamine
  "HexN"    , "GlcN"     , 1L          ,
  "HexN"    , "ManN"     , 1L          ,
  "HexN"    , "GalN"     , 1L          ,
  "HexN"    , "GulN"     , 1L          ,
  "HexN"    , "AltN"     , 1L          ,
  "HexN"    , "AllN"     , 1L          ,
  "HexN"    , "TalN"     , 1L          ,
  "HexN"    , "IdoN"     , 1L          ,
  # Hexuronate
  "HexA"    , "GlcA"     , 1L          ,
  "HexA"    , "ManA"     , 1L          ,
  "HexA"    , "GalA"     , 1L          ,
  "HexA"    , "GulA"     , 1L          ,
  "HexA"    , "AltA"     , 1L          ,
  "HexA"    , "AllA"     , 1L          ,
  "HexA"    , "TalA"     , 1L          ,
  "HexA"    , "IdoA"     , 1L          ,
  # Deoxyhexose
  "dHex"    , "Fuc"      , 1L          ,
  "dHex"    , "Qui"      , 1L          ,
  "dHex"    , "Rha"      , 1L          ,
  "dHex"    , "6dGul"    , 1L          ,
  "dHex"    , "6dAlt"    , 1L          ,
  "dHex"    , "6dTal"    , 1L          ,
  # DeoxyhexNAc
  "dHexNAc" , "QuiNAc"   , 1L          ,
  "dHexNAc" , "RhaNAc"   , 1L          ,
  "dHexNAc" , "6dAltNAc" , 1L          ,
  "dHexNAc" , "6dTalNAc" , 1L          ,
  "dHexNAc" , "FucNAc"   , 1L          ,
  # Di-deoxyhexose
  "ddHex"   , "Oli"      , 1L          ,
  "ddHex"   , "Tyv"      , 1L          ,
  "ddHex"   , "Abe"      , 1L          ,
  "ddHex"   , "Par"      , 1L          ,
  "ddHex"   , "Dig"      , 1L          ,
  "ddHex"   , "Col"      , 1L          ,
  # Pentose
  "Pen"     , "Ara"      , 1L          ,
  "Pen"     , "Lyx"      , 1L          ,
  "Pen"     , "Xyl"      , 1L          ,
  "Pen"     , "Rib"      , 1L          ,
  # 3-deoxy-nonulosonic acids
  "NeuAc"   , "Neu5Ac"   , 2L          ,
  "NeuGc"   , "Neu5Gc"   , 2L          ,
  "gNeu"    , "Neu"      , 2L          ,
  "gKdn"    , "Kdn"      , 2L          ,
  # 3,9-dideoxy-nonulosonic acids
  "gPse"    , "Pse"      , 2L          ,
  "gLeg"    , "Leg"      , 2L          ,
  "gAci"    , "Aci"      , 2L          ,
  "g4eLeg"  , "4eLeg"    , 2L          ,
  # Unknown
  "gBac"    , "Bac"      , 1L          ,
  "Hep"     , "LDmanHep" , 1L          ,
  "gKdo"    , "Kdo"      , 2L          ,
  "HepA"    , "Dha"      , 2L          ,
  "Hep"     , "DDmanHep" , 1L          ,
  "MurAc"   , "MurNAc"   , 1L          ,
  "MurGc"   , "MurNGc"   , 1L          ,
  "gMur"    , "Mur"      , 1L          ,
  # Assigned
  "Pen"     , "Api"      , 1L          ,
  "Hex"     , "Fru"      , 2L          ,
  "Hex"     , "Tag"      , 2L          ,
  "Hex"     , "Sor"      , 2L          ,
  "Hex"     , "Psi"      , 2L
)


# SNFG assumes the L configuration for these residues and the D configuration
# for other applicable residues. Names that encode multiple configurations or
# do not have a single D/L configuration are excluded.
l_configuration_monosaccharides <- c(
  "Alt",
  "AltNAc",
  "AltN",
  "AltA",
  "Ido",
  "IdoNAc",
  "IdoN",
  "IdoA",
  "Fuc",
  "Rha",
  "6dAlt",
  "RhaNAc",
  "6dAltNAc",
  "FucNAc",
  "Col",
  "Ara",
  "Sor",
  "Api"
)

configuration_unspecified_monosaccharides <- c(
  "Neu",
  "Pse",
  "Leg",
  "Aci",
  "4eLeg",
  "LDmanHep",
  "DDmanHep"
)

configuration_monos <- setdiff(
  natural_monosaccharide_definitions$concrete,
  configuration_unspecified_monosaccharides
)
natural_monosaccharide_configurations <- stats::setNames(
  ifelse(configuration_monos %in% l_configuration_monosaccharides, "L", "D"),
  configuration_monos
)
unusual_configurations <- ifelse(
  natural_monosaccharide_configurations == "D",
  "L",
  "D"
)
unusual_configuration_monosaccharides <- stats::setNames(
  paste0(unusual_configurations, "-", configuration_monos),
  configuration_monos
)

unusual_configuration_rows <- natural_monosaccharide_definitions[
  match(configuration_monos, natural_monosaccharide_definitions$concrete),
]
unusual_configuration_rows$concrete <- unname(
  unusual_configuration_monosaccharides[unusual_configuration_rows$concrete]
)
monosaccharide_definitions <- dplyr::bind_rows(
  natural_monosaccharide_definitions,
  unusual_configuration_rows
)


# Furanose ring forms use an "f" after the monosaccharide stem. Pyranose
# forms remain implicit and retain their existing names.
natural_furanose_monosaccharides <- c(
  Glc = "Glcf",
  Man = "Manf",
  Gal = "Galf",
  Gul = "Gulf",
  Alt = "Altf",
  All = "Allf",
  Tal = "Talf",
  Ido = "Idof",
  GlcNAc = "GlcfNAc",
  GalNAc = "GalfNAc",
  ManNAc = "ManfNAc",
  GulNAc = "GulfNAc",
  AltNAc = "AltfNAc",
  AllNAc = "AllfNAc",
  TalNAc = "TalfNAc",
  IdoNAc = "IdofNAc",
  GlcN = "GlcfN",
  ManN = "ManfN",
  GalN = "GalfN",
  GulN = "GulfN",
  AltN = "AltfN",
  AllN = "AllfN",
  TalN = "TalfN",
  IdoN = "IdofN",
  GlcA = "GlcfA",
  ManA = "ManfA",
  GalA = "GalfA",
  GulA = "GulfA",
  AltA = "AltfA",
  AllA = "AllfA",
  TalA = "TalfA",
  IdoA = "IdofA",
  Fuc = "Fucf",
  Qui = "Quif",
  Rha = "Rhaf",
  `6dGul` = "6dGulf",
  `6dAlt` = "6dAltf",
  `6dTal` = "6dTalf",
  QuiNAc = "QuifNAc",
  RhaNAc = "RhafNAc",
  `6dAltNAc` = "6dAltfNAc",
  `6dTalNAc` = "6dTalfNAc",
  FucNAc = "FucfNAc",
  Oli = "Olif",
  Tyv = "Tyvf",
  Abe = "Abef",
  Par = "Parf",
  Dig = "Digf",
  Col = "Colf",
  Ara = "Araf",
  Lyx = "Lyxf",
  Xyl = "Xylf",
  Rib = "Ribf",
  Neu5Ac = "Neuf5Ac",
  Neu5Gc = "Neuf5Gc",
  Neu = "Neuf",
  Kdn = "Kdnf",
  Pse = "Psef",
  Leg = "Legf",
  Aci = "Acif",
  `4eLeg` = "4eLegf",
  Bac = "Bacf",
  LDmanHep = "LDmanHepf",
  Kdo = "Kdof",
  Dha = "Dhaf",
  DDmanHep = "DDmanHepf",
  MurNAc = "MurfNAc",
  MurNGc = "MurfNGc",
  Mur = "Murf",
  Api = "Apif",
  Fru = "Fruf",
  Tag = "Tagf",
  Sor = "Sorf",
  Psi = "Psif"
)

unusual_configuration_furanose_monosaccharides <- stats::setNames(
  paste0(
    unusual_configurations,
    "-",
    unname(natural_furanose_monosaccharides[configuration_monos])
  ),
  unname(unusual_configuration_monosaccharides)
)
furanose_monosaccharides <- c(
  natural_furanose_monosaccharides,
  unusual_configuration_furanose_monosaccharides
)
unusual_configuration_monosaccharides <- c(
  unusual_configuration_monosaccharides,
  stats::setNames(
    unname(unusual_configuration_furanose_monosaccharides),
    unname(natural_furanose_monosaccharides[configuration_monos])
  )
)

natural_furanose_rows <- natural_monosaccharide_definitions
natural_furanose_rows$concrete <- unname(
  natural_furanose_monosaccharides[natural_furanose_rows$concrete]
)
unusual_furanose_rows <- unusual_configuration_rows
unusual_furanose_rows$concrete <- unname(
  unusual_configuration_furanose_monosaccharides[
    unusual_furanose_rows$concrete
  ]
)
monosaccharides <- dplyr::bind_rows(
  natural_monosaccharide_definitions,
  natural_furanose_rows,
  unusual_configuration_rows,
  unusual_furanose_rows
)


.ringless_monosaccharide <- function(mono) {
  furanose_index <- match(mono, unname(furanose_monosaccharides))
  is_furanose <- !is.na(furanose_index)
  mono[is_furanose] <- names(furanose_monosaccharides)[
    furanose_index[is_furanose]
  ]
  mono
}


.natural_configuration_monosaccharide <- function(mono) {
  unusual_index <- match(
    mono,
    unname(unusual_configuration_monosaccharides)
  )
  is_unusual <- !is.na(unusual_index)
  mono[is_unusual] <- names(unusual_configuration_monosaccharides)[
    unusual_index[is_unusual]
  ]
  mono
}


.match_unusual_configuration_monosaccharide <- function(mono) {
  candidates <- unname(unusual_configuration_monosaccharides)
  candidates <- candidates[order(nchar(candidates), decreasing = TRUE)]
  matches <- candidates[stringr::str_starts(mono, candidates)]
  if (length(matches) == 0) NA_character_ else matches[[1]]
}


#' Get Available Monosaacharides
#'
#' This function returns a character vector of monosaccharide names of
#' the given type. See [get_mono_type()] for monosaacharide types.
#' Concrete furanose forms use an `f` after the monosaccharide stem, such as
#' `Galf` and `GlcfNAc`. Generic names do not encode ring form.
#' Less common absolute configurations use a leading `D-` or `L-`, such as
#' `D-Fuc`, `L-Gul`, and `D-Fucf`. Unprefixed names retain
#' their natural configurations.
#'
#' @param mono_type A character string specifying the type of monosaccharides.
#'  Can be "all", "generic", or "concrete". Default is "all".
#'
#' @returns A character vector of monosaccharide names.
#'
#' @examples
#' available_monosaccharides()
#' available_monosaccharides("concrete")
#'
#' @export
available_monosaccharides <- function(mono_type = "all") {
  checkmate::assert_choice(mono_type, c("all", "generic", "concrete"))
  if (mono_type == "all") {
    monos <- monosaccharides[c("generic", "concrete")]
    unique(purrr::discard(unlist(monos, use.names = FALSE), is.na))
  } else {
    unique(purrr::discard(monosaccharides[[mono_type]], is.na))
  }
}


#' Infer Anomer Positions
#'
#' This function infers the anomer position for concrete or generic
#' monosaccharide names.
#'
#' @param mono A character vector of monosaccharide names.
#'
#' @returns An integer vector of anomer positions.
#'
#' @aliases get_anomer_pos
#'
#' @examples
#' infer_anomer_pos(c("Gal", "Hex", "Neu5Ac"))
#'
#' @export
infer_anomer_pos <- function(mono) {
  checkmate::assert_character(mono, any.missing = FALSE)

  mono_names <- c(monosaccharides$concrete, monosaccharides$generic)
  mono_anomer_pos <- c(monosaccharides$anomer_pos, monosaccharides$anomer_pos)
  anomer_pos <- mono_anomer_pos[match(mono, mono_names)]
  unknown_monos <- mono[is.na(anomer_pos)]

  if (length(unknown_monos) > 0) {
    cli::cli_abort(c(
      "{.arg mono} must contain only known monosaccharide names.",
      "x" = "Invalid value{?s}: {.val {unique(unknown_monos)}}.",
      "i" = "Call {.fun available_monosaccharides} to see supported names."
    ))
  }

  names(anomer_pos) <- names(mono)
  anomer_pos
}


#' @rdname infer_anomer_pos
#' @export
get_anomer_pos <- function(mono) {
  infer_anomer_pos(mono)
}


#' Check if a Monosaccharide is Known
#'
#' This function checks if a vector of monosaccharide names are known.
#'
#' @param mono A character vector of monosaccharide names.
#'
#' @returns A logical vector.
#'
#' @examples
#' is_known_monosaccharide(c("Gal", "Hex"))
#' is_known_monosaccharide(c("X", "Hx", "Nac"))
#'
#' @export
is_known_monosaccharide <- function(mono) {
  checkmate::assert_character(mono)
  (mono %in% monosaccharides$generic | mono %in% monosaccharides$concrete)
}

Try the glyrepr package in your browser

Any scripts or data that you put into this service are public.

glyrepr documentation built on Sept. 22, 2026, 5:09 p.m.