Removing direct identifiers is not enough. A record stripped of name, SSN, and MRN can still be singled out by a combination of quasi-identifiers — age, ZIP/region, admission date, sex, rare diagnosis. The HIPAA Expert Determination pathway (45 CFR 164.514(b)(1)) requires a qualified person to apply statistical methods and document that the risk of re-identification is "very small." This skill produces that evidence: quasi-identifier risk metrics (k-anonymity, l-diversity) plus OpenMed's empirical re-identification attack, written up as a residual-risk memo.
auditing-deid-leakage (no leaks) and
you must decide whether the dataset is releasable.from openmed.eval.attacks.reid import run_reid_attack, run_reid_benchmark
# Synthetic de-identified records; each row is the released, de-id'd data.
deidentified = [
{"record_id": "r1", "text": "[NAME], 47F, ZIP 021xx, admitted 2024-03."},
{"record_id": "r2", "text": "[NAME], 47F, ZIP 021xx, admitted 2024-03."},
{"record_id": "r3", "text": "[NAME], 88M, ZIP 597xx, admitted 2024-03."}, # singleton
]
# Auxiliary = what an attacker might already hold (e.g. a voter list).
auxiliary = [{"record_id": "v9", "text": "88M ZIP 597xx"}]
result = run_reid_attack(
fixtures=[], # bring your own records below
deidentified_records=deidentified,
auxiliary_records=auxiliary,
)
metric = result.to_metric()
print(metric["aux_linkage_rate"], # empirical linkage success
metric["k_min"], # smallest equivalence-class size
metric["singleton_count"], # k=1 records (uniquely identifiable)
metric["quasi_identifier_count"])
k_min is the population k-anonymity floor across the dataset; a k_min of 1
means at least one record is unique on its quasi-identifiers and is the highest
re-identification risk. aux_linkage_rate is the empirical attack: how often the
adversary's auxiliary data successfully links back to a released record.
To run against the bundled golden suite and emit a leaderboard-style report:
report = run_reid_benchmark(
suite="golden",
deidentified_records=deidentified,
auxiliary_records=auxiliary,
output_markdown="reid_risk.md",
)
auditing-deid-leakage); QIs are what's left to worry about.run_reid_attack returns k_min and
the list of singleton_records (k=1). A common Expert Determination target is
k ≥ a documented threshold (e.g. k ≥ 5 or k ≥ 11) for every record.run_reid_attack / run_reid_benchmark model an
adversary with auxiliary_records and measure actual linkage success
(aux_linkage_rate), residual leakage (leakage_rate), surrogate-consistency
leaks, and date-shift-inversion leaks. Structural metrics bound risk;
the attack demonstrates it.k_min and linkage rate meet your
documented threshold.k_min, l-diversity, the attack's aux_linkage_rate, the assumptions about
attacker capability, and the conclusion that residual risk is "very small."
Cite the metrics — never paste raw records into the memo.auditing-deid-leakage: only score QI risk once direct-identifier
leakage is zero. A leak short-circuits the whole determination.from openmed.eval.attacks.reid import run_reid_attack, run_reid_benchmark, generate_reid_leaderboard. The attack delegates to
openmed.risk.risk_report for k-anonymity / linkage internals.evaluating-with-leakage-gates: register reid_leakage_rate as a gate
in the eval harness so re-identification risk regressions fail CI.pseudonymizing-for-gdpr: pseudonymized output is still re-identifiable
via QIs — run this attack before claiming a dataset is low-risk or anonymized.auxiliary_records you model. Document the assumed attacker
(motivated insider vs. public voter list) — different aux sets, different risk.singleton_count and singleton_records first.date_shift_inversion_rate.
Watch it when de-id used method="shift_dates".