Agent Skillsmaziyarpanahi/openmed › normalizing-rxnorm

normalizing-rxnorm

GitHub

将OpenMed提取的药物名称标准化为RxNorm RxCUI,支持去重、品牌/通用名解析及NDC关联。利用免费RxNav API,适用于医疗数据编码与互操作性场景。

skills/normalizing-rxnorm/SKILL.md maziyarpanahi/openmed

Trigger Scenarios

用户需要将药物名称标准化或编码为RxCUI 需要区分成分与具体药品(SCD/SBD) 需要获取NDC代码或构建US Core Medication资源 使用关键词如RxNorm, RxCUI, RxNav, drug normalization

Install

npx skills add maziyarpanahi/openmed --skill normalizing-rxnorm -g -y
More Options

Use without installing

npx skills use maziyarpanahi/openmed@normalizing-rxnorm

指定 Agent (Claude Code)

npx skills add maziyarpanahi/openmed --skill normalizing-rxnorm -a claude-code -g -y

安装 repo 全部 skill

npx skills add maziyarpanahi/openmed --all -g -y

预览 repo 内 skill

npx skills add maziyarpanahi/openmed --list

SKILL.md

Frontmatter
{
    "name": "normalizing-rxnorm",
    "license": "Apache-2.0",
    "metadata": {
        "pairs": "after",
        "project": "OpenMed",
        "version": "1.0",
        "category": "terminology-coding"
    },
    "description": "Normalizes drug mentions extracted by OpenMed to RxNorm RxCUIs using the free public RxNav\/RxNorm REST API. Use when the user wants to code, standardize, or de-duplicate medication names, resolve a brand\/generic\/ingredient to a stable RxCUI, link strength+dose-form to an SCD\/SBD, attach NDCs, or build a US Core Medication resource. Trigger keywords: RxNorm, RxCUI, RxNav, drug normalization, medication coding, NDC, ingredient, SCD, SBD, brand vs generic, getApproximateMatch. Pairs after OpenMed NER: consume Pharmaceutical\/Chemical entities from openmed.analyze_text and map each drug span to an RxCUI. RxNorm and RxNav are fully public and free — no API key, no license barrier, the lowest-friction terminology in this set."
}

Normalizing drug mentions to RxNorm

Map free-text medication mentions that OpenMed extracts to RxNorm — the U.S. National Library of Medicine's normalized drug nomenclature. The unit of meaning is the RxCUI (RxNorm Concept Unique Identifier): a stable integer that ties together brand, generic, ingredient, strength, and dose form.

RxNorm and the RxNav REST API are fully public and free: no API key, no license agreement, no rate-limit registration for normal use. Of every skill in this terminology batch, this one has the highest value-to-friction ratio — start here when grounding medications.

When to use

  • A clinical note names drugs ("metformin 500 mg", "Lipitor", "amox/clav") and you need one stable code per drug for storage, analytics, or interoperability.
  • You must distinguish ingredient ("metformin", IN) from a prescribable product — SCD (Semantic Clinical Drug, generic) or SBD (Semantic Brand Drug) — e.g. "metformin 500 MG Oral Tablet".
  • You need to de-duplicate brand/generic synonyms onto one concept.
  • You need NDC codes (package-level) for a product, or a US Core Medication/MedicationRequest coded with RxNorm.

If the source text is non-English or you need ATC/SNOMED links instead, see mapping-to-snomed; RxNorm itself is U.S.-centric.

Quick start (real RxNav API calls)

Base URL: https://rxnav.nlm.nih.gov/REST. No auth. JSON via ?...&... paths ending in nothing or .json depending on endpoint; the REST root returns XML by default, so request JSON explicitly.

import requests

BASE = "https://rxnav.nlm.nih.gov/REST"

