The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
Flexible synthetic data for nested, longitudinal and linked multi-table data.
flexsynth generates utility-oriented synthetic data for
supported flat, nested, longitudinal, and tree-linked structures,
working natively in long format — no pivoting nested or
longitudinal data to wide. It has first-class support for
tree-linked multi-table data (e.g. patients →
admissions → procedures / labs / meds), with referential integrity
preserved by construction.
It ships two engines behind one interface:
synth(..., privacy = dp_control(...)) implements a
person-level (ε, δ) mechanism and emits its
budget-accounting record. A governed release still requires independent
review of the privacy unit, public-domain assumptions, contribution
caps, and selected mechanism.Synthetic data is not anonymisation, and Track A output must never be described as differentially private.
structure formula
(~ id / visit / test).check_linkage() verifies key uniqueness and the absence of
orphans.sample,
cart, forest, ctree,
norm, normrank built in; add your own with
register_method().rule()
enforces row-wise or per-unit constraints
(e.g. dbp <= sbp, monotone length-of-stay) by unit-grain
rejection sampling.pool_synth() /
synth_glm() implement the published fully-synthetic
variance rules (synthpop / Reiter) across m synthetic
datasets. They use large-sample normal intervals; calibration still
depends on the estimand, synthesis model, sample size, and analysis
assumptions. compare_estimates() scores real-vs-synthetic
analyses by confidence-interval overlap.diagnose() (marginal fit,
correlation-matrix difference, categorical association via Cramer’s V,
propensity pMSE) and disclosure_risk() (replicated uniques,
distance-to-closest-record, membership-inference AUC, and TCAP
attribute-disclosure via target =).data.table
fast-path and parallel replicates
(synth_control(parallel = TRUE)) with reproducible L’Ecuyer
streams.# install.packages("remotes")
remotes::install_github("lauyeehow1986-hub/Flexsynth")The package installs with base-R dependencies. The default
method = "cart" requires the suggested rpart
package; ranger / partykit unlock additional
tree methods, and data.table unlocks the row-binding
fast-path. Use method = "sample" when only base R is
available.
library(flexsynth)
df <- data.frame(
id = rep(1:20, each = 2),
visit = rep(1:2, times = 20),
age = rep(round(rnorm(20, 60, 8)), each = 2),
sbp = round(rnorm(40, 130, 15))
)
res <- synth(df, structure = ~ id / visit, method = "cart", seed = 1)
head(as.data.frame(res))patients <- data.frame(id = 1:50, sex = sample(c("F", "M"), 50, TRUE))
adm <- do.call(rbind, lapply(patients$id, function(pid) {
n <- 1 + rpois(1, 0.6)
data.frame(id = pid, admission_id = seq_len(n), los = 1L + rpois(n, 3))
}))
res <- synth_linked(
tables = list(patients = patients, admissions = adm),
structures = list(patients = ~ id,
admissions = ~ id / admission_id),
keys = list(patients = "id",
admissions = c("id", "admission_id")),
seed = 1
)
syn <- as.list(res)
check_linkage(syn, keys = list(patients = "id",
admissions = c("id", "admission_id")))syn <- as.data.frame(synth(df, structure = ~ id / visit, seed = 1))
analysis_vars <- c("age", "sbp") # exclude generated structure keys
d <- diagnose(real = df, syn = syn, vars = analysis_vars)
plot(d) # overlaid marginals
disclosure_risk(real = df, syn = syn, quasi = analysis_vars)# Analyse all m synthetic sets with a published fully-synthetic pooling rule.
# A single set analysed naively generally under-states synthesis uncertainty.
res <- synth(df, structure = ~ id / visit, m = 10, seed = 1)
synth_glm(res, sbp ~ age) # pooled linear model
# any estimator works via pool_synth(res, function(d) <fit returning coef/vcov>)dp <- dp_control(epsilon = 1, delta = 1e-6, mechanism = "gaussian",
bounds = list(visit = c(1, 2), age = c(18, 100), sbp = c(60, 240)))
dp_res <- synth(df, structure = ~ id, privacy = dp, seed = 1)
dp_res$privacy # the (ε, δ) accounting recordTrack B supports three release shapes:
~ id);~ id / visit) using a bounded DP
Markov model; andsynth_linked(), using the
root entity as the privacy unit.dp_control() also exposes privately learned domains,
cross-table conditioning, higher-order transitions, adaptive marginal
selection, Private-PGM reconciliation, and AIM-style models. These
controls trade statistical fidelity, cell sparsity, runtime, and privacy
budget; they are not universally beneficial.
A governed DP release requires more than setting
epsilon: define the privacy unit and public domain
assumptions, justify contribution caps, retain the accounting record,
review utility at the intended analysis grain, and obtain independent
privacy review. See vignette("differential-privacy") for
the supported combinations, accounting model, and limitations.
vignette("getting-started") — getting startedvignette("valid-inference") — pooled inference and
attribute-disclosure (TCAP)vignette("nested-longitudinal") — repeated-measures
datavignette("linked-cardiac") — multi-table linked
synthesisvignette("differential-privacy") — Track Bdocs/roadmap.md
— phased delivery and what’s nextBundled example datasets are fully synthetic cardiac
data (see data-raw/make_toy_cardiac.R). No real patient
data ships with this package, and none should ever be committed.
MIT © 2026 flexsynth authors
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.