Ce code a été préparé dans le cadre d’une présentation sur l’anonymisation des données de santé, destinée au Congrès de l’ACFAS. Il illustre un processus complet de traitement statistique visant à réduire les risques de réidentification tout en préservant l’utilité analytique des données. À travers l’utilisation du package sdcMicro
, différentes techniques d’anonymisation sont appliquées de manière progressive, accompagnées d’analyses comparatives pour évaluer l’impact des transformations sur la qualité de l’information.
1. Génération de données simulées
Un jeu de données fictif est créé pour représenter un registre de santé contenant des informations sensibles (statut VIH), des variables quasi-identifiantes (âge, genre, code postal) et des variables numériques sensibles (revenu, score de santé).
2. Création de l’objet d’anonymisation (sdcMicro
)
L’objet sdc
est initialisé avec les variables à protéger. Cela permet de calculer les risques de réidentification et de manipuler les données tout en suivant leur niveau de confidentialité.
3. Réduction du risque par étapes successives
Plusieurs techniques d’anonymisation sont appliquées pour réduire le risque de réidentification :
- Regroupement de modalités rares dans
gender
pour éviter l’unicité. - Regroupement par classes d’âge pour réduire la granularité.
- Top/Bottom coding sur le score de santé pour limiter les valeurs extrêmes.
- Suppression locale pour garantir un
k-anonymat
. - Microagrégation pour brouiller les variables numériques.
- Ajout de bruit pour perturber davantage les valeurs sensibles.
À chaque étape, le risque est recalculé.
4. Extraction des données anonymisées
Les données transformées sont extraites à deux étapes :
- après la première anonymisation légère,
- après l’ensemble des techniques appliquées.
5. Analyse d’utilité
Des comparaisons visuelles entre les données réelles et les données anonymisées sont réalisées pour évaluer l’impact des transformations :
- Répartition par genre et code postal selon le statut VIH,
- Comparaisons des moyennes (âge, revenu, score santé) entre les deux jeux de données.
Chargement des bibliothèques et génération des données
library(sdcMicro)
library(ggplot2)
library(dplyr)
library(CGPfunctions)
Ces lignes chargent les bibliothèques nécessaires :
sdcMicro
pour l’anonymisation des données,ggplot2
pour la visualisation,dplyr
pour la manipulation de données,CGPfunctions
pour des fonctions graphiques complémentaires.
set.seed(123)
n <- 10000
Initialisation du générateur aléatoire pour reproductibilité. Création de n
, la taille de l’échantillon (10 000 individus).
health_data <- data.frame(
id = 1:n,
age = sample(18:90, n, replace = TRUE),
gender = sample(c("M","F","NB","QR","FLD"), n, replace = TRUE,prob=c(0.49, 0.49, 0.01, 0.005, 0.005)),
postal_code = sample(paste0("PC-",100:130), n, replace = TRUE),
income = rlnorm(n, meanlog = 10, sdlog = 0.5),
health_score = rnorm(n, mean = 50, sd = 10),
hiv_status = rbinom(n, 1, 0.03)
)
Création du jeu de données health_data
avec :
- un identifiant unique (
id
), - l’âge entre 18 et 90 ans,
- le genre, avec cinq modalités et des probabilités spécifiées,
- un code postal fictif,
- un revenu suivant une loi log-normale,
- un score de santé normalement distribué,
- un statut VIH binomial (probabilité de 3 % d’être positif).
health_data$hiv_status=as.factor(health_data$hiv_status)
Conversion de la variable hiv_status
en facteur pour les traitements ultérieurs.
Initialisation de l’objet sdcMicro
sdc <- createSdcObj(
dat = health_data,
keyVars = c("age","gender", "postal_code"),
numVars = c("income", "health_score"),
weightVar = NULL,
hhId = NULL,
sensibleVar = "hiv_status"
)
Création d’un objet sdcMicro
pour l’anonymisation, en précisant :
- les quasi-identifiants (
keyVars
) : variables pouvant potentiellement réidentifier les individus, - les variables numériques sensibles (
numVars
), - la variable confidentielle (
sensibleVar
) : ici, le statut VIH.
measure_risk(sdc)
print(sdc, "risk")
Évaluation initiale du risque de réidentification et affichage des résultats.
Regroupement des catégories rares du genre
sdc <- groupAndRename(sdc, var="gender", before=c("NB","QR","FLD"), after=c("Other"))
Regroupement des catégories peu fréquentes dans la variable gender
sous une seule modalité "Other"
.
Extraction des données manipulées
data_anon_01=extractManipData(sdc)
Extraction du jeu de données après regroupement des genres.
crosstable(health_data, gender, by=hiv_status) %>% as_flextable(keep_id=F)
crosstable(data_anon_01, gender, by=hiv_status) %>% as_flextable(keep_id=F)
Création de tableaux croisés pour comparer la distribution de gender
selon le hiv_status
dans les données originales et anonymisées.
Regroupement global par tranches d’âge
sdc <- globalRecode(sdc, column = 'age', breaks = 10 * c(1:9))
Discrétisation de l’âge en intervalles de 10 ans.
Encodage haut/bas (top/bottom coding)
sdc <- topBotCoding(obj = sdc, value = 70, replacement = 70, kind = 'top', column = 'health_score')
sdc <- topBotCoding(obj = sdc, value = 30, replacement = 30, kind = 'bottom', column = 'health_score')
Limitation des valeurs extrêmes de health_score
à des seuils définis (respectivement 70 pour les hauts, 30 pour les bas).
data_anon_02=extractManipData(sdc)
Nouvelle extraction des données après encodage top/bottom.
Suppression locale
sdc <- localSuppression(sdc,k = 5)
Suppression ciblée de certaines valeurs pour garantir qu’au moins 5 enregistrements partagent les mêmes quasi-identifiants (k-anonymat
).
Microagrégation
sdc <- microaggregation(sdc, method = "mdav", variables=c("income","health_score"), aggr = 2)
Application de la microagrégation (méthode MDAV) sur les variables income
et health_score
pour brouiller les valeurs tout en conservant leur distribution générale.
Ajout de bruit
sdc <- addNoise(sdc, noise = 0.9)
Ajout d’un bruit aléatoire aux variables numériques pour renforcer l’anonymisation.
Comparaison visuelle (analyse d’utilité)
Genre vs VIH
pl1=PlotXTabs2(data_anon_02, hiv_status, gender, plottype = "percent")
pl2=PlotXTabs2(health_data, hiv_status, gender, plottype = "percent")
grid.arrange(pl1, pl2, ncol=2)
Visualisation des répartitions conditionnelles du genre selon le statut VIH, avant et après anonymisation.
Code postal vs VIH
pl3=PlotXTabs2(data_anon_02, postal_code, hiv_status, plottype = "percent", x.axis.orientation="vertical")
pl4=PlotXTabs2(health_data, postal_code, hiv_status, plottype = "percent", x.axis.orientation="vertical")
grid.arrange(pl3, pl4, ncol=1)
Analyse de la distribution du VIH par code postal, avec comparaison avant/après anonymisation.
Comparaison des moyennes d’âge
p1=data_anon_02 %>% group_by(hiv_status) %>%
summarise(mean_age=mean(age, na.rm = T)*10, .groups = 'drop') %>% ggplot( aes(x = hiv_status, y = mean_age, fill = hiv_status)) + geom_bar(stat = "identity", position = "dodge") + coord_cartesian(ylim = c(40,50)) + ggtitle("anonymized_data") + theme_solarized() +
scale_colour_solarized()
p2=health_data %>% group_by(hiv_status) %>% summarise(mean_age=mean(age, na.rm = T), .groups = 'drop') %>% ggplot( aes(x = hiv_status, y = mean_age, fill = hiv_status)) + geom_bar(stat = "identity", position = "dodge") + coord_cartesian(ylim = c(40,55)) + ggtitle("real_data") + theme_solarized() +
scale_colour_solarized()
grid.arrange(p1, p2, ncol=2)
Graphiques à barres montrant la moyenne d’âge par statut VIH dans les jeux de données anonymisé et original.
Comparaison des revenus moyens
p3=data_anon_02 %>% group_by(hiv_status) %>%
summarise(mean_income=mean(income, na.rm = T), .groups = 'drop') %>% ggplot( aes(x = hiv_status, y = mean_income, fill = hiv_status)) + geom_bar(stat = "identity", position = "dodge") + coord_cartesian(ylim = c(20000,27000)) + ggtitle("anonymized_data") + theme_solarized() +
scale_colour_solarized()
p4=health_data %>% group_by(hiv_status) %>%
summarise(mean_income=mean(income, na.rm = T), .groups = 'drop') %>% ggplot( aes(x = hiv_status, y = mean_income, fill = hiv_status)) + geom_bar(stat = "identity", position = "dodge") + coord_cartesian(ylim = c(20000,25000)) + ggtitle("real_data") + theme_solarized() +
scale_colour_solarized()
grid.arrange(p3, p4, ncol=2)
Comparaison graphique des revenus moyens par statut VIH avant et après anonymisation.
Comparaison des scores de santé
p5=data_anon_02 %>% group_by(hiv_status) %>%
summarise(mean_health_score=mean(health_score, na.rm = T), .groups = 'drop') %>% ggplot( aes(x = hiv_status, y = mean_health_score, fill = hiv_status)) + geom_bar(stat = "identity", position = "dodge") + coord_cartesian(ylim = c(40,52)) + ggtitle("anonymized_data") + theme_solarized() +
scale_colour_solarized()
p6=health_data %>% group_by(hiv_status) %>%
summarise(mean_health_score=mean(health_score, na.rm = T), .groups = 'drop') %>% ggplot( aes(x = hiv_status, y = mean_health_score, fill = hiv_status)) + geom_bar(stat = "identity", position = "dodge") + coord_cartesian(ylim = c(40,52)) + ggtitle("real_data") + theme_solarized() +
scale_colour_solarized()
grid.arrange(p5, p6, ncol=2)
Visualisation des scores de santé moyens par statut VIH pour évaluer la préservation de l’utilité des données.