--- title: "Evaluating and visualising synthetic data" output: rmarkdown::html_vignette: toc: true vignette: > %\VignetteIndexEntry{Evaluating and visualising synthetic data} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 4.2, dpi = 72, out.width = "100%") ``` Synthetic data are only useful if they are *faithful* to the real data, *useful* for the analyses people will run, and *safe* to release. No single number captures all three, so SynthesizerPlus provides a set of complementary measures and plots. ```{r} library(SynthesizerPlus) real <- airquality syn <- synthesize(real, by = "Month", seed = 1) ``` ## Everything at once: `compare_synthetic()` ```{r} cmp <- compare_synthetic(real, syn, seed = 1) cmp summary(cmp) ``` The `ideal` column of `summary()` gives the value expected for a perfect synthesizer that still produces new records. The components are described below. ## Marginal fidelity `marginal_metrics()` reports, for each variable, the Kolmogorov–Smirnov distance (numeric) or the total variation distance (categorical), the Wasserstein-1 distance in units of the real standard deviation, and moments and missing-value rates. ```{r} marginal_metrics(real, syn)[, c("variable", "distance", "wasserstein", "mean_real", "mean_synthetic", "missing_real", "missing_synthetic")] ``` ```{r, fig.height = 5} plot_marginals(real, syn) plot_qq(real, syn) ``` For categorical variables, `plot_marginals()` (or `plot_categorical()`) shows the category shares side by side: ```{r} syn_iris <- synthesize(iris, by = "Species", seed = 1) plot_categorical(iris, syn_iris) ``` ## Dependence `association_matrix()` works for mixed data: Spearman correlation for pairs of numeric variables, bias-corrected Cramér's V for pairs of categorical variables and the correlation ratio for mixed pairs. ```{r, fig.height = 4} plot_association(real, syn, vars = c("Ozone", "Solar.R", "Wind", "Temp")) ``` ## General utility: can a classifier tell them apart? If real and synthetic records are exchangeable, a classifier should not be able to predict which set a record comes from. * **pMSE** (Snoke et al., 2018) is the mean squared deviation of the estimated propensity scores from the share of synthetic records. Its ratio to the expected value under the null of identical distributions should be close to 1. * The **discriminator AUC** is computed from cross-validated predictions; 0.5 means indistinguishable. ```{r} pmse(real, syn, seed = 1) discriminator_auc(real, syn, seed = 1) ``` ## Specific utility: do analyses give the same answers? `ci_overlap()` fits the same regression to both data sets and compares the confidence intervals (Karr et al., 2006): ```{r} ci_overlap(real, syn, Ozone ~ Temp + Wind + Solar.R)[, c("term", "estimate_real", "estimate_synthetic", "std_diff", "overlap")] ``` Overlaps close to 1 mean that inference on synthetic data leads to the same conclusions. ## Disclosure risk: distance to closest record A synthesizer that copies real records has perfect fidelity but no privacy. `dcr()` computes, for each synthetic record, the Gower distance to the closest real record, and compares it to how close real records are to each other. ```{r} risk <- dcr(real, syn, seed = 1) risk$quantiles risk$exact_match_rate plot_dcr(risk) ``` Exact copies, or a synthetic distance distribution well to the left of the real one, are warning signs. Remedies include `knots` (coarser quantile functions), lowering `dependence`, or a larger `min_stratum_size`. ## Plots from a comparison object All plots are also available from the comparison object: ```{r, eval = FALSE} plot(cmp, type = "marginals") plot(cmp, type = "association") plot(cmp, type = "pairs") plot(cmp, type = "qq") plot(cmp, type = "dcr") ggplot2::autoplot(cmp, type = "association") + ggplot2::ggtitle("airquality") ``` Every plotting function returns a `ggplot` object, so themes, scales and labels can be changed with the usual **ggplot2** syntax. ## References Karr, A. F., Kohnen, C. N., Oganian, A., Reiter, J. P. and Sanil, A. P. (2006). A framework for evaluating the utility of data altered to protect confidentiality. *The American Statistician*, 60(3), 224–232. Snoke, J., Raab, G. M., Nowok, B., Dibben, C. and Slavkovic, A. (2018). General and specific utility measures for synthetic data. *Journal of the Royal Statistical Society A*, 181(3), 663–688.