SynthProof

Nearest-neighbour distance ratio (NNDR)

What NNDR measures, how it flags synthetic rows that hug one real record, and how to read it with DCR.

Updated 16 August 2026 · Figures from metrics engine 0.5.1

The nearest-neighbour distance ratio is a per-row privacy check that complements distance to closest record. Where DCR asks how close synthetic rows are to real ones in general, NNDR asks a sharper question about each row: is this synthetic record much closer to one particular real person than to anyone else?

That is the signature of a copy. A synthetic row generated from a learned population sits in a neighbourhood of similar real records — several are about equally near. A synthetic row that is a lightly perturbed real record sits right on top of one real person and noticeably further from the next. NNDR is the number that tells those two situations apart, row by row.

Definition

For each synthetic record, find its nearest and second-nearest neighbours in the real dataset. NNDR is the ratio of the two distances:

NNDR = distance to nearest real record ÷ distance to second-nearest real record

The ratio lies between 0 and 1. A value near 1 means the two nearest real records are about equally far — the synthetic row is "between" real people, not on one of them. A value near 0 means the nearest real record is dramatically closer than the runner-up — the synthetic row is hugging a specific individual.

The metric is summarised across the synthetic dataset by its distribution — typically the 5th percentile and the count or share of rows below a threshold — because the concern is the tail of suspicious rows, not the average.

Distances use the same mixed-type metric as DCR (scaled numerics, categorical mismatch penalties, Gower or similar), so NNDR values are comparable within an engine and not across engines.

Why the ratio, and not just the distance

Raw distance depends on the density of the data. In a dense region of real records, every distance is small; in a sparse region, every distance is large. A small DCR in a dense region is normal; the same value in a sparse region is alarming. NNDR removes that dependency: it compares each row's nearest distance to its own second-nearest, so a row is judged against its local neighbourhood rather than a global scale.

That makes NNDR the better tool for finding individually suspicious rows in mixed-density data — customer tables with a few very common profiles and a long tail of unusual ones, for instance. The unusual profiles are exactly where memorisation both happens most and hides best in aggregate metrics, and NNDR is the metric that surfaces them.

As with DCR, the real data provides the baseline: compute NNDR for real records against the rest of the real data (excluding self) and compare distributions. If synthetic NNDRs skew markedly lower than real-to-real NNDRs, synthetic rows are closer to single individuals than real people are to each other.

What a bad score usually means

Partial memorisation. The most common pattern: the bulk of the synthetic data is fine, but a subset of rows — often generated from rare or outlying training records — are near-copies. Aggregate DCR may look acceptable; NNDR's lower tail lights up. The finding is "N rows with NNDR below threshold, concentrated in [region of the data]," and it should be read as evidence that outliers were memorised.

Rare categorical combinations. Rows containing an unusual combination of categorical values have very few real neighbours to begin with, so the nearest is far closer than the second-nearest by construction. Some low NNDRs on such rows are expected; the baseline comparison against real-to-real NNDR is what separates expected from suspicious.

High-cardinality identifiers left in. As with DCR, near-unique columns make every row an outlier and NNDR uninformative. Remove or generalise them before generation.

The remedy is generator-agnostic and the same as for DCR: regularise or stop earlier, generalise near-unique columns, and, if only a small subset of rows is affected, consider whether they can be dropped from the synthetic output without harming utility.

Reading NNDR alongside the other pillars

NNDR is a distance-based metric: it catches copying and near-copying and it always runs, holdout or not. It does not measure inference or linkability, and a dataset can pass NNDR while failing an attack-based metric that exploits subtler structure. Read it as the fine-grained companion to DCR — the two together tell you whether copying happened and where — and then read the attack-based metrics for the rest of the privacy picture.

It is also useful diagnostically for utility. Rows the generator memorised are usually the outliers, and outliers are what regression targets and minority classes depend on; a bad NNDR tail and a poor TSTR on a rare class often have the same root cause.

Worked example

UCI Adult census, 4,200 rows × 15 columns, SDV Gaussian Copula, metrics engine 0.6.0. Privacy pillar 98.5.

Nearest-neighbour distance ratio 5th pct Median Rows below 0.5
SDV Gaussian Copula 0.7373 0.9777 57 of 4,200 (1.4%)
Real → real (baseline) 0.2437 0.8896 18.6%
Memorising control 0.0000 0.0030 2,490 of 4,200 (59.3%)

The baseline row is why this metric is reported as an excess rather than a raw share. 18.6% of real Adult records are themselves anchored to one other real record — in a census table with repeated demographic profiles, that is ordinary. A tool reporting the bare share would flag every dataset ever measured, which is the same as flagging none.

Against that, the SDV run's 1.4% is 17 points below baseline: its rows sit between neighbours more evenly than real records do. The memorising control's 59.3% is 40.7 points above it, and its 5th percentile is exactly 0.0000 — those rows have a nearest real record at zero distance and a second-nearest somewhere further off, which is the arithmetic signature of a copy.

Note what the median hides. The control's median ratio is 0.003, so on that run the median catches it too — but on a partial memoriser the median would sit in normal territory while the 5th percentile collapsed. The lower tail is the number to read.

This engine does not yet attribute flagged rows to a region of the data; the finding reports the count and the excess over baseline and names the dataset as a whole.

How SynthProof reports it

SynthProof reports NNDR as a distribution with the 5th percentile and the count and share of flagged rows, side by side with the real-to-real baseline computed the same way. It runs in every mode that includes a real sample and is labelled as part of the privacy pillar that does not depend on a holdout. When flagged rows concentrate in a recognisable region — a rare category, an outlying numeric range — the findings describe that region (never the rows themselves) and note the likely link to memorised outliers, with the general remedy.

Distance to closest record is the aggregate view NNDR sharpens. Exact-copy detection is the limiting case (NNDR of exactly 0). Membership inference is what to run, with a holdout, when NNDR looks clean and you need to know about subtler leakage.

Back to the guide: Synthetic data validation: the complete guide.