Data Dictionary Harmonization Tool: cluster equivalent variables across data dictionaries and anchor them to Common Data Elements (CDEs)
Project description
ddharmon — Data Dictionary Harmonization Tool
ddharmon harmonizes biomedical data dictionaries. It clusters equivalent variables across many studies, assigns each concept to an existing Common Data Element (CDE) from the NIH catalog — or flags it for a newly generated CDE when nothing fits — and routes every recommendation to expert review.
Try it in your browser — no install — at ddharmon.io.
Prefer to run it yourself? ddharmon is a Python package: reach for the hosted app above for a quick look,
the ddharmon CLI for scriptable and batch/headless runs, and the Python library (see the
example notebook) to build it into your own pipelines.
It reads data-dictionary metadata only — never participant-level data — so restricted cohorts can run
entirely within their own infrastructure.
Quick start
Requirements: Python 3.12+ and uv.
git clone https://github.com/Phenome-Health/ddharmon.git
cd ddharmon
uv sync --extra all
cp .env.example .env # set ANTHROPIC_API_KEY for the LLM passes (sync / batch)
Then open notebooks/clustering/harmonization_pipeline.ipynb — it runs the full pipeline end to
end on bundled example data (All of Us + CLSA dictionaries against the NIH CDE catalog) and is the best
place to start. A MAX_CLUSTERS cap keeps the demo cheap; the embedding and clustering stages run with no
API key.
The bundled
data/examples/all_cdes_flat.tsvis a flattened snapshot of the NIH CDE catalog. To refresh it or use your own, runscripts/flatten_cde_repo.py <All-CDEs.json> <out.tsv>. Without a CDE catalog, ddharmon still clusters your variables — it just won't assign CDEs.
Command-line usage
Besides the notebook, ddharmon ships two subcommands. Both take dictionaries inline as NAME=path or via
a JSON --config.
ddharmon harmonize — the full split-aware pipeline (embed → cluster → retrieve → generate-ideal →
split → assign/route → transform specs → EITL campaign):
# harmonize two cohorts against a CDE backbone
ddharmon harmonize AoU=all_of_us_surveys.csv CLSA=clsa_baseline.csv --cde all_cdes_flat.tsv -o out/
# reproduce the bundled example (All of Us + CLSA vs the NIH CDE catalog)
ddharmon harmonize --config data/examples/harmonize_example.json -o out/
# $0 dry run: build the generate-ideal prompts without calling the LLM
ddharmon harmonize --config data/examples/harmonize_example.json --dry-run -o out/
Set ANTHROPIC_API_KEY for the full run; without it (or with --dry-run) ddharmon writes the stage-1
prompts for later batch submission. Outputs a records.json plus an eitl/ expert-review campaign. Cost
knobs: --max-clusters N (harmonize the N largest clusters first), --top-k, --min-cluster-size.
ddharmon cluster — cluster equivalent variables only ($0, no LLM):
ddharmon cluster AoU=all_of_us_surveys.csv CLSA=clsa_baseline.csv -o clusters.json
Columns are auto-detected; override them per dictionary in the --config columns block (keys are
load_dictionary's kwargs).
How it works
ddharmon leads with assignment to the existing CDE backbone for concepts that are already covered, and only sends the uncovered tail to generation/clustering:
ingest (study dictionaries + NIH CDE catalog)
→ embed variables (BioLORD semantic vectors, SQLite-cached)
→ cluster equivalent variables (coarse semantic clusters)
→ per cluster: retrieve candidate CDEs (hybrid: BM25 + dense, fused with RRF)
→ generate an "ideal" CDE (an independent coverage anchor)
→ split the cluster into distinct concept-groups
→ per group: rank the candidates and decide adopt / refine / novel
→ route: adopt / refine → assign that CDE
novel → generate a CDE / re-cluster the tail
→ expert-in-the-loop (EITL) review queue
Nothing is auto-applied — every adopt/refine/novel recommendation lands in a review queue. Full algorithm,
parameters, and design rationale are in docs/methods.md.
What ddharmon adds
Most tools cluster either a CDE repository or a single cohort, use a single semantic vector, and map element-by-element. ddharmon targets harmonizing many studies at once:
- Assignment-first, across many studies. Variables from many study dictionaries and the NIH CDE catalog are embedded together, so equivalent variables surface as one cluster and each concept is assigned to a real CDE — not one pairwise lookup at a time.
- Hybrid retrieval. Candidate CDEs come from a fusion (reciprocal rank fusion) of lexical BM25 and dense similarity using BioLORD-2023, a concept↔definition biomedical encoder — stronger candidates than dense-only retrieval.
- Split-aware concept resolution. A coarse cluster that happens to pool more than one concept is split into distinct concept-groups, so each gets its own CDE decision — distinct concepts are never silently merged onto a single CDE.
- A novel-CDE path. When no existing CDE fits, the concept is routed to a generated (GenCDE) candidate and/or re-clustered with the rest of the uncovered tail, instead of being forced onto a poor match.
- adopt / refine / novel → expert review. Every recommendation is routed to expert-in-the-loop (EITL) review; nothing is applied automatically.
- Reproducible evaluation. Standing, $0, no-proprietary-data benchmarks (see
benchmarks/) gate retrieval and clustering quality on every change.
Limitations & measured quality
ddharmon is a data-dictionary harmonizer: it works on metadata (field names, descriptions, value codings) from public catalogs, never participant-level data. It proposes; humans decide — every recommendation carries a confidence and verdict and is routed to expert-in-the-loop review. It emits transform specs (including parameterized specs for data-dependent transforms) but does not execute them on data. And it scores cross-dictionary harmonization (pooling equivalent fields across ≥2 dictionaries) separately from single-dictionary CDE assignment (the CDEMapper / DIVER task).
Measured quality — BioLORD-2023 encoder + hybrid BM25⊕dense retrieval + split-aware LLM assignment. These are current baselines, not ceilings; dev-vs-held-out is labelled, and dev numbers (measured on the set we tuned against) should be read as optimistic:
| Signal | Number | Gold set | Held-out? |
|---|---|---|---|
| Candidate retrieval recall@5 / @100 | 0.674 / 0.926 | CDEMapper | dev (tuned on it) |
| In-backbone CDE assignment | 0.521 | CDEMapper | dev |
| Cross-cohort separability Δ | 0.611 | PhenX | held-out |
| Variable → concept recall@5 / @100 | 0.655 / 0.914 | AI-READI OMOP/CDE | held-out |
| Value-recode pair accuracy | 0.869 | ATHLOS | held-out |
| N1 unit-conversion accuracy | 1.00 | curated UCUM gold | deterministic |
On a full public 5-cohort run (18,128 fields), 97.5% of fields reach a record, 60.7% land in a real-concept group, and 41.9% of records are assigned to a CDE — a coverage snapshot of what a run produces, not a gold-accuracy score.
Known limitations:
- We do not outperform dedicated CDE mappers on retrieval recall, and don't claim to. ddharmon is a convergent method with extended scope (cross-dictionary pooling + transform-spec generation). CDEMapper is also our tuning set, so its numbers are optimistic; PhenX and AI-READI are the held-out generalization checks.
- The adopt / refine / novel router is conservative and uncalibrated, by design — a strict cutoff plus a 0.30 retrieval floor. Expect over-routing to review (novel-precision ≈ 0.5): a recall-favoring safety bias. Calibrating the thresholds against human review verdicts is a planned follow-up, not part of this release.
- Numeric transforms are partial by construction: unit (N1) and arithmetic (N2) specs are authored and gold-gated; data-dependent transforms (quantile binning, normalization) are detected and emitted as a parameterized spec routed to review — not authored with final values.
- Pairwise 1:1 mapping is not yet a first-class surface (the engine exists).
- Clustering is not bit-reproducible across machines (UMAP / HDBSCAN); reproducibility comes from freezing the clustering substrate and caching the LLM responses.
Related work
Framing biomedical variable/CDE harmonization as an embedding → clustering → LLM problem is an active line of work; ddharmon builds directly on it:
- CDEMapper (Wang et al., JAMIA 2025) — LLM-powered mapping of local data elements to NIH CDEs via semantic indexing + BM25 + GPT candidates + human review. Per-element lookup.
- DataTecnica — DIVER (Long et al., npj Digital Medicine 2026) — an AI-assisted pipeline that generates and audits Common Data Elements at scale (GenCDEs) to align data standards. Closest to generating novel CDEs.
- Krishnamurthy et al., 2025 (arXiv:2506.02160) — embeds ~24k NIH CDEs and clusters them with HDBSCAN, then LLM-labels the clusters. Clusters the target CDE repository.
- PASSIONATE (Salimi et al., Sci Rep 2025) — a Parkinson's variable-mapping ground truth; shows language-model embeddings beat fuzzy string matching for pairwise cohort harmonization.
- Harmony (McElroy et al., BMC Psychiatry 2024), Semantic Search Helper (Gottfried 2025), datastew, and BDI-Kit (Lopez et al., Patterns 2026) — embedding/LLM harmonization and schema/value-matching siblings.
Installation
The core install is lightweight; optional extras unlock additional capabilities.
| Extra | Adds |
|---|---|
| (none) | Core ingestion + lexical matching |
embeddings |
sentence-transformers + faiss-cpu (semantic embedding + vector search) |
llm |
openai + anthropic (LLM assignment / reranking) |
clustering |
scikit-learn, UMAP, plotly |
viz |
matplotlib, seaborn, scipy |
bertopic |
BERTopic topic modeling |
all |
everything above — required to run the full pipeline + notebook |
notebooks |
all + Jupyter |
dev |
all + pytest, ruff, black, pyright |
pip install "ddharmon[all]" # full pipeline
# or, for development:
uv sync --extra dev
The package also installs a ddharmon console command (ddharmon --help); the notebook is the primary
end-to-end entry point.
Development
./scripts/check.sh # lint, format, typecheck, test
./scripts/fix.sh # auto-fix lint + format
pytest # tests
Project structure
src/ddharmon/
├── models/ # data models (dataclasses)
├── ingestion/ # study + CDE dictionary parsers, preprocessing
├── embedding/ # BioLORD semantic embedding, SQLite cache
├── clustering/ # semantic clustering (BERTopic / HDBSCAN) + residual re-clustering
├── matching/ # hybrid retrieval (BM25 + dense, RRF) + pairwise matching
├── harmonization/ # split-aware assignment → adopt/refine/novel + EITL export
├── llm/ # LLM clients + Anthropic Batch API
├── values/ # value-encoding parsing
└── export/ # EITL campaigns + visualization
benchmarks/ # reproducible $0 eval gates (CDEMapper, PhenX, ATHLOS) + gate.py
notebooks/clustering/harmonization_pipeline.ipynb # end-to-end entry point
data/examples/ # bundled dictionaries (All of Us, CLSA) + flattened NIH CDE catalog
scripts/ # data builders, prompt runners, release verification, check/fix
tests/
Benchmarks
benchmarks/ runs standing, reproducible, $0 evaluations on public data — CDEMapper (variable→CDE
retrieval), PhenX (cross-cohort co-clustering), and ATHLOS (value-recode correctness) — with
benchmarks/gate.py asserting hard floors on the deterministic signals. See
benchmarks/README.md.
Roadmap
- Pairwise 1:1 mapping as a first-class surface (the engine is built).
- Standards mapping (LOINC / SNOMED / OMOP).
- Calibrated adopt/refine/novel thresholds — tuned from expert-review verdicts (the router ships conservative today; transform-spec authoring already generates categorical + unit/arithmetic specs).
License
MIT — see LICENSE. © 2026 Phenome Health.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ddharmon-1.0.0.tar.gz.
File metadata
- Download URL: ddharmon-1.0.0.tar.gz
- Upload date:
- Size: 3.5 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1a593de633d257f59b09c4ba932120628307a55aab49359b8b03224596a4e0c4
|
|
| MD5 |
a2100992128aa1273c7b5f6f987b961b
|
|
| BLAKE2b-256 |
0ff0cbf5386a0747bfa0ceb980f9dbf37283b4c6fd583276381ec7784b2b77be
|
Provenance
The following attestation bundles were made for ddharmon-1.0.0.tar.gz:
Publisher:
publish.yml on Phenome-Health/ddharmon
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ddharmon-1.0.0.tar.gz -
Subject digest:
1a593de633d257f59b09c4ba932120628307a55aab49359b8b03224596a4e0c4 - Sigstore transparency entry: 2165206493
- Sigstore integration time:
-
Permalink:
Phenome-Health/ddharmon@8c6ed642729f30512445bcdcc18e1f12c6f120ed -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/Phenome-Health
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8c6ed642729f30512445bcdcc18e1f12c6f120ed -
Trigger Event:
release
-
Statement type:
File details
Details for the file ddharmon-1.0.0-py3-none-any.whl.
File metadata
- Download URL: ddharmon-1.0.0-py3-none-any.whl
- Upload date:
- Size: 191.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aef1787e11945b080e0ff77e14c44b777d857cffd9feb06f7ebb4b50ea50d04f
|
|
| MD5 |
c6d81fa796d6e84a303825582dedff9f
|
|
| BLAKE2b-256 |
245d6a95ed6b68715bc545df443c19b7278536f693bd8d87da68118716f2dc1e
|
Provenance
The following attestation bundles were made for ddharmon-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on Phenome-Health/ddharmon
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ddharmon-1.0.0-py3-none-any.whl -
Subject digest:
aef1787e11945b080e0ff77e14c44b777d857cffd9feb06f7ebb4b50ea50d04f - Sigstore transparency entry: 2165206504
- Sigstore integration time:
-
Permalink:
Phenome-Health/ddharmon@8c6ed642729f30512445bcdcc18e1f12c6f120ed -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/Phenome-Health
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8c6ed642729f30512445bcdcc18e1f12c6f120ed -
Trigger Event:
release
-
Statement type: