Overview
rxref provides tidy, vectorized tools for working with
drug identifiers and metadata from the RxNorm and RxClass APIs (part of
the Unified
Medical Language System maintained by the U.S. National Library of
Medicine. It is designed for workflows that need to resolve drug names
and identifiers, inspect RxNorm
concepts, expand ingredients to product concepts, map between RxNorm
Concept Unique Identifiers (RxCUIs) and National Drug Codes (NDCs),
retrieve clinical product attributes, and query drug-class
information.
A common medication-list workflow looks like this:
- resolve a drug name or identifier to an RxCUI;
- identify the ingredient concept or concepts of interest;
- expand ingredients to RxNorm product concepts;
- refine the product set using term type, route, combination-product status, or other clinical attributes;
- map the final product concepts to NDCs when needed; and
- save the resulting medication definition for reproducible downstream use.
The examples below query live RxNorm or RxClass APIs and therefore
are not evaluated when this vignette is built. You can run them
interactively after installing rxref.
Installation
Install the stable version of rxref from CRAN:
install.packages("rxref")Or install the development version from GitHub:
# install.packages("pak")
# pak::pak("ssmithm/rxref")Then load rxref. The examples below also use
dplyr for data manipulation.
Resolve drug names and identifiers
resolve() accepts free-text drug names, NDCs, and
RxCUIs. With the default type = "auto", rxref
infers the input type and returns the corresponding RxNorm concept
information.
This is useful when medication data arrive in mixed or imperfect formats and you first need to determine which RxNorm concepts they represent.
Inspect RxNorm concepts
Once you have one or more RxCUIs, use get_properties()
to retrieve core RxNorm metadata such as the preferred concept name,
term type (TTY), suppress flag, and UMLS Concept Unique Identifier
(CUI).
ids <- c("860975", "860976")
get_properties(ids)Understanding the TTY is often important because RxNorm distinguishes ingredients, clinical drugs, branded drugs, packs, and several other concept types. Only some of these are prescribable products, as discussed below.
Identify ingredient concepts
For medication-list construction, the next step is often to identify the ingredient-level RxCUIs corresponding to one or more drug names.
find_ingredients(c("metformin", "semaglutide", "lisinopril"))find_ingredients() returns ingredient (IN)
and, when available, precise ingredient (PIN) concepts. A
common pattern is to retain the base ingredient concepts before
expanding them to products.
ingredients <- find_ingredients(c("metformin", "semaglutide")) |>
filter(tty == "IN") |>
distinct(
ingredient_rxcui = rxcui,
ingredient_name = name
)
ingredientsExpand ingredients to product concepts
Use products_for_ingredients() to identify RxNorm
product concepts that contain the selected ingredients.
products <- products_for_ingredients(
ingredients$ingredient_rxcui
)
productsBy default, rxref uses a product-focused set of TTYs
corresponding to semantic clinical drugs, semantic branded drugs,
generic packs, and branded packs. These are the concept types most
likely to be useful when the ultimate goal is NDC mapping.
You can inspect the available predefined TTY sets with:
product_ttys("default")
product_ttys("extended_product")
product_ttys("extended")For more detail about individual RxNorm term types, use:
A broader TTY set can be supplied explicitly when the study question requires additional product-related concepts:
products_extended <- products_for_ingredients(
ingredients$ingredient_rxcui,
ttys = product_ttys("extended_product")
)
products_extendedThe narrowest useful product definition is often preferable. Broader TTY sets can capture additional structural or grouping concepts, but many of these do not map directly to NDCs.
Refine products using clinical attributes
get_clinical_attributes() adds clinically useful
information such as strength, dose form, route, dose-form group,
brand/generic status, and ingredient count.
attrs <- get_clinical_attributes(products$product_rxcui)
attrsFor example, you can inspect the routes and dose-form groups represented in a product set:
attrs |>
count(route, dose_form_group, sort = TRUE)Or identify combination products:
attrs |>
filter(ingredient_count > 1)Clinical attributes are derived from RxNorm concept information, including some parsing of RxNorm names and dose-form metadata. They are useful for medication-list construction, but study-specific product definitions should still be reviewed before final use.
Filter products by route
When only one route is relevant,
filter_products_by_route() provides a convenient way to
retain products matching that route.
oral_products <- products |>
filter_products_by_route(route = "ORAL")
oral_productsRoute filtering can also be requested directly during product expansion:
oral_products <- products_for_ingredients(
ingredients$ingredient_rxcui,
route = "ORAL"
)For more involved medication definitions, it is often useful to inspect the unfiltered product set first and then apply route restrictions explicitly.
Map between RxCUIs and NDCs
rxref provides helpers for mapping in both directions
between NDCs and RxCUIs.
To map NDCs to RxCUIs:
map_ndc_to_rxcui(c(
"00093-1048-01",
"00093-1048-10"
))To map product RxCUIs to currently active NDC associations:
ndcs <- map_rxcui_to_ndc(
oral_products$product_rxcui,
status = "ACTIVE"
)
ndcsBecause multiple NDCs may correspond to the same RxNorm product concept, NDC mapping commonly increases the number of rows substantially.
Use search_drug() for a compact workflow
The preceding sections show the individual steps so that users can
inspect and control each stage of medication-list construction. For many
common workflows, search_drug() combines ingredient search,
product expansion, route filtering, and optional NDC mapping in one
call.
To return oral metformin product concepts:
search_drug(
term = "metformin",
return = "rxcui",
route = "ORAL"
)To return currently active NDCs:
search_drug(
term = "metformin",
return = "ndc",
route = "ORAL",
ndc_status = "ACTIVE"
)Or return both product concepts and NDCs:
metformin <- search_drug(
term = "metformin",
return = "both",
route = "ORAL",
ndc_status = "ACTIVE"
)
metformin$products
metformin$ndcssearch_drug() is useful for concise workflows, while the
step-by-step approach is preferable when you need to audit intermediate
concepts or apply custom study-specific rules.
Work with historical concepts and NDC associations
Historical medication-list construction involves two related but distinct questions:
- RxNorm concept history: should the product search include concepts that are no longer active in the current RxNorm release?
- NDC association history: should NDC mapping include historical direct or indirect NDC associations rather than only currently active associations?
To include active and historical RxNorm product concepts, use
concept_status = "active_and_historical":
products_historical <- products_for_ingredients(
ingredients$ingredient_rxcui,
concept_status = "active_and_historical"
)
products_historicalTo retrieve historical NDC associations for selected product RxCUIs,
use the history argument to
map_rxcui_to_ndc():
ndcs_historical <- map_rxcui_to_ndc(
products_historical$product_rxcui,
history = "all"
)
ndcs_historicalUse history = "direct" when only NDCs ever directly
associated with the input RxCUI are desired, and
history = "all" when indirect associations through remapped
or archived concepts should also be considered. The status
argument can then be used to filter the NDCs that were retrieved.
Historical concepts and NDCs can be important when reconstructing exposure in older study periods. They also require additional review because historical concepts may have less complete route, dose-form, or other clinical metadata than current concepts.
Query drug classes with RxClass
rxref also provides access to drug-class relationships
from the RxClass API. Because different sources classify drugs
differently, class source and relationship information are retained
rather than collapsed into a single universal class definition.
Source-specific convenience functions include get_atc(),
get_epc(), and get_va().
For a broader class-oriented summary, get_drug_classes()
combines selected class-like assertions from multiple sources:
get_drug_classes("metformin", by = "name")get_drug_classes() is experimental because “drug class”
is not a single native RxClass concept and different source vocabularies
use different classification logic. For source-specific or
relationship-specific work, use get_classes() and the
dedicated RxClass helpers.
Configure API behavior
rxref uses an in-memory cache by default so repeated
identical requests within a session do not need to be sent to the API
again. Current package settings can be inspected with:
You can also adjust the delay between API requests:
rxref_conf(rate_delay = 0.2)For large batch workflows, caching and an appropriate request delay help reduce unnecessary API traffic. RxNav currently specifies a maximum request rate of 20 requests per second per IP address; see the RxNav Terms of Service.
Reproducibility
The public RxNorm and RxClass APIs reflect source data that change as new releases are published. For analyses that require a reproducible medication definition, save the final ingredient list, product concepts, NDC mappings, and other API-derived metadata used in the analytic workflow.
This is especially important for studies tied to historical calendar periods, because the concepts and mappings returned by the current APIs may differ from those available when the original data were generated.
Summary
A typical rxref workflow is:
- resolve names or identifiers with
resolve(); - inspect concepts with
get_properties(); - identify ingredients with
find_ingredients(); - expand ingredients to products with
products_for_ingredients(); - refine products using
get_clinical_attributes()andfilter_products_by_route(); - map between RxCUIs and NDCs with
map_ndc_to_rxcui()andmap_rxcui_to_ndc(); - use
search_drug()when a compact end-to-end workflow is sufficient; and - query drug-class information with the RxClass helpers when needed.
These tools are intended to support transparent, auditable, and
reproducible medication-list construction while keeping outputs tidy and
compatible with common tidyverse workflows.
