- Je onderscheidt gegevensminimalisatie van herkenbaarheid via gecombineerde kenmerken.
- Je bouwt zelfstandige projecties en equivalentieklassen en berekent een rijgewogen grootteverslag zonder anonimiteitsclaim.
Een team wil een AI-router verbeteren met helpdeskmeldingen. De namen verdwijnen uit een export, maar combinaties van zone, leeftijdscategorie en dienst blijven staan. Hoeveel rijen kun je daarmee nog van elkaar onderscheiden?
Denk aan een bak kaartjes. Met alleen een zone zijn veel kaartjes hetzelfde; met telkens een extra kenmerk kunnen de stapeltjes kleiner worden. De vraag welke velden je nodig hebt, komt daarom vóór de vraag hoe groot zo’n stapeltje is.
Je gebruikt uitsluitend zes verzonnen rijen. De oefening meet herkenbaarheid tegenover gekozen kenmerken en een gekozen minimumgrootte. Dat is geen certificaat dat willekeurige externe informatie niemand kan herkennen.
1. Begin met de vraag die je wilt beantwoorden
Voor de vraag “hoeveel meldingen komen uit elke zone?” heb je leeftijd en dienst niet nodig. Gegevensminimalisatie (data minimization) begint met dat doel. Een veld weglaten voorkomt dat deze export de weggelaten waarde rechtstreeks meestuurt.
| Rij | Zone | Leeftijd | Dienst | |---|---|---|---| | 1 | noord | 20-29 | vroeg | | 2 | noord | 20-29 | laat | | 3 | noord | 30-39 | vroeg | | 4 | zuid | 20-29 | vroeg | | 5 | zuid | 20-29 | laat | | 6 | zuid | 30-39 | vroeg |
Een projectie selecteert de gevraagde kolommen. Je behoudt de rijvolgorde, maar maakt nieuwe dictionaries zodat een latere wijziging de oorspronkelijke tabel niet beschadigt. Hier kies je bewust alleen de zone.
data = [
('noord','20-29','vroeg'),
('noord','20-29','laat'),
('noord','30-39','vroeg'),
('zuid','20-29','vroeg'),
('zuid','20-29','laat'),
('zuid','30-39','vroeg')]
fields = ['zone','age','shift']
rows = [dict(zip(fields,values))
for values in data]
minimal = project_fields(rows,['zone'])
print(minimal[0])
print('age' in minimal[0])
# {'zone': 'noord'}
# False
De rijlabels in de tabel helpen ons tellen; ze zijn geen namen. Ook een projectie die een direct identificerend veld weglaat, kan nog indirect herkenbare kenmerken (quasi-identifiers) bevatten. Wat elders over iemand bekend is, staat niet in deze tabel en wordt hier dus niet volledig gemodelleerd.
012. Onderzoek welke rijen gelijk blijven
Kies eerst welke kenmerken een waarnemer kent. Rijen met op die velden dezelfde waarden vormen een equivalentieklasse. Verander de selectie in het beeld en kijk hoe twee stapels van drie uit elkaar vallen.
Kies welke kenmerken een waarnemer kent. Bekijk de overblijvende velden, de groepen met dezelfde combinatie en hoeveel rijen onder jouw minimum vallen.
| Rij | Zone | Leeftijd |
|---|---|---|
| 1 | noord | 20-29 |
| 2 | noord | 20-29 |
| 3 | noord | 30-39 |
| 4 | zuid | 20-29 |
| 5 | zuid | 20-29 |
| 6 | zuid | 30-39 |
- K1 noord · 20-29Rij 1, 2
- K2 noord · 30-39Rij 3
- K3 zuid · 20-29Rij 4, 5
- K4 zuid · 30-39Rij 6
- Rijen
- 6
- Klassen
- 4
- Rijen onder minimum
- 2
- Fractie rijen onder minimum
- 33,333%
2 van 6 rijen zitten in een klasse kleiner dan 2. Het gekozen minimum is niet overal gehaald. Een klasse van precies 2 telt als groot genoeg.
Alle zes rijen zijn verzonnen. Een gehaald gekozen minimum zegt alleen iets over deze tabel en deze kenmerken; het bewijst geen anonimiteit tegenover externe informatie of juridische naleving.
Noem de klasse rond rij i E_Q(i), waarbij Q de gekozen velden aanduidt. De grootte k_i telt hoeveel rijen inclusief i dezelfde combinatie hebben. De verticale strepen betekenen hier “aantal elementen”.
Alleen zone geeft de klassen noord en zuid, elk met drie rijen. Bij zone plus leeftijd ontstaan vier klassen: de twee noordelijke twintigers, de noordelijke dertiger en dezelfde verdeling in zuid. Hun groottes zijn 2, 1, 2 en 1.
classes = equivalence_classes(
rows,['zone','age'])
sizes = [c['count'] for c in classes]
print(sizes)
print([c['indices'] for c in classes])
# [2, 1, 2, 1]
# [[0, 1], [2], [3, 4], [5]]
De indices beginnen in Python bij nul; de zichtbare rijlabels beginnen bij één. Voeg ook dienst toe en iedere combinatie wordt uniek in deze zes rijen. Meer kenmerken kunnen een naamloze tabel dus alsnog heel onderscheidend maken.
fine = equivalence_classes(rows,fields)
print([c['count'] for c in fine])
# [1, 1, 1, 1, 1, 1]
De oefening bewaart ook het exacte Python-type: integer 0 en string “0” zijn verschillende categorieën. Harmoniseer zulke codes alleen wanneer je weet dat ze dezelfde betekenis hebben. Dat opschoonbesluit volgt niet automatisch uit een privacymaat.
023. Tel rijen onder een gekozen minimum
Kies nu een minimumklassegrootte van twee. Een klasse van precies twee haalt die grens. Bij groottes 2, 1, 2 en 1 zitten twee rijen in een te kleine klasse.
Noem het totale aantal rijen N, het aantal rijen in te kleine klassen B en hun fractie r. Je deelt door rijen, omdat iedere rij een bijdrage voorstelt. Twee kleine klassen van vier klassen betekent daarom niet dat de helft van de rijen te klein gegroepeerd is.
report = class_size_report(sizes,2)
print(report['below_minimum_rows'])
print(round(
report['below_minimum_fraction'],3))
print(report['meets_chosen_minimum'])
# 2
# 0.333
# False
Een grotere klasse voorkomt bovendien niet dat alle leden dezelfde gevoelige waarde kunnen hebben. De gekozen kenmerken, mogelijke koppelingen en verdere gegevensuitgiftes blijven van belang. Presenteer een gehaald minimum daarom steeds met zijn beperkte betekenis.
03Wat je nu kunt
- Je selecteert velden vanuit een expliciete vraag en bewaart zelfstandige rijcontainers.
- Je onderzoekt herkenbaarheid tegenover gekozen kenmerken met equivalentieklassen.
- Je rapporteert een rijgewogen fractie onder een inclusief gekozen minimum, met duidelijke grenzen aan de conclusie.
Hierna onderzoek je een ander idee: de invloed van één begrensde bijdrage op een statistiek, en de wiskundige rol van ruis in een ideaal Laplace-mechanisme.