Modern survival analysis for Python: Narwhals-native, R-validated, beautifully visualized.
Developers
Richard Iannone
Maintainer
Modern survival analysis for Python
Greenwood turns time-to-event data into estimates, tests, models, and figures. Whether you’re tracking patient outcomes, equipment failure times, or any event that takes time to happen, Greenwood provides a complete toolkit for survival analysis.
Unlike many statistical libraries, Greenwood is built on Narwhals for backend-agnostic compute, validated to tolerance against R’s gold-standard survival package, and visualized with publication-quality interactive plots and tables. It works with Pandas, Polars, PyArrow, or any dataframe library you prefer.
Why Greenwood?
- Works with your dataframe library: Pandas, Polars, PyArrow, or any Narwhals-compatible frame (use the
format= argument to control output types).
- Rigorously validated: every statistic is tested to tolerance against R’s survival package, so results match what the field already trusts.
- Beautiful visualizations: interactive survival curves, forest plots, and cumulative incidence plots with a choice of plotting backends.
- Publication-ready tables: built-in integration with Great Tables for production-quality reporting.
- Complete toolkit: from simple Kaplan-Meier curves to Cox regression, competing risks, and multi-state models (everything you need, all validated)`.
The analytical pipeline
Every Greenwood analysis follows a consistent workflow:
- Represent the data with a formula such as
"Surv(time, status) ~ age" or an Outcome that names your columns, or with Surv() for values already in hand: capture survival outcomes and censoring (right, left, interval, or counting-process form) in a validated response object.
- Estimate and summarize: use KaplanMeier, NelsonAalen, or Turnbull (for interval-censored data) to describe the data, then compare groups with logrank_test, trend tests, or RMST comparisons.
- Build and fit models: fit Cox proportional hazards, parametric AFT models, or competing-risks models to examine covariate effects.
- Visualize and evaluate: create publication-quality plots with plot_survival,
forest_plot, or cif_plot, and assess model performance with concordance, Brier scores, or cross-validation.
- Report results: tidy model summaries and publication tables are built in via broom-compatible tidy() functions and Great Tables.
A first look
Here’s a complete example: load survival data, estimate a curve, test for differences, and visualize.
Step 1: Describe the response by column names
import greenwood as gw
# Load data and describe the survival response (a status of 2 marks a death)
lung = gw.load_dataset("lung", backend="polars")
death = gw.Outcome.surv(time="time", event="status")
# Evaluate it against the frame to see the Surv object it produces
death.bind(lung)
Surv(type=right, n=228, events=165)
Step 2: Estimate Kaplan-Meier survival curves, stratified by sex
# Fit stratified Kaplan-Meier with log-log confidence intervals
km = gw.KaplanMeier(conf_type="log-log").fit(death, by="sex", data=lung)
# Get median survival per group with 95% confidence intervals
km.median(format="polars")
shape: (2, 4)| strata | time | conf_low | conf_high |
|---|
| str | f64 | f64 | f64 |
| "sex=1" | 270.0 | 210.0 | 306.0 |
| "sex=2" | 426.0 | 345.0 | 524.0 |
Step 3: Compare survival between groups with a log-rank test
# Test for differences in survival between sexes
test_result = gw.logrank_test("Surv(time, status) ~ sex", data=lung)
test_result
TestResult(method='Log-rank test', statistic=10.3267, df=1, p_value=0.001311)
Step 4: Visualize with an interactive plot and at-risk table
# Create publication-quality survival curves
gw.plot_survival(km, risk_table=True)
Step 5: Fit a Cox proportional hazards model
# Model covariate effects on hazard, written as an R-style formula
cox = gw.CoxPH().fit("Surv(time, status) ~ age + sex", data=lung)
# Get hazard ratios with confidence intervals
gw.tidy(cox, exponentiate=True, format="polars")
shape: (2, 7)| term | estimate | std_error | statistic | p_value | conf_low | conf_high |
|---|
| str | f64 | f64 | f64 | f64 | f64 | f64 |
| "age" | 1.017191 | 0.009223 | 1.848078 | 0.064591 | 0.998969 | 1.035747 |
| "sex" | 0.598566 | 0.167458 | -3.06476 | 0.002178 | 0.431094 | 0.831099 |
What’s inside
Descriptive statistics: Kaplan-Meier curves with Greenwood confidence intervals, restricted mean survival time (RMST), quantiles, and medians. Nelson-Aalen cumulative hazard. Turnbull’s NPMLE for interval-censored data. Event tables and risk sets.
Group comparisons: Log-rank and Peto-Peto tests, Fleming-Harrington tests for weighted comparisons, linear trend tests across ordered groups, and RMST hypothesis tests with pairwise comparisons.
Regression models: Cox proportional hazards with stratification, baseline hazard prediction with confidence intervals, and full diagnostics. Parametric accelerated failure time (AFT) models with Weibull, exponential, log-normal, and log-logistic distributions. Competing risks via Aalen-Johansen and Fine-Gray models. Multi-state transition probabilities.
Visualization: interactive survival curves with confidence bands, censoring marks, and aligned at-risk tables. Forest plots with hazard ratios. Cumulative incidence plots for competing risks. All plots are interactive and publication-ready.
Model evaluation: Concordance index (Harrell’s C-statistic), IPCW Brier scores, integrated Brier scores, and k-fold cross-validation with stratification support.
Built-in data: load bundled datasets (lung, veteran, ovarian, pbc, colon, mgus2) in any supported backend.