Skip to content
Back to skills

Extracting Clinical Entities

ASecurity

Run clinical and biomedical named-entity recognition on medical text with OpenMed's analyze_text. Use when the user wants to extract diseases, drugs, anatomy, genes, or other biomedical entities from notes; needs NER output as dict/json/html/csv; wants to filter by confidence, group entities, toggle sentence detection, or save spans to JSONL; or wants the openmed analyze CLI. Pairs with loading-openmed-models and choosing-openmed-models, and runs after deidentifying-clinical-text in a privacy...

  • 5,450 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 12, 2026
developmentpythongobashgitapi

Works with

  • cli
  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned September 12, 2026

npx -y skills add maziyarpanahi/openmed --skill extracting-clinical-entities --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Extracting Clinical Entities?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Extracting Clinical Entities
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/maziyarpanahi-extracting-clinical-entities/badge)](https://www.skillsdirectory.com/skills/maziyarpanahi-extracting-clinical-entities)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: extracting-clinical-entities
description: "Run clinical and biomedical named-entity recognition on medical text with OpenMed's analyze_text. Use when the user wants to extract diseases, drugs, anatomy, genes, or other biomedical entities from notes; needs NER output as dict/json/html/csv; wants to filter by confidence, group entities, toggle sentence detection, or save spans to JSONL; or wants the openmed analyze CLI. Pairs with loading-openmed-models and choosing-openmed-models, and runs after deidentifying-clinical-text in a privacy-first pipeline."
license: Apache-2.0
metadata:
  project: OpenMed
  category: openmed-core
  pairs: adjacent
  version: "1.0"
---

# Extracting Clinical Entities

`openmed.analyze_text` runs a token-classification model over medical text and
returns structured entities with character offsets and confidence scores. It runs
**on-device** after a one-time model download.

## When to use

- Pull diseases, medications, anatomy, genes, proteins, etc. out of clinical text.
- You need exact **character spans** (start/end) plus **confidence** per entity.
- You want output as objects, JSON, an HTML highlight view, or CSV.
- You are building the "extract entities" stage of a clinical NLP pipeline.

To choose a model, see `choosing-openmed-models`. To load it once and reuse it,
see `loading-openmed-models`. **In a PHI workflow, de-identify first** (see
`deidentifying-clinical-text`), then run NER on the redacted text.

## Install

```bash
pip install "openmed[hf]"
```

## Quick start

```python
import openmed

note = (
    "Patient prescribed 500 mg metformin for type 2 diabetes mellitus. "
    "Reports intermittent chest pain; ruled out myocardial infarction."
)

result = openmed.analyze_text(
    note,
    model_name="disease_detection_superclinical",  # registry key, HF id, or local path
    output_format="dict",                           # dict | json | html | csv
    confidence_threshold=0.5,
)

for ent in result.entities:
    print(f"{ent.label:12} {ent.text!r:40} {ent.confidence:.2f} [{ent.start}:{ent.end}]")
```

With `output_format="dict"` you get a `PredictionResult`. The fields you use most:

```text
result.text          # the original input text
result.entities      # list of entity objects
result.model_name    # which model produced these
ent.text             # the surface string
ent.label            # entity type, e.g. "DISEASE"
ent.confidence       # model score in [0, 1]   (NOTE: .confidence, not .score)
ent.start / ent.end  # character offsets into result.text
```

## Output formats

`analyze_text(...)` returns different types depending on `output_format`:

| `output_format` | Return type | Use for |
| --- | --- | --- |
| `"dict"` (default) | `PredictionResult` object | Programmatic access via `.entities`. |
| `"json"` | `str` (JSON) | Logging, APIs, writing to disk. |
| `"html"` | `str` (HTML) | A highlighted preview of the note. |
| `"csv"` | `str` (CSV) | Spreadsheet / quick review. |

```python
import openmed

note = "Started atorvastatin 40 mg; history of myocardial infarction."

json_str = openmed.analyze_text(note, output_format="json")
html_str = openmed.analyze_text(note, output_format="html")   # render in a browser
csv_str  = openmed.analyze_text(note, output_format="csv")
```

## Key parameters

```python
openmed.analyze_text(
    text,
    model_name="disease_detection_superclinical",
    output_format="dict",
    confidence_threshold=0.5,    # drop entities below this score; None keeps all
    aggregation_strategy="simple",  # HF subword aggregation; None for raw tokens
    group_entities=False,        # merge adjacent same-label spans into one
    include_confidence=True,     # include scores in formatted output
    sentence_detection=True,     # pySBD sentence splitting (better long-doc spans)
    sentence_language="en",
    loader=None,                 # pass a reused ModelLoader (see loading skill)
)
```

- **`confidence_threshold`** — the most useful knob. Use the model's
  `recommended_confidence` (from `get_model_info`) as a starting point.
- **`group_entities=True`** — merges `"type"`, `"2"`, `"diabetes"` fragments into a
  single `"type 2 diabetes"` span. Turn on for cleaner output.
- **`sentence_detection=True`** (default) — splits long notes into sentences before
  inference for more accurate offsets and to respect model max length. Requires
  pySBD; if unavailable it silently falls back to whole-text inference.

## Save results to JSONL

One line per note keeps offsets and labels for downstream grounding or eval:

```python
import json
import openmed

notes = [
    "Type 2 diabetes managed with metformin.",
    "Acute myocardial infarction; started aspirin and atorvastatin.",
]

with open("entities.jsonl", "w", encoding="utf-8") as fh:
    for i, note in enumerate(notes):
        result = openmed.analyze_text(note, output_format="dict")
        fh.write(json.dumps({
            "doc_id": i,
            "text": result.text,
            "model": result.model_name,
            "entities": [
                {"label": e.label, "text": e.text,
                 "start": e.start, "end": e.end,
                 "confidence": round(e.confidence, 4)}
                for e in result.entities
            ],
        }) + "\n")
```

Store **offsets and labels**, not extra copies of free text, in PHI contexts.

## CLI

```bash
openmed analyze --text "Type 2 diabetes managed with metformin." \
  --model disease_detection_superclinical \
  --format json \
  --threshold 0.5 \
  --group

# Or analyze a file:
openmed analyze --input-file note.txt --model disease_detection_superclinical -o csv
```

Flags: `--text/-t`, `--input-file/-f`, `--model/-m`, `--format/-o`
(`dict|json|html|csv`), `--threshold/-c`, `--group`, `--no-confidence`,
`--sentence-detection/--no-sentence-detection`.

## Hand-off to / from OpenMed

- **From `loading-openmed-models`:** pass your reused `loader=` so a batch loads
  weights once.
- **From `deidentifying-clinical-text`:** run NER on `result.deidentified_text`,
  not raw PHI:

  ```python
  deid = openmed.deidentify(raw_note, method="mask", policy="hipaa_safe_harbor")
  ner  = openmed.analyze_text(deid.deidentified_text, output_format="dict")
  ```

- **To terminology grounding (out-of-process):** map `ent.text`/`ent.label` to
  RxNorm / LOINC / SNOMED using the user's own licensed service — OpenMed does not
  bundle restricted terminologies.
- **To batch processing:** for large corpora use `openmed.process_batch(...)` /
  `BatchProcessor` (see processing utilities) with a shared loader.

## Edge cases & gotchas

- **Attribute is `.confidence`, not `.score`.** Entity objects extend
  `EntityPrediction` (`text`, `label`, `confidence`, `start`, `end`).
- **Right model for the labels.** A Disease model won't emit oncology staging or
  gene labels — pick the category in `choosing-openmed-models` and check
  `entity_types`.
- **Offsets index `result.text`.** Slice the original string with `start:end`; the
  surface form in `ent.text` is whitespace-trimmed.
- **Long documents:** keep `sentence_detection=True` so chunks respect the model's
  max length (`get_model_max_length`); disabling it can truncate long notes.
- **NER assists, it does not diagnose.** Treat output as decision support; surface
  a disclaimer for any clinical-facing use.
- **No raw PHI in logs.** Log labels, offsets, and hashes — never patient text.

## Standards & references

- Token classification / NER (Hugging Face):
  https://huggingface.co/docs/transformers/tasks/token_classification
- pySBD sentence boundary detection: https://github.com/nipunsadvilkar/pySBD
- OpenMed model cards: https://huggingface.co/OpenMed

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.