A clinic deletes every name and publishes its records. You, armed with nothing but a public voter roll, will unmask its patients in three clicks. Then you switch sides and defend the release with k-anonymity, and discover that every unit of privacy is paid for in answers.
Removing names is not anonymization
The clinic in the fictional town of Midvale did what most organizations still do: it deleted the name column and called the data anonymous. But ZIP code, age, and gender survived, and those three fields are quasi-identifiers: harmless alone, a fingerprint together. In 2000, Latanya Sweeney estimated that 87 percent of one country's population could be uniquely identified by ZIP code, birth date, and sex alone (Sweeney, 2000). That is a cited historical result, not something this page recomputes. What this page does recompute, on every click, is the same attack in miniature.
How this playground stays honest: both tables below are synthetic, with fictional people, and both are fully visible on this page. Every match count, k value, group size, and utility percentage is computed live in your browser from those visible rows. Nothing is faked.
You are the attacker. On the left, Midvale's public voter roll: names attached to ZIP, age, and gender, as many real voter files are. On the right, the clinic's "anonymized" release. Pick a neighbor and join the tables on the three shared columns. Whenever exactly one record matches, that person's diagnosis is yours. Re-identify 3 people.
Midvale voter roll (public)
Anyone can look these 12 neighbors up. Pick a person to run the join on ZIP + age + gender.
| Name | ZIP | Age | Gender | Attack |
|---|---|---|---|---|
| Nora Fielding | 54801 | 23 | F | |
| Priya Raman | 54802 | 34 | F | |
| Marcus Webb | 54801 | 33 | M | |
| Elaine Soto | 54801 | 42 | F | |
| Tomas Rivera | 54802 | 48 | M | |
| Dana Whitfield | 55120 | 22 | F | |
| Omar Haddad | 55120 | 30 | M | |
| Grete Lund | 55121 | 35 | F | |
| Astrid Berge | 54803 | 38 | F | |
| Victor Chen | 55121 | 43 | M | |
| Ruth Ambrose | 54803 | 61 | F | |
| Felix Njoku | 55120 | 52 | M |
Midvale Clinic release ("anonymized")
32 discharge records. Names were deleted before publication; ZIP, age, and gender were kept for research value.
| Name | ZIP | Age | Gender | Diagnosis |
|---|---|---|---|---|
| removed | 54801 | 23 | F | Asthma |
| removed | 54801 | 26 | F | Anxiety disorder |
| removed | 54802 | 29 | F | Migraine |
| removed | 54802 | 21 | M | Asthma |
| removed | 54803 | 27 | M | Back pain |
| removed | 54801 | 31 | F | Migraine |
| removed | 54802 | 34 | F | Type 2 diabetes |
| removed | 54803 | 38 | F | Anxiety disorder |
| removed | 54801 | 33 | M | Hypertension |
| removed | 54802 | 36 | M | Back pain |
| removed | 54803 | 39 | M | Asthma |
| removed | 54801 | 42 | F | Hypertension |
| removed | 54803 | 47 | F | Type 2 diabetes |
| removed | 54801 | 44 | M | Type 2 diabetes |
| removed | 54802 | 45 | M | Hypertension |
| removed | 54802 | 48 | M | Migraine |
| removed | 55120 | 22 | F | Back pain |
| removed | 55121 | 28 | F | Asthma |
| removed | 55120 | 24 | M | Anxiety disorder |
| removed | 55120 | 25 | M | Migraine |
| removed | 55121 | 29 | M | Hypertension |
| removed | 55120 | 32 | F | Asthma |
| removed | 55121 | 35 | F | Hypertension |
| removed | 55121 | 37 | F | Back pain |
| removed | 55120 | 30 | M | Type 2 diabetes |
| removed | 55120 | 35 | M | Anxiety disorder |
| removed | 55121 | 38 | M | Migraine |
| removed | 55120 | 41 | F | Migraine |
| removed | 55120 | 46 | F | Anxiety disorder |
| removed | 55121 | 43 | F | Asthma |
| removed | 55121 | 43 | M | Back pain |
| removed | 55121 | 43 | M | Hypertension |
Join result
Pick a neighbor on the left. The join keeps every released record whose ZIP, age, and gender all equal theirs.
Your re-identifications
0
of 9 neighbors who link to exactly one record
The defense has a name: k-anonymity
The attack worked because some rows are unique on (ZIP, age, gender). A release is k-anonymous when every row is indistinguishable from at least k - 1 others on those quasi-identifiers, so the best any linkage attack can do is point at a crowd of k people. You get there by generalizing (54801 becomes district 548**, age 34 becomes 30 to 39) or by suppressing a column outright. The catch: every step that blurs the attacker's view blurs the honest analyst's view too.
Now you are the data steward, republishing the same 32 records. Coarsen each column until the smallest group of identical rows holds at least 5 people, and watch what each step does to the attack and to the three queries an analyst still needs to answer. There is more than one way to reach k = 5; they do not cost the same utility.
Choose how much detail the release keeps
ZIP code
Age
Gender
The release, as the world now sees it
Same 32 records, published at your chosen detail. An asterisk means the column was removed.
| ZIP | Age | Gender | Diagnosis |
|---|---|---|---|
| 54801 | 23 | F | Asthma |
| 54801 | 26 | F | Anxiety disorder |
| 54802 | 29 | F | Migraine |
| 54802 | 21 | M | Asthma |
| 54803 | 27 | M | Back pain |
| 54801 | 31 | F | Migraine |
| 54802 | 34 | F | Type 2 diabetes |
| 54803 | 38 | F | Anxiety disorder |
| 54801 | 33 | M | Hypertension |
| 54802 | 36 | M | Back pain |
| 54803 | 39 | M | Asthma |
| 54801 | 42 | F | Hypertension |
| 54803 | 47 | F | Type 2 diabetes |
| 54801 | 44 | M | Type 2 diabetes |
| 54802 | 45 | M | Hypertension |
| 54802 | 48 | M | Migraine |
| 55120 | 22 | F | Back pain |
| 55121 | 28 | F | Asthma |
| 55120 | 24 | M | Anxiety disorder |
| 55120 | 25 | M | Migraine |
| 55121 | 29 | M | Hypertension |
| 55120 | 32 | F | Asthma |
| 55121 | 35 | F | Hypertension |
| 55121 | 37 | F | Back pain |
| 55120 | 30 | M | Type 2 diabetes |
| 55120 | 35 | M | Anxiety disorder |
| 55121 | 38 | M | Migraine |
| 55120 | 41 | F | Migraine |
| 55120 | 46 | F | Anxiety disorder |
| 55121 | 43 | F | Asthma |
| 55121 | 43 | M | Back pain |
| 55121 | 43 | M | Hypertension |
k-anonymity of this release
k = 1
target: k ≥ 5
Every published row is identical to at least 0 other rows on (ZIP, age, gender). The release splits into 31 groups; the smallest is (54801, 23, F) with 1 person.
One bar per group of identical rows. Orange bars are groups smaller than 5.
The attack, re-run against this release
9 unique matches
voter-roll neighbors who still link to exactly one record
Utility left for analysts
100%
Each line is the mean absolute error of that query answered from the release instead of the raw table. Removed columns fall back to the midpoint of the release's age range, an even split across ZIPs, or a 50 percent gender assumption. 100% means every query still comes out exact.
Try at least 3 different settings and compare where they land. Full detail sits at k = 1 with perfect utility; total suppression reaches k = 32 and answers nothing. Real releases live on the frontier between them, and choosing the point on that frontier is an ethical decision, not a technical one: it decides whose privacy is protected and which questions can still be answered.
The tradeoff curve you are tracing
Every setting you try lands here: privacy (k) to the right, analyst utility upward. The empty top-right corner is the point of this guide.