| Title: | Synthetic Data Doubles for Safer Prototyping |
| Version: | 0.6.1 |
| Author: | Lennon Li [aut, cre] |
| Maintainer: | Lennon Li <yeli@biostats.ai> |
| Description: | Creates synthetic data doubles from real datasets for prototyping, teaching, 'shiny' development, and AI-assisted programming. Provides data profiling, role detection, configurable synthesis, utility comparison, and disclosure-risk warnings. Synthetic outputs are intended to reduce direct disclosure risk, not to guarantee privacy. |
| License: | MIT + file LICENSE |
| Encoding: | UTF-8 |
| Language: | en-US |
| Depends: | R (≥ 4.1.0) |
| Imports: | cli, digest, dplyr, haven, jsonlite, readr, readxl, rlang, stats, tibble, utils, withr, yaml, zip |
| Suggests: | bslib, callr, DT, ggplot2, knitr, nnet, pkgdown, plotly, rmarkdown, shiny, shinytest2, spelling, synthpop, testthat (≥ 3.0.0) |
| Config/testthat/edition: | 3 |
| URL: | https://dataganger.biostats.ai/, https://github.com/lennon-li/dataganger |
| BugReports: | https://github.com/lennon-li/dataganger/issues |
| Config/roxygen2/version: | 8.0.0 |
| RoxygenNote: | 8.0.0 |
| LazyData: | true |
| VignetteBuilder: | knitr |
| NeedsCompilation: | no |
| Packaged: | 2026-07-12 16:49:35 UTC; yeli |
| Repository: | CRAN |
| Date/Publication: | 2026-07-21 11:00:19 UTC |
DataGangeR uses two synthesis engines. By default the engine is chosen
automatically from your objective: demo uses the dependency-free internal
engine, development uses synthpop when it is installed so moderate
correlations can be preserved, and analytics requires synthpop plus an
explicit risk acknowledgement because high-fidelity synthesis may retain
sensitive structure. The engine can also be selected explicitly (auto,
internal, or synthpop) in both the Shiny app and the CLI. Install
synthpop with install.packages("synthpop") to enable
relationship-preserving synthesis at full fidelity.
When synthpop is used, please cite: Nowok B, Raab GM, Dibben C (2016).
"synthpop: Bespoke Creation of Synthetic Data in R." Journal of
Statistical Software, 74(11), 1-26. doi:10.18637/jss.v074.i11
Description
Creates synthetic data doubles from real datasets for prototyping, teaching, 'shiny' development, and AI-assisted programming. Provides data profiling, role detection, configurable synthesis, utility comparison, and disclosure-risk warnings. Synthetic outputs are intended to reduce direct disclosure risk, not to guarantee privacy.
Author(s)
Maintainer: Lennon Li yeli@biostats.ai
Authors:
Lennon Li yeli@biostats.ai
See Also
Useful links:
Report bugs at https://github.com/lennon-li/dataganger/issues
Assess k-anonymity over a set of quasi-identifier columns
Description
Cross-tabulates the quasi-identifier columns and reports how many records
fall in combinations (equivalence classes) smaller than k. NA is treated
as a distinct level so that missing values cannot mask a small cell.
Usage
assess_kanonymity(data, qi_cols, k = 5)
Arguments
data |
A data frame. |
qi_cols |
Character vector of quasi-identifier column names. |
k |
Minimum acceptable cell size (default 5). |
Value
A list with no_qi (logical), smallest_cell (integer),
n_below, pct_below, and worst_cells (a tibble of the smallest
combinations and their counts).
Check whether synthetic data is code-compatible with the original
Description
Evaluates whether code written against the synthetic development twin will
run against the original data without errors. Checks column presence, R
class compatibility, factor level coverage, all-NA columns, zero-variance
columns, missingness spikes, and ID uniqueness. haven_labelled columns
currently round-trip as character in synthetic data, so that class change is
expected for now.
Usage
check_code_readiness(original, synthetic, roles = NULL)
Arguments
original |
The original data frame. |
synthetic |
The synthetic data frame (from |
roles |
Optional; a |
Value
An S3 object of class dataganger_code_readiness with
components:
- checks
A tibble with one row per check:
check,scope,column,status("pass"/"warn"/"fail"),message.- summary
List with
n_pass,n_warn,n_fail,ready(TRUE when n_fail == 0).- meta
List with dimensions and
generated_at.
Examples
orig <- data.frame(x = 1:10, y = factor(letters[1:10]))
spec <- synth_spec(purpose = "demo")
syn <- synthesize_data(orig, spec)
check_code_readiness(orig, syn)
Compare original and synthetic datasets
Description
Compares an original dataset with its synthetic double across dataset-level
dimensions, numeric distributions, categorical distributions, and numeric
correlations. Returns a structured dataganger_comparison object.
Usage
compare_synthetic(original, synthetic, roles = NULL)
Arguments
original |
The original data frame. |
synthetic |
The synthetic data frame (from |
roles |
Optional; a |
Value
An S3 object of class dataganger_comparison, a list with
components dataset, numeric, categorical, relationship, interaction,
privacy_flags, and meta.
Examples
dat <- data.frame(x = 1:10, y = letters[1:10])
spec <- synth_spec(purpose = "demo")
syn <- synthesize_data(dat, spec)
compare_synthetic(dat, syn)
DataGangeR command-line interface
Description
Testable command-line entrypoint used by the installed exec/dataganger shim.
Usage
dataganger_cli(args = commandArgs(trailingOnly = TRUE), quit = FALSE)
Arguments
args |
Character vector of trailing command-line arguments. |
quit |
Logical. When |
Value
Integer status code: 0 success, 1 processing error, 2 syntax error.
Detect data roles for each column
Description
Applies heuristic-based role detection to every column in a data frame.
Roles include a recommended synthesis role plus the two primary disclosure
axes used by the Configure step: whether a column points to a person
(identifies) and whether it is sensitive. The legacy single
disclosure_role value is retained as derived compatibility metadata for
existing synthesis/export/CLI paths.
Usage
detect_roles(data, profile = NULL)
Arguments
data |
A data frame. |
profile |
Optional; a |
Value
An S3 object of class dataganger_roles, a tibble with columns:
- variable
Column name.
- class
R class of the column.
- recommended_role
Role detected by heuristic.
- user_role
User-supplied override (initially
NA).- simulation
How the column is treated during synthesis.
- reason
Justification for the recommended role.
- identifies
Whether the column points to a person:
"none","combination", or"direct".- sensitive
Logical flag for whether the column is sensitive if revealed.
- user_identifies
User-supplied override for
identifies(initiallyNA).- user_sensitive
User-supplied override for
sensitive(initiallyNA).- disclosure_role
Disclosure role.
NA(unselected) is the conservative default whenever detection is not confident; the user must choose a role before generating."direct"and"sensitive"are the only values auto-assigned (confident identifier / known-sensitive name)."quasi"and"none"are user-assigned choices only.- disclosure_reason
Justification for the auto-assigned disclosure role.
Examples
df <- data.frame(
id = 1:50,
date = as.Date("2020-01-01") + 0:49,
city = rep(c("Toronto", "Vancouver", "Montreal"), length.out = 50),
cat = factor(rep(letters[1:3], length.out = 50))
)
detect_roles(df)
Enforce k-anonymity on a synthetic dataset (output guarantee)
Description
Shapes the synthetic output so that no quasi-identifier combination appears
in fewer than k records. Direct identifiers are removed. Quasi-identifiers
are coarsened step-by-step and any residual cell still below k has its QI
values blanked (NA). Operates on the output only.
Usage
enforce_kanon(synthetic, roles, k = 5, max_steps = 6L, max_suppress_frac = 0.2)
Arguments
synthetic |
A synthetic data frame. |
roles |
A roles object/data frame with |
k |
Minimum cell size (default 5). |
max_steps |
Maximum coarsening iterations (default 6). |
max_suppress_frac |
Feasibility backstop. If satisfying |
Value
The shaped synthetic data frame, with an attribute kanon
recording the achieved state (smallest_cell, suppressed_cells,
qi_cols, k, infeasible).
Example administrative claims dataset
Description
A realistic-but-fictional administrative claims dataset with 300 synthetic records. Contains claim identifiers, procedure codes (haven-labelled), costs, and provider locations. No real patient data.
Usage
example_admin_claims
Format
A tibble with 300 rows and 9 columns:
- claim_id
Integer. Claim identifier.
- patient_id
Character. Patient identifier.
- service_date
Date. Date of service.
- dx_code
Factor. Diagnosis code.
- proc_code
haven_labelled. Procedure type (Consult / Surgery / Lab / Imaging).
- cost
Numeric. Claim cost in dollars, some missing.
- approved
Logical. Whether the claim was approved, some missing.
- provider_city
Character. City of the service provider.
- postal_code
Character. Forward sortation area (FSA).
Example health survey dataset
Description
A realistic-but-fictional health survey dataset with 200 synthetic records. Contains demographics, clinical measures, and haven-labelled smoking status. No real patient data.
Usage
example_health_survey
Format
A tibble with 200 rows and 10 columns:
- record_id
Character. Record identifier.
- age
Numeric. Age in years.
- sex
Factor. Biological sex (Male / Female).
- bmi
Numeric. Body mass index, with some missing values.
- smoking_status
haven_labelled. Smoking status (Current / Former / Never).
- systolic_bp
Numeric. Systolic blood pressure, some missing.
- diastolic_bp
Numeric. Diastolic blood pressure.
- survey_date
Date. Date of survey response.
- province
Factor. Canadian province abbreviation.
- comments
Character. Free-text comments, some missing.
Example disease registry dataset
Description
A realistic-but-fictional disease registry dataset with 150 synthetic records. Contains enrollment data, disease staging (haven-labelled), biomarker values, and patient status. No real patient data.
Usage
example_registry
Format
A tibble with 150 rows and 10 columns:
- subject_id
Character. Subject identifier.
- enroll_date
Date. Date of enrollment.
- age_at_enroll
Numeric. Age at enrollment in years.
- disease_stage
haven_labelled. Disease stage (Stage I-IV).
- biomarker_a
Numeric. Biomarker A level, some missing.
- biomarker_b
Numeric. Biomarker B level, some missing.
- status
Factor. Current patient status.
- last_visit
Date. Date of last follow-up visit, some missing.
- region
Factor. Geographic region.
- notes
Character. Clinical notes, some missing.
Export a Lens-compatible diagnostic schema for a dataset
Description
Profiles a data frame and writes a diagnostic_view.json describing
column roles, sensitivity, and exposure levels. Does not synthesise data.
Intended for agent pre-inspection and Lens ingestion.
Usage
export_diagnostic_package(
data,
path,
roles = NULL,
profile = NULL,
overwrite = FALSE
)
Arguments
data |
A data frame to describe. |
path |
Output path for the JSON file. |
roles |
Optional; a |
profile |
Optional; a |
overwrite |
Logical. When |
Value
Invisibly, the written JSON path.
Examples
dat <- data.frame(age = c(34, 29, 41), grp = c("a", "b", "c"))
export_diagnostic_package(dat, path = tempfile(fileext = ".json"))
Export a synthetic data bundle
Description
Writes a reviewable export bundle containing the synthetic data, a human guide, an optional comparison report, a combined agent recipe, the packaged agent instructions, and a manifest. By default the bundle is written as a zip archive.
Usage
export_synthetic(
synthetic,
original = NULL,
comparison = NULL,
privacy = NULL,
path,
format = c("zip", "dir"),
sanitize_for_spreadsheets = TRUE,
purpose = NULL,
roles = NULL,
include_original_names = NULL,
fail_on_exact_match = FALSE,
include_report = TRUE,
kanon_acknowledged = FALSE,
include_dictionary = TRUE,
code_readiness = NULL,
compact = FALSE,
overwrite = FALSE
)
Arguments
synthetic |
A synthetic data frame, typically from |
original |
Optional original data frame. When provided, used for the data dictionary, comparison fallback, privacy fallback, and exact-row guard. |
comparison |
Optional |
privacy |
Optional |
path |
Output path. Required. For |
format |
Character. One of |
sanitize_for_spreadsheets |
Logical. When |
purpose |
Optional purpose label for README text. Defaults to the
purpose recorded in |
roles |
Optional recorded role decisions as a |
include_original_names |
Logical or |
fail_on_exact_match |
Logical. When |
include_report |
Logical. When |
kanon_acknowledged |
Logical. Records whether a human explicitly
acknowledged exporting a bundle whose k-anonymity backstop was infeasible.
Interactive UI export uses this to clear the bundle blocker; non-interactive
exports leave it |
include_dictionary |
Deprecated, ignored. |
code_readiness |
Optional |
compact |
Deprecated, ignored. |
overwrite |
Logical. When |
Value
Invisibly, the written bundle path.
Examples
dat <- data.frame(
age = 21:70,
score = seq(10, 59),
grp = rep(LETTERS[1:5], each = 10)
)
spec <- synth_spec(purpose = "demo", seed = 1, engine = "internal")
syn <- synthesize_data(dat, spec)
export_synthetic(syn, path = tempfile(fileext = ".zip"), include_report = FALSE)
Geographic synthetic sample data
Description
A synthetically generated dataset of 50 regional summary records for use
as sample input in the DataGangeR Shiny app. Simulates public-health
surveillance data aggregated by region. Generated with set.seed(42).
Usage
geographic_sample
Format
A data frame with 50 rows and 5 columns:
- region
Region identifier (Region_01 through Region_50)
- population
Regional population count
- rate_per_100k
Event rate per 100,000 population
- category
Area classification (Urban / Suburban / Rural)
- risk_level
Assigned risk level (Low / Medium / High)
Source
Synthetically generated via data-raw/Geographic_sample.R
Individual-level synthetic sample data
Description
A synthetically generated dataset of 200 individual records for use as
sample input in the DataGangeR Shiny app. Contains demographic and health
variables with realistic distributions. Generated with set.seed(42).
Usage
individual_sample
Format
A data frame with 200 rows and 7 columns:
- id
Integer record identifier
- age
Age in years (18–85)
- sex
Sex (Male / Female / Other)
- income
Annual income in dollars (log-normal, some NAs)
- education
Highest education level
- smoker
Logical smoking status
- bmi
Body mass index
Source
Synthetically generated via data-raw/individual_sample.R
Heuristic: does this data frame look pre-aggregated (a table of counts)?
Description
Disclosure control assumes individual-level microdata. A positive result should drive a non-blocking warning, not a separate policy.
Usage
looks_aggregated(data)
Arguments
data |
A data frame. |
Value
A list with aggregated (logical) and reason (character).
Create a one-command agent-ready bundle from a raw data file
Description
Reads a data file, profiles it, detects column roles, synthesizes data, and exports a zip bundle suitable for passing to an AI agent.
Usage
make_agent_bundle(
file,
out,
purpose = "development",
seed = NULL,
overwrite = FALSE,
...
)
Arguments
file |
Path to the input data file. Passed to |
out |
Path for the output zip file. |
purpose |
Synthesis purpose preset. Defaults to |
seed |
Optional integer random seed for reproducible synthesis. |
overwrite |
Logical. When |
... |
Additional arguments passed to |
Value
Invisibly, the written bundle path.
Examples
dat <- data.frame(
age = c(24, 28, 35, 41),
score = c(88, 91, 84, 95),
grp = c("A", "A", "B", "B")
)
path <- tempfile(fileext = ".csv")
readr::write_csv(dat, path)
make_agent_bundle(
file = path,
out = tempfile(fileext = ".zip"),
purpose = "demo",
seed = 1
)
Plot comparison summaries
Description
Produces two bar charts comparing original and synthetic data:
standardized differences for numeric columns and total variation
distance for categorical columns. Requires ggplot2 (Suggests).
Usage
plot_comparison(comparison)
Arguments
comparison |
A |
Value
Invisibly, a list with two ggplot objects: numeric and
categorical. Each is NULL if no columns of that type exist.
Examples
dat <- data.frame(x = 1:10, y = letters[1:10])
spec <- synth_spec(purpose = "demo")
syn <- synthesize_data(dat, spec)
cmp <- compare_synthetic(dat, syn)
if (requireNamespace("ggplot2", quietly = TRUE)) {
plot_comparison(cmp)
}
Run disclosure-risk privacy checks
Description
Scans original and (optionally) synthetic data for disclosure-risk flags.
Supports two stages: "pre" (before synthesis, requires only the
original dataset and roles) and "post" (after synthesis, requires both
original and synthetic).
Usage
privacy_check(
original,
synthetic = NULL,
roles = NULL,
stage = c("pre", "post"),
spec = NULL
)
Arguments
original |
The original data frame. |
synthetic |
Optional; the synthetic data frame (required for
|
roles |
Optional; a |
stage |
Character. |
spec |
Optional; a |
Value
An S3 object of class dataganger_privacy_check, a tibble with
columns variable, flag, severity, stage, and recommendation.
Examples
df <- data.frame(id = 1:50, x = rnorm(50), city = rep("Toronto", 50))
roles <- detect_roles(df)
privacy_check(df, roles = roles, stage = "pre")
Profile a dataset column-by-column
Description
Profiles each column in a data frame, detecting type, computing summary statistics, missingness, cardinality, and flags for free-text, dates, and haven-labelled vectors.
Usage
profile_data(data)
Arguments
data |
A data frame or tibble. |
Value
An S3 object of class dataganger_profile, which is a list
containing:
-
profile: a tibble with one row per column. -
n_rows: total number of rows. -
n_cols: total number of columns. -
generated_at: POSIXct timestamp of when profiling ran.
Examples
df <- data.frame(
id = 1:5,
name = letters[1:5],
score = c(10.1, 15.2, NA, 13.8, 11.0)
)
profile_data(df)
Read a data file into a tibble
Description
Reads CSV, Excel (.xlsx, .xls), SAS (.sas7bdat), and XPT (.xpt)
files into a tibble. Dispatches on file extension.
Usage
read_input(file, sheet = NULL, encoding = NULL, ...)
Arguments
file |
Path to the data file. |
sheet |
For Excel files, the sheet name or index to read. Passed to
|
encoding |
Character encoding for CSV files (e.g. |
... |
Additional arguments passed to the underlying reader
( |
Value
A tibble::tibble(). SAS/XPT imports preserve haven_labelled
vectors as-is.
Examples
path <- tempfile(fileext = ".csv")
readr::write_csv(data.frame(id = 1:3, grp = c("a", "b", "c")), path)
read_input(path)
Report a problem or share feedback
Description
Prints a pre-filled GitHub issue you can copy into your browser for the
lennon-li/dataganger repository, with package and R environment details
already populated. Use this to report a bug, suggest a feature, or send
general feedback without copying session details by hand.
Usage
report_issue(
message = NULL,
context = NULL,
type = c("feedback", "bug", "feature")
)
Arguments
message |
Character. A short description of the problem or suggestion.
If |
context |
Character. Optional context about where the issue happened,
such as |
type |
Character. One of |
Value
Invisibly, the GitHub issue URL that was printed.
Examples
if (interactive()) {
report_issue(
message = "The export step was unclear when I skipped the dictionary.",
context = "Shiny app",
type = "feedback"
)
}
Launch the DataGangeR Shiny Application
Description
Opens the DataGangeR interactive workflow in a local Shiny app. Requires
the shiny, bslib, DT, and plotly packages.
Usage
run_app(max_upload_mb = 50, launch = interactive(), port = NULL, ...)
Arguments
max_upload_mb |
Maximum file upload size in megabytes. Default 50. |
launch |
Whether to launch the app. Default |
port |
Port to pass to |
... |
Additional arguments passed to |
Value
Invisibly NULL.
Examples
if (interactive()) {
run_app()
}
Suggest a sufficient synthetic row count
Description
Given a profile_data() profile (which carries cross-column coverage
information), suggests how many rows to synthesize so that the synthetic
data can still represent every category combination and every category
level observed in the original data, without blindly matching a large
original row count.
Usage
suggest_min_rows(
profile,
roles = NULL,
data = NULL,
k = 5L,
threshold = 1000L,
cap = 5000L
)
Arguments
profile |
A |
roles |
Optional; a |
data |
Optional; the original data frame. When provided alongside
|
k |
Reserved for a future k-anonymity-style cell-size floor; unused by the current coverage rule. |
threshold |
Row count at or above which a reduction is suggested. |
cap |
Maximum suggested row count from combination coverage. |
Details
The rule (coverage-based) is:
For small inputs (fewer than
thresholdrows, default 1000) the original row count is kept — there is nothing to gain from reducing.Otherwise the suggestion is the number of observed cross-column category combinations, capped at
cap(default 5000) to avoid suggesting millions of rows on wide data, and floored at the largest per-column distinct count so every level remains representable. The suggestion never exceeds the original row count.
Continuous columns are covered by preserving their min/max (already handled by the synthesis engine); they do not raise the suggested count.
Value
A list with:
- n
Suggested integer row count.
- rationale
Human-readable explanation.
- original_n
Original row count.
- combination_count
Observed category-combination count (or
NA).- floor
Per-column distinct floor used (or
NA).- capped
TRUEif the cap bound the suggestion.- reduced
TRUEif the suggestion is below the original count.
Examples
p <- profile_data(datasets::iris)
suggest_min_rows(p)
Create a synthesis specification
Description
Builds a synthesis specification from a purpose preset with optional
user overrides. The specification records the synthesis parameters but
does not check engine availability - that is done by synthesize_data().
Usage
synth_spec(
purpose,
level = NULL,
n = NULL,
roles = NULL,
privacy = NULL,
name_strategy = NULL,
seed = NULL,
engine = NULL,
acknowledge_risk = FALSE,
...
)
Arguments
purpose |
Character. A single non-missing string: |
level |
Character or |
n |
Integer or |
roles |
A |
privacy |
A |
name_strategy |
Character or |
seed |
Integer or |
engine |
Character or |
acknowledge_risk |
Logical. Required to be |
... |
Additional decision parameters passed to the spec list. These are the same settings exposed under Synthesis Settings in the app:
|
Value
An S3 object of class dataganger_spec (a named list).
Examples
synth_spec(purpose = "demo")
synth_spec(purpose = "development", n = 200, seed = 42)
synth_spec(purpose = "analytics", acknowledge_risk = TRUE)
Synthesize a data double
Description
Creates a synthetic copy of a dataset using the specified specification and engine. The internal engine supports schema-only (Level 1) and marginal (Level 2) synthesis. The optional synthpop engine is used for objectives that request moderate or high relationship preservation.
Usage
synthesize_data(data, spec, roles = NULL, engine = NULL)
Arguments
data |
A data frame to synthesize from. |
spec |
A |
roles |
Optional; a |
engine |
Character or |
Value
An S3 object of class dataganger_synthetic, a tibble with
attributes spec, original_dims, seed_used, and generated_at.
Disabling synthpop
Set options(dataganger.disable_synthpop = TRUE) to steer
auto-derived synthesis onto the internal engine even when synthpop is
installed. This is intended for environments where a synthpop synthesis
is undesirable or can hang unattended (for example continuous
integration). An explicit engine = "synthpop" request is still
honoured; only objective-derived routing is affected.
Examples
dat <- data.frame(x = 1:5, y = letters[1:5])
spec <- synth_spec(purpose = "demo")
syn <- synthesize_data(dat, spec)
Temporal synthetic sample data
Description
A synthetically generated dataset of 365 daily records for use as sample
input in the DataGangeR Shiny app. Simulates environmental monitoring
data across multiple sites. Generated with set.seed(42).
Usage
temporal_sample
Format
A data frame with 365 rows and 5 columns:
- date
Measurement date (daily from 2023-01-01)
- site_id
Site identifier (SITE_A through SITE_E)
- measurement
Numeric measurement value (some NAs)
- temperature
Ambient temperature in degrees Celsius
- flagged
Logical quality-control flag
Source
Synthetically generated via data-raw/temporal_sample.R