import json
import dapla as dp
import pandas as pd
from dapla_pseudo import Depseudonymize, Pseudonymize
from dapla_pseudo.constants import MapFailureStrategy
from dapla_pseudo.utils import convert_to_date
from IPython.display import JSONDen strenge tilgangsstyringen til pseudonymiseringsfunksjonaliteten på Dapla gjør at det er vanskelig for brukere å bli kjent med funksjonaliteten ved bruk av produksjonsdata. Derfor bør alle som jobber med dette starte med å bruke testdata og jobbe i test-miljøet på Dapla.
Importere
Først så importerer vi noen biblioteker som vi skal benytte. Du kan benytte standard-kernelen som heter python3, for der er biblioteket som pseudonymiserer, dapla-toolbelt-pseudo, tilgjengelig.
Versjonen av dapla-toolbelt-pseudo er 2.1.2.
Dataene vi skal bruke syntetiske fødselsnummer fra testversjonen SNR-katalogen. På den måten får vi også testet pseudonymiseringen via SNR-katalogen som er veldig vanlig i SSB. Denne SNR-katalogen ligger som en fil i en bøtte som alle i SSB har tilgang til.
path = "gs://ssb-dapla-felles-data-produkt-test/freg/snr_kat.csv"
df = dp.read_pandas(path, file_format="csv", dtype={"fnr": str, "fnr_date": str})
df.head(n=5).style.set_table_styles([
{'selector': 'th', 'props': [('text-align', 'left')]}
])| fnr | current_fnr | snr | current_snr | fnr_date | current_fnr_date | |
|---|---|---|---|---|---|---|
| 0 | 16890249063 | 16890249063 | 026mxd3 | 026mxd3 | 20201222 | 20201222 |
| 1 | 15854996565 | 15854996565 | 34qm7pt | 34qm7pt | 20201222 | 20201222 |
| 2 | 27871547810 | 27871547810 | 53uxelp | 53uxelp | 20201222 | 20201222 |
| 3 | 50889200399 | 50889200399 | f35lbnf | f35lbnf | 20201222 | 20201222 |
| 4 | 22919199052 | 22919199052 | c2hxvdv | c2hxvdv | 20201222 | 20201222 |
Fra Tabell 1 ser vi at datasettet inkluderer en del kolonner. For utforsking av pseudonymiseringsfunksjonalitet så trenger vi kun fnr-kolonnen.
Forberedelse av datasettet
La oss kun beholde fnr-kolonnen og kopiere den en ny kolonne slik at vi enklere kan sammenligne før og etter pseudonymisering. I tillegg kutter jeg antall rader til 10, siden vi ikke trenger noe mer for formålet her.
df2 = df.head(n=10)
df3 = df2[["fnr"]]
df4 = df3.copy()
df4['fnr_original'] = df4['fnr']
df4.head(n=5).style.set_table_styles([
{'selector': 'th', 'props': [('text-align', 'left')]}
])| fnr | fnr_original | |
|---|---|---|
| 0 | 16890249063 | 16890249063 |
| 1 | 15854996565 | 15854996565 |
| 2 | 27871547810 | 27871547810 |
| 3 | 50889200399 | 50889200399 |
| 4 | 22919199052 | 22919199052 |
Hvis du ønsker å teste hvordan krypteringsalgoritmene fungerer med kolonner som inneholder navn, så kan vi generere noe data med et også.
fornavn = [
"Jo",
"Hans-August",
"Nils",
"Eva",
"Lars",
"Øyvind",
"Kenneth",
"Johnny",
"Rupinder",
"Nicolas",
]
etternavn = [
"Nordman",
"Karlsen",
"Nordmann",
"Karlsen",
"Carlsen",
"Nordmann",
"Carlsen",
"Nordmann",
"Karlsen",
"Normann",
]
df4['fornavn'] = fornavn
df4['etternavn'] = etternavn
df5 = df4.copy()Til slutt legger vi på noen ugyldige fødselsnummer slik at vi får testet hvordan algoritmene håndterer dette.
new_row1 = pd.DataFrame(
[
{
"fnr": "99999999999",
"fnr_original": "99999999999",
"fornavn": "Michael",
"etternavn": "Norman",
}
]
)
df6 = pd.concat([df5, new_row1], ignore_index=True)
new_row2 = pd.DataFrame(
[{"fnr": "XX", "fnr_original": "XX", "fornavn": "Ola Glenn", "etternavn": "Gåås"}]
)
df7 = pd.concat([df6, new_row2], ignore_index=True)
new_row3 = pd.DataFrame(
[
{
"fnr": "X8b7k28",
"fnr_original": "X8b7k28",
"fornavn": "Lars",
"etternavn": "Gaas",
}
]
)
df8 = pd.concat([df7, new_row3], ignore_index=True)
df8.head(n=5).style.set_table_styles([
{'selector': 'th', 'props': [('text-align', 'left')]}
])| fnr | fnr_original | fornavn | etternavn | |
|---|---|---|---|---|
| 0 | 16890249063 | 16890249063 | Jo | Nordman |
| 1 | 15854996565 | 15854996565 | Hans-August | Karlsen |
| 2 | 27871547810 | 27871547810 | Nils | Nordmann |
| 3 | 50889200399 | 50889200399 | Eva | Karlsen |
| 4 | 22919199052 | 22919199052 | Lars | Carlsen |
Tabell 3 viser datasettet vi skal bruke til å teste med.
Pseudonymisering
Nå kan vi begynne å leke med dataene. Det første vi kan gjøre er å pseudonymisere med den mest vanlige algoritmen som benyttes i produksjon: Papis-nøkkelen.
result = (
Pseudonymize.from_pandas(df8)
.on_fields("fnr")
.with_stable_id()
.run()
)
result.to_pandas()Unexpected length of metadata: 2
| fnr | fnr_original | fornavn | etternavn | |
|---|---|---|---|---|
| 0 | BnQe23u | 16890249063 | Jo | Nordman |
| 1 | I1mQmBP | 15854996565 | Hans-August | Karlsen |
| 2 | eVbGYLy | 27871547810 | Nils | Nordmann |
| 3 | O4jegSM | 50889200399 | Eva | Karlsen |
| 4 | 6JhhRKi | 22919199052 | Lars | Carlsen |
| 5 | vygqpLq | 66821775168 | Øyvind | Nordmann |
| 6 | JWzitL8 | 13824498614 | Kenneth | Carlsen |
| 7 | oIhVngD | 46927100797 | Johnny | Nordmann |
| 8 | 0y8qqUZ | 16907699157 | Rupinder | Karlsen |
| 9 | FNnp5AL | 10920998203 | Nicolas | Normann |
| 10 | 4yI2BlkviaI | 99999999999 | Michael | Norman |
| 11 | XX | XX | Ola Glenn | Gåås |
| 12 | tKHXmUl | X8b7k28 | Lars | Gaas |
I Tabell 4 ser vi at kolonnen fnr har blitt pseudonymisert. Det er også verdt å legge merke til at kolonnen ikke endrer navn. Grunnen til at lengden på verdiene som er pseudonymiserte er på 7 tegn for de opprinnelige fødselsnummerne, er at det først skjer en oversetting fra fnr til snr før det pseudonymiseres, og snr-nummerserien er på 7 tegn. Med andre ord så preserverer algoritmen lengden på snr-nummeret siden det er dette som pseudonymiseres.
Det er også verdt å merke seg at verdier som er kortere enn 4 i lengde, f.eks. XX i rad 11, ikke blir pseudonymisert i det hele tatt. Verdier som er 4 eller lengre, vil bli pseudonymisert selv om de ikke fikk treff i SNR-katalogen.
Metadata
Det genereres også 2 metadata-objekter ved pseudonymisering. Disse er:
- result.datadoc
- result.metadata
La oss se nærmere på de:
data = json.loads(result.datadoc)
display(data){'document_version': '0.0.1',
'pseudonymization': {'document_version': '0.1.0',
'pseudo_variables': [{'short_name': 'fnr',
'data_element_path': 'fnr',
'data_element_pattern': '/fnr',
'stable_identifier_type': 'FREG_SNR',
'stable_identifier_version': '2023-08-31',
'encryption_algorithm': 'TINK-FPE',
'encryption_key_reference': 'papis-common-key-1',
'encryption_algorithm_parameters': [{'keyId': 'papis-common-key-1'},
{'strategy': 'skip'}]}]}}
Dette er metadata som skal integreres i Datadoc etter hvert.
La oss se på den andre typen metadata:
display(result.metadata){'logs': ['No SID-mapping found for fnr 999********',
'No SID-mapping found for fnr X8b****'],
'metrics': {'MAPPED_SID': 10, 'FPE_LIMITATION': 1, 'MISSING_SID': 2}}
Her ser vo at 10 felt fikk treff i SNR-katalogen, 1 felt var for kort for algoritmen, og 2 felt fikk ikke treff SNR-katalogen. Vi får også se litt fødselsnummeret til de 2 som ikke fikk treff.
Depseudonymisering
La oss ta vare på den pseudonymiserte kolonnen og så depseudonymisere og se om resultatet blir riktig:
result2 = result.to_pandas()
result2['pseudo_fnr'] = result2['fnr']
result_df = (
Depseudonymize.from_pandas(result2)
.on_fields("fnr")
.with_stable_id()
.run()
.to_pandas()
)
result_df.style.set_table_styles([
{'selector': 'th', 'props': [('text-align', 'left')]}
])| fnr | fnr_original | fornavn | etternavn | pseudo_fnr | |
|---|---|---|---|---|---|
| 0 | 16890249063 | 16890249063 | Jo | Nordman | BnQe23u |
| 1 | 15854996565 | 15854996565 | Hans-August | Karlsen | I1mQmBP |
| 2 | 27871547810 | 27871547810 | Nils | Nordmann | eVbGYLy |
| 3 | 50889200399 | 50889200399 | Eva | Karlsen | O4jegSM |
| 4 | 22919199052 | 22919199052 | Lars | Carlsen | 6JhhRKi |
| 5 | 66821775168 | 66821775168 | Øyvind | Nordmann | vygqpLq |
| 6 | 13824498614 | 13824498614 | Kenneth | Carlsen | JWzitL8 |
| 7 | 46927100797 | 46927100797 | Johnny | Nordmann | oIhVngD |
| 8 | 16907699157 | 16907699157 | Rupinder | Karlsen | 0y8qqUZ |
| 9 | 10920998203 | 10920998203 | Nicolas | Normann | FNnp5AL |
| 10 | 99999999999 | 99999999999 | Michael | Norman | 4yI2BlkviaI |
| 11 | XX | XX | Ola Glenn | Gåås | XX |
| 12 | X8b7k28 | X8b7k28 | Lars | Gaas | tKHXmUl |
Tabell 5 viser at depseudonymiseringen returnerer de opprinnelige fødselsnummerne.
Videre kan man utforske å pseudonymisere navn ved bruk av ulike algoritmer.