def rxcui_for(name: str) -> str | None:
    """Exact-match RxCUI lookup for a normalized drug name."""
    r = requests.get(f"{BASE}/rxcui.json", params={"name": name}, timeout=10)
    r.raise_for_status()
    ids = r.json().get("idGroup", {}).get("rxnormId", [])
    return ids[0] if ids else None

def approximate(name: str, max_entries: int = 3) -> list[dict]:
    """Fuzzy match for misspelled or abbreviated drug text."""
    r = requests.get(
        f"{BASE}/approximateTerm.json",
        params={"term": name, "maxEntries": max_entries},
        timeout=10,
    )
    r.raise_for_status()
    return r.json().get("approximateGroup", {}).get("candidate", [])

print(rxcui_for("metformin"))                       # -> '6809' (ingredient)
print(approximate("metformin 500"))                 # fuzzy -> candidate RxCUIs

Resolve a full prescribable product (ingredient + strength + form) to an SCD:

# getApproximateMatch / getRxConceptProperties give term type (TTY)
def properties(rxcui: str) -> dict:
    r = requests.get(f"{BASE}/rxcui/{rxcui}/properties.json", timeout=10)
    r.raise_for_status()
    return r.json().get("properties", {})

# Find the SCD ("metformin 500 MG Oral Tablet") from the ingredient:
def related_by_tty(rxcui: str, tty: str) -> list[dict]:
    r = requests.get(
        f"{BASE}/rxcui/{rxcui}/related.json", params={"tty": tty}, timeout=10
    )
    r.raise_for_status()
    groups = r.json().get("relatedGroup", {}).get("conceptGroup", [])
    out = []
    for g in groups:
        out.extend(g.get("conceptProperties", []) or [])
    return out

Attach NDCs and check interactions (both public):

ndcs = requests.get(f"{BASE}/rxcui/{rxcui}/ndcs.json").json()   # package codes

Workflow

  1. Extract drug spans with OpenMed (pharma_detection_superclinical).
  2. Parse each span into name + strength + dose form when present ("metformin 500 mg tablet" → ingredient metformin, strength 500 MG, form Oral Tablet).
  3. Exact match the cleaned name with /rxcui.json?name=. If empty, fall back to /approximateTerm.json.
  4. Pick the right term type (TTY) for your use case:
    • IN ingredient — analytics, allergy lists, class rollups.
    • SCD generic product / SBD brand product — orders, US Core Medication.
    • BN brand name, PIN precise ingredient — display/lineage.
  5. Validate by reading /rxcui/{rxcui}/properties.json and confirming the tty and name match expectations; record the score from approximate matches as a confidence signal.
  6. Emit {system: "http://www.nlm.nih.gov/research/umls/rxnorm", code, display}.

Hand-off from OpenMed

OpenMed's analyze_text returns a dict whose entities list contains, per span, the keys text, label, confidence, start, end. Consume the Pharmaceutical/Chemical entities directly:

import openmed, requests

note = "Patient on metformin 500 mg BID and atorvastatin 20 mg nightly."
result = openmed.analyze_text(
    note,
    model_name="pharma_detection_superclinical",   # Pharmaceutical category
    output_format="dict",
)

DRUG_LABELS = {"DRUG", "MEDICATION", "CHEM"}        # OpenMed Pharmaceutical labels
for ent in result["entities"]:
    if ent["label"] in DRUG_LABELS:
        span = ent["text"]                          # e.g. "metformin"
        rxcui = rxcui_for(span) or (
            (approximate(span) or [{}])[0].get("rxcui")
        )
        print(span, "->", rxcui, f"(conf {ent['confidence']:.2f})")

Keep OpenMed's character offsets (start/end) alongside the RxCUI so every code is traceable back to the exact source span — never store the raw note text in your mapping table.

