Skip to content

gen_surv

Simulate survival data with a known truth. gen_surv generates synthetic time-to-event datasets from twelve models — proportional hazards, accelerated failure time, competing risks, cure fractions, piecewise hazards, recurrent events and two illness-death processes — so you can test an estimator against parameters you chose yourself.

It is a Python port of the R package genSurv, extended well past the original's four models.

Install it Generate your first dataset


Thirty seconds

from gen_surv import generate

df = generate(
    model="cphm",           # Cox proportional hazards
    n=6,
    beta=0.5,               # log hazard ratio
    covariate_range=2.0,    # X0 ~ Uniform(0, 2)
    model_cens="uniform",
    cens_par=1.0,
    seed=42,
)
print(df)
       time  status        X0
0  0.438878     0.0  1.547912
1  0.094177     0.0  1.394736
2  0.037041     1.0  1.522279
3  0.370798     0.0  0.900772
4  0.646901     1.0  1.287730
5  0.251113     1.0  0.454477

You picked beta = 0.5, so you know what a correct estimator should recover. That is the whole point: every column above was produced by a mechanism you specified.

What it is for

  • Method development

    Check that a new estimator recovers the parameters that generated the data, across sample sizes and censoring levels.

  • Teaching

    Hand students a dataset whose hazard ratio, cure fraction or transition intensities are known, and let them try to find them.

  • Benchmarking

    Compare implementations on identical inputs — every generator takes a seed, so runs are byte-for-byte reproducible.

  • Software validation

    Exercise an analysis pipeline against edge cases: heavy censoring, a cured subpopulation, non-proportional hazards, panel-observed states.

The twelve models

model= Family Returns Page
cphm Cox proportional hazards one row per subject Cox PH
aft_ln Log-normal AFT one row per subject AFT
aft_weibull Weibull AFT one row per subject AFT
aft_log_logistic Log-logistic AFT one row per subject AFT
piecewise_exponential Piecewise constant hazard one row per subject Piecewise
competing_risks Cause-specific constant hazards one row per subject Competing risks
competing_risks_weibull Cause-specific Weibull hazards one row per subject Competing risks
mixture_cure Logistic cure + exponential failure one row per subject Mixture cure
cmm Illness-death, counting-process intervals two or three rows per subject CMM
thmm Illness-death, observed state panel two or three rows per subject THMM
tdcm Cox with a time-dependent covariate one row per subject TDCM
recurrent_events Repeated events per subject (AG, PWP) one row per at-risk interval Recurrent events

There is a thirteenth generator that generate() cannot reach. gen_multistate takes an arbitrary transition graph as a list of objects rather than a set of scalars, so it has no model= string and no command-line form. cmm and thmm are configurations of it.

Not sure which one you need? Choosing a model walks through the decision.

The output shape is not the same for every model

Multi-state generators return several rows per subject, and column names differ between families — cphm has no id column at all, tdcm uses covariate where the others use X0. Read Output schemas before you write code that consumes a generated frame.

The ground truth, not just the data

A generated frame looks like a real one, which means it hides the same things. simulate() hands back what a real dataset never could — the coefficients actually used, the event time before censoring intervened, which subjects are cured, when a covariate crossed over:

from gen_surv import simulate

result = simulate("cphm", n=1000, beta=0.5, covariate_range=2.0,
                  model_cens="uniform", cens_par=1.0, seed=42)

result.truth["event_time"]       # when each subject would have failed
result.truth["censoring_time"]   # what censoring hid

Several models draw their coefficients for you when you omit them, and result.truth["betas"] is the only way to learn what they were.

Beyond generating

  • Ground truth — configurations, latent times, and the coefficients a model chose for itself.
  • Baseline hazards — five families, and the protocol for writing your own.
  • Censoring — the two mechanisms wired into every generator, and the standalone samplers for everything else.
  • Summarising a dataset — event counts, follow-up, quality checks, dataset comparison.
  • Plotting — Kaplan-Meier curves, hazard comparisons, covariate effects.
  • Fitting models to the data — handing the frame to lifelines, scikit-survival or scikit-learn.
  • Command line — generate and plot without writing Python.

Citing

@software{ribeiro_gensurv,
  title   = {gen_surv: Survival Data Simulation in Python},
  author  = {Diogo Ribeiro},
  url     = {https://github.com/DiogoRibeiro7/genSurvPy},
  version = {3.1.2}
}

Machine-readable metadata lives in CITATION.cff and .zenodo.json.