Health Data: Mixed Types and Disclosure Risk

Synthesizing EHR-like Data

Health records mix continuous lab values, categorical diagnoses and outcomes. This vignette synthesizes such data and audits disclosure risk.

library(DPSynth)
set.seed(3)
n <- 400
ehr <- data.frame(
  age        = as.integer(pmin(100, pmax(18, round(rnorm(n, 60, 15))))),
  labs       = rnorm(n, 5, 1.5),
  diagnosis  = factor(sample(c("I10", "E11", "J44", "N18"), n,
                             replace = TRUE)),
  readmitted = factor(sample(c("no", "<30d", ">30d"), n,
                             prob = c(.6, .15, .25), replace = TRUE))
)

res <- dp_synthesize(ehr, method = "copula", epsilon = 2, n_synth = 400)
#> Warning in regularize.values(x, y, ties, missing(ties), na.rm = na.rm):
#> collapsing to unique 'x' values
head(res$synthetic_data)
#>        age     labs diagnosis readmitted
#> 1 48.94249 4.850001       E11       >30d
#> 2 63.79404 5.220086       N18       >30d
#> 3 62.11651 4.453750       N18         no
#> 4 53.80522 3.208926       I10         no
#> 5 81.07928 5.642745       J44         no
#> 6 81.40499 4.700785       J44         no
print(res)
#> === Differentially Private Synthetic Data ===
#> Method: copula 
#> Privacy: (epsilon = 2 , delta = 1e-06 )
#> Synthetic records: 400 
#> 
#> --- Utility Summary ---
#> Propensity pMSE ratio: 0.0038 
#> Correlation distance: 0.0516 
#> 
#> --- Risk Summary ---
#> Membership risk score: 0.05

# Empirical disclosure risk beyond the theoretical epsilon guarantee
res$risk$membership$risk_score
#> [1] 0.05
res$risk$attribute$mean_disclosure_error
#> [1] NA

Downstream validation – predicting readmission from synthetic training data and testing on real held-out records:

ds <- evaluate_downstream(res$synthetic_data, ehr, target_var = "age")
ds
#> $RMSE_synthetic_training
#> [1] 14.60813
#> 
#> $RMSE_original_training
#> [1] 14.43176
#> 
#> $utility_ratio
#> [1] 1.012221