Survival Analysis
fyron.survival supports standard time-to-event workflows for clinical cohorts: Kaplan-Meier curves, log-rank tests, restricted mean survival time, Cox proportional hazards models, proportional hazards diagnostics, Weibull accelerated failure time models, and optional gradient survival boosting.
Install
uv add "fyron[survival]"For gradient survival boosting:
uv add "fyron[survival-ml]"Expected Cohort Format
Survival functions expect a patient-level DataFrame with:
| Column | Meaning |
|---|---|
time | positive follow-up duration |
event | event indicator, where 1 means event and 0 means censored |
| covariates | clinical, imaging, biomarker, or model-score features |
| group column | optional categorical group for KM/log-rank/RMST |
You can build time and event with fyron.cohort.build_survival_columns.
from fyron.cohort import build_survival_columns
df = build_survival_columns(
df,
start_col="diagnosis_date",
end_col="last_followup_or_event_date",
event_col="death_event",
time_col="time",
)Choosing A Survival Method
Use Kaplan-Meier curves to describe survival experience and compare groups visually. Use RMST when proportional hazards are questionable or when an absolute time-based summary is easier to communicate. Use Cox models when you need adjusted hazard ratios and can assess proportional hazards. Use Weibull AFT when an acceleration-time interpretation is more natural. Use gradient survival boosting when you need non-linear risk modeling, but keep it separate from classical inference.
Always inspect censoring, follow-up time, and event definitions before modeling. A technically valid model can still be clinically misleading if time starts at the wrong index date or if censoring differs systematically between groups.
Research Decision: Endpoint And Method
| Decision | Practical guidance |
|---|---|
| Index date | Use the date where follow-up truly starts, such as diagnosis, baseline CT, treatment, or enrollment. |
| Event coding | event=1 means the event occurred; event=0 means censored. |
| Time unit | Keep one unit across KM, Cox, RMST, calibration, and fixed-horizon prediction. |
| KM vs Cox | Use KM for descriptive curves; use Cox for adjusted hazard ratios when PH assumptions are plausible. |
| RMST | Prefer RMST when absolute survival time is easier to communicate or PH is questionable. |
| PH diagnostics | Run check_ph_assumptions before presenting adjusted Cox results as stable hazard ratios. |
API Contract
| Topic | Contract |
|---|---|
| Input shape | Patient-level DataFrame with one row per analysis unit. |
| Required columns | Positive duration column, binary event column where 1 means event, and named group/covariate columns. |
| Return shape | Kaplan-Meier result objects, DataFrames with estimates/tests, fitted lifelines objects, or Matplotlib figures. |
| Saved artifacts | KM figures, at-risk tables, Cox/HR tables, RMST tables, Schoenfeld diagnostics, and survival prediction tables. |
| Failure modes | Missing/invalid duration or event columns, nonpositive follow-up, no events, convergence warnings, violated PH assumptions, or missing optional survival extras. |
API Summary
| Function | Purpose | Main output |
|---|---|---|
plot_kaplan_meier | KM curves, log-rank tests, optional risk counts/RMST annotation | KaplanMeierPlotResult |
create_survival_groups | categorical, median, tertile, quartile, custom splits | DataFrame copy |
calculate_rmst | restricted mean survival time by group | DataFrame |
compare_rmst | pairwise RMST contrasts | DataFrame |
fit_univariate_cox | one Cox model per covariate | result table and fitters |
fit_multivariate_cox | multivariable Cox PH model | result table and fitter |
cox_forest_table | reporting table for Cox forest plots | DataFrame |
survival_prediction_table | fixed-time survival/risk predictions from supported models | DataFrame |
check_ph_assumptions | Schoenfeld-style PH diagnostics | summary/violations |
schoenfeld_residuals_table | scaled Schoenfeld residuals for Cox PH diagnostics | DataFrame |
schoenfeld_test_table | PH test table with optional adjusted p-values | DataFrame |
plot_schoenfeld_residuals | residual scatter plot by covariate | (fig, ax) |
fit_weibull_aft | parametric accelerated failure time model | result table and fitter |
plot_aft_survival_curves | predicted survival curves for covariate profiles | figure/axes |
train_gradient_survival_boosting | non-linear survival risk modeling | model, risk scores, concordance |
Function Parameter Reference
| Function | Required | Optional | Returns |
|---|---|---|---|
plot_kaplan_meier | df, duration_col, event_col | group_col, split options, CI/risk count/style/save/RMST options | KaplanMeierPlotResult |
create_survival_groups | df, column, method, group_col | bins, labels, duplicates | DataFrame copy |
default_tau_from_observed_times | df, duration_col | group_col | float tau |
calculate_rmst | df, duration_col, event_col | group_col, tau | RMST DataFrame |
compare_rmst | df, duration_col, event_col, group_col, reference | tau | comparison DataFrame |
fit_univariate_cox | df, duration_col, event_col, covariates | strata, robust | result table and fitters |
fit_multivariate_cox | df, duration_col, event_col, one of covariates/formula | strata, robust | result table and fitter |
cox_forest_table | Cox result dict, fitter, or DataFrame | covariate_col | reporting DataFrame |
survival_prediction_table | fitted model, feature DataFrame, times | id_col | long prediction DataFrame |
check_ph_assumptions | training_df, fitted Cox fitter | p_value_threshold, show_plots, columns, advice | summary/violations dict |
schoenfeld_residuals_table | fitted Cox fitter, training DataFrame | scaled/raw residuals | residual DataFrame |
schoenfeld_test_table | fitted Cox fitter, training DataFrame | time transform, FDR correction | test DataFrame |
plot_schoenfeld_residuals | residual table | covariates, save options | (fig, ax) |
fit_weibull_aft | df, duration_col, event_col, one of covariates/formula | robust | result table and fitter |
plot_aft_survival_curves | fitter, covariates, profiles | plot_baseline, figsize, ax, plot_kwargs | figure/axes/fitter dict |
train_gradient_survival_boosting | X_train, duration_train, event_train | test data, random_state, model params | model, risk scores, concordance dict |
Kaplan-Meier Curves
from fyron.survival import plot_kaplan_meier
km = plot_kaplan_meier(
df,
duration_col="time",
event_col="event",
group_col="stage",
at_risk_counts=True,
title="Overall survival by stage",
figsize=(7, 4),
)
km.logrank
km.n_per_groupUse Kaplan-Meier plots for descriptive group comparisons. They are especially helpful before fitting adjusted models because they expose censoring patterns and group sizes.
Automatic Risk Groups
You can derive groups from a continuous score:
km = plot_kaplan_meier(
df,
duration_col="time",
event_col="event",
split_column="risk_score",
split_method="quartile",
at_risk_counts=True,
)Survival Group Construction
from fyron.survival import create_survival_groups
df_q = create_survival_groups(
df,
column="risk_score",
method="quartile",
out_col="risk_quartile",
)Supported methods include:
| Method | Use |
|---|---|
categorical | preserve an existing categorical column |
median | high/low split |
tertile | three risk bands |
quartile | four risk bands |
custom | user-defined bins and labels |
Restricted Mean Survival Time
RMST is often easier to interpret clinically than a hazard ratio because it estimates average event-free time up to a fixed horizon tau.
from fyron.survival import calculate_rmst, compare_rmst
rmst = calculate_rmst(
df,
duration_col="time",
event_col="event",
group_col="treatment",
tau=365,
)
contrast = compare_rmst(
df,
duration_col="time",
event_col="event",
group_col="treatment",
reference="standard",
tau=365,
)Choose tau before looking at results when possible. It should be within the range where the survival curve is still supported by enough patients.
Cox Regression
from fyron.survival import fit_univariate_cox, fit_multivariate_cox
uni = fit_univariate_cox(
df,
duration_col="time",
event_col="event",
covariates=["age", "risk_score", "tumor_volume"],
)
multi = fit_multivariate_cox(
df,
duration_col="time",
event_col="event",
covariates=["age", "risk_score", "tumor_volume"],
)
multi["results"]The results table includes hazard ratios, confidence intervals, p-values, row counts, and concordance information when available.
For reporting and forest plots:
from fyron.survival import cox_forest_table
forest = cox_forest_table(multi)For fixed-time survival or event-risk tables:
from fyron.survival import survival_prediction_table
predictions = survival_prediction_table(
multi["fitter"],
df[["age", "risk_score", "tumor_volume"]],
times=[365, 730],
)Formula Interface
Use formulas for categorical terms or interactions supported by lifelines:
multi = fit_multivariate_cox(
df,
duration_col="time",
event_col="event",
formula="age + risk_score + C(stage)",
)Proportional Hazards Diagnostics
from fyron.survival import check_ph_assumptions
diagnostics = check_ph_assumptions(
df.dropna(),
multi["fitter"],
show_plots=False,
)
diagnostics["summary"]
diagnostics["violations"]Use these checks before interpreting Cox coefficients as time-constant hazard ratios.
For manuscript supplements and reviewer-facing diagnostics, export the residual and test tables explicitly:
from fyron.survival import (
plot_schoenfeld_residuals,
schoenfeld_residuals_table,
schoenfeld_test_table,
)
residuals = schoenfeld_residuals_table(multi["fitter"], df.dropna())
ph_tests = schoenfeld_test_table(
multi["fitter"],
df.dropna(),
method="rank",
p_adjust="fdr_bh",
)
fig, ax = plot_schoenfeld_residuals(
residuals,
covariates=["age", "risk_score"],
)Low p-values suggest possible time-varying effects. They do not automatically invalidate a study; they indicate that the model interpretation needs review, such as stratification, time interactions, RMST, or AFT modeling.
Weibull AFT
AFT models describe covariate effects on survival time rather than instantaneous hazard.
from fyron.survival import fit_weibull_aft, plot_aft_survival_curves
aft = fit_weibull_aft(
df,
duration_col="time",
event_col="event",
covariates=["age", "risk_score"],
)
plot_aft_survival_curves(
aft["fitter"],
covariates="risk_score",
profiles=[{"risk_score": -1.0}, {"risk_score": 1.0}],
figsize=(6, 4),
)Gradient Survival Boosting
Gradient survival boosting is useful when you expect non-linear or interaction effects and want a risk ranking for censored outcomes.
from fyron.survival import train_gradient_survival_boosting
boosted = train_gradient_survival_boosting(
X_train,
duration_train=train_df["time"],
event_train=train_df["event"],
X_test=X_test,
duration_test=test_df["time"],
event_test=test_df["event"],
n_estimators=300,
learning_rate=0.05,
max_depth=2,
random_state=42,
)
boosted["concordance_index"]
boosted["risk_score"]Returned keys include:
| Key | Meaning |
|---|---|
model | fitted GradientBoostingSurvivalAnalysis |
risk_score | evaluation-set risk scores |
concordance_index | evaluation concordance |
train_risk_score | training-set risk scores |
train_concordance_index | training concordance |
y_train / y_eval | structured survival arrays used by scikit-survival |
Higher risk scores indicate higher event risk.
Practical Checklist
- Confirm
timevalues are positive. - Confirm
eventuses1for event and0for censored. - Plot Kaplan-Meier curves before modeling.
- Report group sizes and censoring patterns.
- Use RMST when an absolute time difference is more interpretable than a hazard ratio.
- Check PH assumptions before relying on Cox PH interpretations.
- Treat survival boosting as a prediction/ranking model, not a replacement for causal survival analysis.
Return Values
| Function family | Return shape | Notes |
|---|---|---|
| Kaplan-Meier plotting | KaplanMeierPlotResult | Includes figure/axes plus group counts and log-rank details when available. |
| RMST helpers | DataFrame | One row per group or contrast. |
| Cox/AFT helpers | dict with result table and fitted model | Keep both the table and fitter for diagnostics or plotting. |
| PH diagnostics | dict | Includes summary tables and violation flags. |
| survival boosting | dict | Includes model, risk scores, concordance, and structured survival arrays. |
Common Pitfalls
- Do not use a post-diagnosis or post-treatment variable as if it were baseline.
- Do not interpret Cox hazard ratios without checking proportional hazards.
- Do not choose
taufor RMST only after inspecting the most favorable result. - Do not compare KM curves without checking group sizes and censoring.
- Ensure all duration columns use the same time unit.
Related Modules
- Cohort Tables for building
timeandevent. - Descriptive Analysis for endpoint summaries before modeling.
- Clinical Plotting for KM and survival validation figures.
- Model Validation for survival risk score validation.