Edge cases & gotchas

  • Strength/form live in separate spans. OpenMed labels the drug name; the "500 mg" and "tablet" may be adjacent tokens. Reassemble using offsets before querying for an SCD, or you will only get the ingredient.
  • Combination products ("amoxicillin/clavulanate") normalize to a single multi-ingredient SCD; do not split them into two RxCUIs.
  • Brand vs generic. Lipitor (SBD/BN) and atorvastatin (IN/SCD) are different RxCUIs of the same drug. Decide up front which TTY your pipeline stores and map the other via /related.json.
  • Approximate-match noise. approximateTerm will happily return a candidate for garbage input. Gate on the returned score and re-validate with /properties.json before trusting it.
  • Obsolete RxCUIs. Use /rxcui/{rxcui}/historystatus.json to detect retired/remapped concepts; follow the remap rather than storing a dead code.
  • Licensing: none for RxNorm/RxNav. RxNorm is public domain. But RxNorm includes source vocabularies (e.g. some proprietary drug data) whose own terms-of-use apply if you redistribute the full dataset — calling the live API for normalization is unrestricted. Do not bundle UMLS to get RxNorm; RxNav is the clean path.
  • Local-first stays intact. Run OpenMed NER on-device; only the de-identified drug string leaves the process to hit RxNav. Never send a raw note containing PHI to the API.

Standards & references

Version History

  • f213557 Current 2026-07-23 00:45

Same Skill Collection

skills/building-with-openmed/SKILL.md
skills/loading-openmed-models/SKILL.md
skills/annotating-variants/SKILL.md
skills/assembling-fhir-bundles/SKILL.md
skills/auditing-deid-leakage/SKILL.md
skills/auditing-deidentification-runs/SKILL.md
skills/auditing-part11-trails/SKILL.md
skills/auditing-safe-harbor-checklist/SKILL.md
skills/auditing-subgroup-fairness/SKILL.md
skills/authoring-model-cards/SKILL.md
skills/batch-processing-clinical-text/SKILL.md
skills/benchmarking-clinical-ner/SKILL.md
skills/bridging-presidio-and-spacy/SKILL.md
skills/building-gold-corpus/SKILL.md
skills/building-patient-timelines/SKILL.md
skills/checking-hipaa-compliance/SKILL.md
skills/choosing-openmed-models/SKILL.md
skills/coding-hcc-risk-adjustment/SKILL.md
skills/coding-icd10/SKILL.md
skills/computing-ecqms/SKILL.md
skills/configuring-privacy-policies/SKILL.md
skills/defining-cohort-phenotypes/SKILL.md
skills/deidentifying-clinical-text/SKILL.md
skills/deidentifying-multilingual-text/SKILL.md
skills/deploying-openmed-mcp/SKILL.md
skills/detecting-pv-signals/SKILL.md
skills/enforcing-nophi-logging/SKILL.md
skills/etl-to-omop-cdm/SKILL.md
skills/evaluating-with-leakage-gates/SKILL.md
skills/exporting-bulk-fhir/SKILL.md
skills/exporting-to-fhir/SKILL.md
skills/extracting-clinical-entities/SKILL.md
skills/extracting-dicom-metadata/SKILL.md
skills/extracting-lab-tables/SKILL.md
skills/extracting-pii-entities/SKILL.md
skills/extracting-sdoh/SKILL.md
skills/fetching-fhir-resources/SKILL.md
skills/gating-deid-leakage/SKILL.md
skills/generating-synthea-data/SKILL.md
skills/generating-synthetic-surrogates/SKILL.md
skills/ingesting-clinical-documents/SKILL.md
skills/linking-umls-concepts/SKILL.md
skills/mapping-loinc/SKILL.md
skills/mapping-to-snomed/SKILL.md
skills/mining-pubmed-literature/SKILL.md
skills/parsing-ccda-documents/SKILL.md
skills/parsing-hl7v2-messages/SKILL.md
skills/parsing-lab-values/SKILL.md
skills/parsing-trial-eligibility/SKILL.md

Metadata

Files
0
Version
f213557
Hash
c9bef629
Indexed
2026-07-23 00:45

ホーム - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-07-29 13:11
浙ICP备14020137号-1 $お客様$