PORTRAIT describes an individual against a reference population on nameable, clinician-readable axes — with calibrated uncertainty, and an explicit refusal to describe when the data do not support a description beyond what covariance alone explains.
It is a research characterisation aid. Every statement it makes is a population-position statement carrying a stated uncertainty.
Given a reference cohort (a table of features for a population) and one individual:
- Describability gate. A calibrated two-sample test decides whether the reference carries structure beyond its own covariance. If it does not, PORTRAIT abstains — it will not manufacture a rich description from noise. This is the core guarantee.
- Position. Each feature is placed as a population centile with a distribution-free (DKW) confidence band, so the position carries a guaranteed-coverage interval.
- Atypicality. A conformal, multiplicity-controlled flag for individuals who sit in a sparsely populated region of feature space.
- Profile coherence. A per-feature decomposition of how internally consistent an individual's profile is, naming which features fail to cohere — information a single distance number cannot give.
- Conditional surprise. Which individual features are surprising given the others.
- Passport render. A self-contained, accessible HTML page (WCAG 2.2: state is always carried by a text label and glyph, never colour alone) showing the above for one person, in one of three honest states: DESCRIBE, ABSTAIN, or REFUSE.
Requires Python 3.9+ (reference results were produced on Python 3.13; 3.9 imports and runs).
pip install -e .
This installs the analysis package and its dependencies. (For the exact pinned versions used
to produce the reference results, use pip install -r requirements.txt instead.)
portrait-demo # installed console script
# or, equivalently:
python -m analysis.run
This runs the method on synthetic generators and writes portrait_demo_passport.html:
it shows the describability gate DESCRIBING structured data and declining a covariance-matched
null, then renders one Passport.
The method modules are plain NumPy/scikit-learn and take arrays directly:
import numpy as np
from analysis.structure_test.gate import describability_gate
from analysis.passport.render import build_passport, render_passport
reference = ... # (n_people, n_features) reference-population array
individual = ... # (n_features,) the person to describe
names = [...] # feature names
gate = describability_gate(reference) # DESCRIBE / BORDERLINE / REFUSE
ps = build_passport(reference, individual, names, seed=0)
open("passport.html", "w").write(render_passport(ps))The worked clinical example uses the public NHANES adult cardiometabolic panel. The loader in
analysis/nhanes/cohort.py reads the public NHANES .xpt files from $NHANES_DIR
(default ./data/NHANES); download them from CDC NHANES and point NHANES_DIR at that folder.
No patient data ships in this repository — it is referenced by path only.
analysis/— the method. Independent components (structure_test,axes,position,uncertainty,atypicality,coherence,surprise,decision,typicality), thepassportrender/orchestration layer, theappHTML builders, thenhanesdata loader, and a synthetic-dataoracleso the method can be exercised with nothing to download.results/app/— a prebuilt interactive Passport explorer (self-contained HTML).README,LICENSE,requirements.txt,reproduce.sh.
- Every statement is a within-cohort population position, each carrying a stated uncertainty.
- The describability guarantee is a calibrated statistical test; like any test it has power limits at small sample sizes, and it abstains rather than overclaims when data are thin.
- Profile coherence is descriptive: its value is the per-feature attribution and that it names different individuals than raw extremity. On a labelled discrimination task a plain covariance distance matches it, so coherence is reported for attribution.
- The interactive app runs in a browser; the numeric method depends only on the standard scientific-Python stack.
MIT — see LICENSE.