AFT

Parametric accelerated failure time model.

Usage

Source

AFT(
    dist="weibull",
    *,
    conf_level=0.95,
)

While the Cox proportional hazards model leaves the baseline hazard unspecified, AFT models assume a fully parametric distribution for survival times and model how covariates accelerate or decelerate the “clock” of failure. Specifically, \(\log(T) = \mu + \beta^\top x + \sigma\varepsilon\), where \(T\) is survival time, \(\beta\) are log-time-scale coefficients, \(\sigma\) is a scale parameter, and \(\varepsilon\) follows a specified error distribution (e.g., extreme-value, logistic, normal). This means a unit increase in covariate \(x\) multiplies survival time by \(\exp(\beta)\).

AFT models are useful when you want explicit, interpretable survival time predictions or when the parametric assumptions are reasonable. Unlike Cox models, they require choosing a distributional family (Weibull, exponential, lognormal, or loglogistic). Call fit() with a right-censored Surv response and a design matrix. The model automatically adds an intercept and estimates coefficients (on the log-time scale), the scale parameter, and standard errors via maximum likelihood.

The implementation uses numerical optimization (typically Newton-Raphson) to maximize the likelihood. Coefficients on the log-time scale can be exponentiated to obtain time- acceleration ratios: \(\exp(\beta)\) is the multiplicative effect on median or mean survival. The model also supports prediction of survival probabilities and quantiles at future times given covariate values.

Parameters

dist: str = "weibull"

Error distribution: "weibull" (default), "exponential", "lognormal", or "loglogistic".

conf_level: float = 0.95
Confidence level for coefficient intervals (default is 0.95).

Returns

Fitted estimator
Call fit() to produce a fitted estimator with cached results (coef_, scale_, std_error_, z_, p_value_, conf_low_, conf_high_, loglik_, aic_, bic_), accessible as arrays or exported to DataFrames.

Details

Call fit(surv, covariates) with a right-censored Surv response and a covariate design (a 2-D array or a dataframe). An intercept is added automatically; rows with missing covariates are dropped. Results are exposed as arrays (coef_, scale_, std_error_, z_, p_value_) and as tidy frames via to_frame() (optionally format=) and greenwood.tidy.

Examples

Build a Surv response from the bundled lung dataset and fit a Weibull AFT model with age and sex as covariates. Printing the fitted object reports the coefficients (on the log-time scale), the scale, and the log-likelihood.

import greenwood as gw

lung = gw.load_dataset("lung", backend="polars")
y = gw.Surv.right(lung["time"], event=(lung["status"] == 2))
aft = gw.AFT("weibull").fit(y, lung[["age", "sex"]])
aft
AFT (accelerated failure time model, dist='weibull')

                 coef  se(coef)       z         p
(Intercept)     6.275    0.4814  13.036  7.65e-39
age          -0.01226  0.006957  -1.762    0.0781
sex            0.3821    0.1275   2.997  0.002723

Scale = 0.7541
n = 228, events = 165
Log-likelihood = -1147

Methods

Name Description
fit() Fit the accelerated failure time model to survival data.
predict() Predict survival times, quantiles, or survival probabilities from the AFT model.
to_frame() Return the coefficient table as a DataFrame.

fit()

Fit the accelerated failure time model to survival data.

Usage

Source

fit(surv, covariates, *, data=None)

Fits a parametric accelerated failure time (AFT) model to a right-censored response and covariates. The AFT models the log-survival time as a linear regression on covariates plus a random error from a specified parametric distribution (Weibull, exponential, log-normal, or log-logistic). An intercept is added automatically.

The AFT is a parametric alternative to Cox regression, providing a fully specified survival distribution at the cost of stronger distributional assumptions. Unlike Cox, AFT supports median survival predictions and is naturally interpreted on the log-time scale: a coefficient of 0.1 means the covariate multiplies survival time by \(\exp(0.1)\). Results are stored in the fitted object as coefficient arrays and can be exported to DataFrames.

Parameters
surv: Surv

A right-censored Surv response. Built with Surv.right(). Interval-censored or other response types raise NotImplementedError.

covariates: Any

A dataframe (pandas or polars), a 2-D array, or a formula string (e.g., "age + sex") evaluated against the data argument.

data: Any = None
A dataframe to evaluate the formula string (ignored if covariates is a dataframe or array).
Returns
AFT
The fitted estimator object itself (for method chaining) with cached coefficient arrays (coef_, std_error_, z_, p_value_), scale parameter (scale_), and log-likelihood (loglik_).
Details

The AFT model parameterizes log-survival time as \(\log(T) = X\beta + \sigma\varepsilon\), where \(X\) is the design matrix, \(\beta\) are coefficients, \(\sigma\) is a scale parameter, and \(\varepsilon\) is an error term from the chosen distribution. The survival function is then \(S(t \mid X) = P(T > t \mid X) = G((\log(t) - X\beta) / \sigma)\), where \(G\) is the survival function of the error distribution.

Estimation uses maximum likelihood via numerical optimization. Exponential and Weibull models are nested special cases; log-normal and log-logistic offer different tail behaviors.

Examples

Fit a log-normal AFT model on the bundled lung dataset with age and sex as covariates:

import greenwood as gw

lung = gw.load_dataset("lung", backend="polars")
y = gw.Surv.right(lung["time"], event=(lung["status"] == 2))
aft = gw.AFT(dist="lognormal").fit(y, lung[["age", "sex"]])
aft
AFT (accelerated failure time model, dist='lognormal')

                 coef  se(coef)       z          p
(Intercept)     6.408    0.5929  10.808  3.152e-27
age          -0.02336  0.008388  -2.785   0.005359
sex            0.5193    0.1551   3.347  0.0008175

Scale = 1.053
n = 228, events = 165
Log-likelihood = -1159

Use a formula string with the data argument:

aft_formula = gw.AFT(dist="weibull").fit(y, "age + sex", data=lung)
aft_formula
AFT (accelerated failure time model, dist='weibull')

                 coef  se(coef)       z         p
(Intercept)     6.275    0.4814  13.036  7.65e-39
age          -0.01226  0.006957  -1.762    0.0781
sex            0.3821    0.1275   2.997  0.002723

Scale = 0.7541
n = 228, events = 165
Log-likelihood = -1147

predict()

Predict survival times, quantiles, or survival probabilities from the AFT model.

Usage

Source

predict(
    newdata=None,
    *,
    type="survival",
    times=None,
    p=0.5,
    conditional_after=None,
    format=None
)

Generates predictions from a fitted accelerated failure time model. The AFT is a fully parametric survival model, so predictions require specifying both the predictor values (via newdata) and the type of prediction desired. Pass newdata=None to predict for the training data (fitted subjects).

Three prediction types are available:

  1. Linear predictor (type="lp"): the log-time location \(X\beta\), showing how covariates shift the log-survival time distribution.

  2. Quantile (type="quantile"): predicted survival-time quantiles at specified failure probabilities (e.g., median survival when \(p=0.5\)). Useful for clinical summaries like “50% of subjects with these covariates survive to time X.”

  3. Survival (type="survival"): survival probabilities \(S(t \mid x)\) at specified times, returned as a DataFrame for easy visualization. Optionally condition on already having survived to a landmark time (conditional_after) for landmark-based predictions.

Parameters
newdata: Any = None

Covariate values for prediction. A DataFrame (Pandas or Polars), 2-D array, or None (default). If None, uses the training data (design matrix used at fit time). Must have the same columns/features as the training data.

type: str = "survival"

Prediction type (default "survival"):

  • "lp": Linear predictor \(X\beta\) (log-time location). Returns an array.
  • "quantile": Survival-time quantiles at failure probabilities p. Returns a frame with p column and one column per subject.
  • "survival": Survival probabilities \(S(t \mid x)\) at times in times. Returns a frame with time column and one column per subject (one per query time, one per subject in newdata).
times: Any = None

Query times for type="survival" (ignored for other types). An array-like of floats. If None (default), uses an automatic grid based on the fitted distribution (50 equally spaced times on the log scale, rounded).

p: Any = 0.5

Failure probabilities for type="quantile" (ignored for other types). Can be a scalar (e.g., 0.5 for median) or array-like. Default 0.5 (median). Must be in (0, 1).

conditional_after: Any = None

For type="survival", optionally compute conditional survival: \(P(T > t \mid T > c) = S(t) / S(c)\). Scalar (same conditioning time for all subjects) or array-like (one per subject). Default None (unconditional). Predictions before the landmark time return 1.0.

format: str | None = None
Output format for the returned frame (type="quantile" or "survival"): None (default), "pandas", "polars", or "pyarrow". When None, a backend is auto-detected (Polars, then Pandas, then PyArrow). Ignored for type="lp".
Returns
ndarray or DataFrame
If type="lp": an array of shape (n_subjects,) containing log-time locations. If type="quantile": a DataFrame with columns p (failure probabilities) and subject_1, subject_2, etc. containing survival times at each p. If type="survival": a DataFrame with columns time (query times) and subject_1, subject_2, etc. containing survival probabilities at each time. Column names match the input row index if newdata has a row index.
Details

The AFT model assumes \(\log(T) = X\beta + \sigma\varepsilon\), where \(\varepsilon\) follows a parametric error distribution (Weibull, lognormal, etc.). Predictions are made by evaluating the CDF/survival function of this distribution at covariate-adjusted locations. All predictions respect the fitted distribution and scale parameter.

Predictions assume the model is well-specified. For flexible models, consider parametric bootstrap to quantify uncertainty.

Examples

Fit a Weibull AFT model on the bundled lung dataset, then predict the linear predictor (log-time location) for the first two subjects:

import greenwood as gw

lung = gw.load_dataset("lung", backend="polars")
y = gw.Surv.right(lung["time"], event=(lung["status"] == 2))
aft = gw.AFT("weibull").fit(y, lung[["age", "sex"]])

aft.predict(lung[["age", "sex"]][:2], type="lp")
array([5.74991419, 5.82345873])

Predicted survival-time quantiles for the first two subjects at the lower quartile, median, and upper quartile (a table, so pass format=):

aft.predict(lung[["age", "sex"]][:2], type="quantile", p=[0.25, 0.5, 0.75],
            format="polars")
shape: (3, 3)
psubject_1subject_2
f64f64f64
0.25122.785921132.156509
0.5238.303411256.489886
0.75401.903952432.575842

Read survival probabilities off the fitted curves at chosen times. Here are the estimates at 180 and 365 days for those same two subjects:

aft.predict(lung[["age", "sex"]][:2], type="survival", times=[180, 365],
            format="polars")
shape: (2, 3)
timesubject_1subject_2
f64f64f64
180.00.6201630.648318
365.00.2952110.330653

Predict conditional survival given already having survived to 100 days:

aft.predict(lung[["age", "sex"]][:2], type="survival", times=[180, 365],
            conditional_after=100, format="polars")
shape: (2, 3)
timesubject_1subject_2
f64f64f64
180.00.7720930.790875
365.00.3675330.40336

to_frame()

Return the coefficient table as a DataFrame.

Usage

Source

to_frame(*, format=None)

Exports one row per term, including the intercept, with coefficient estimates, standard errors, Wald statistics, p-values, and confidence limits.

Parameters
format: str | None = None
Output format: None (default), "pandas", "polars", or "pyarrow". When None, a backend is auto-detected (Polars, then Pandas, then PyArrow).
Returns
pandas.DataFrame, polars.DataFrame, or pyarrow.Table
A tidy table with columns term, estimate, std_error, statistic, p_value, conf_low, and conf_high.
Raises
ImportError
If the requested (or, when auto-detecting, any) DataFrame library is not installed.
Examples

Fit a Weibull AFT model on the bundled lung dataset, then export its coefficient table as a Polars frame:

import greenwood as gw

lung = gw.load_dataset("lung", backend="polars")
y = gw.Surv.right(lung["time"], event=(lung["status"] == 2))
aft = gw.AFT("weibull").fit(y, lung[["age", "sex"]])

aft.to_frame(format="polars")
shape: (3, 7)
termestimatestd_errorstatisticp_valueconf_lowconf_high
strf64f64f64f64f64f64
"(Intercept)"6.2748790.48135513.0358597.6500e-395.331447.218318
"age"-0.0122570.006957-1.7617940.078104-0.0258940.001379
"sex"0.3820850.1274732.9973740.0027230.1322420.631927

Request a different backend with format=:

aft.to_frame(format="pandas")
term estimate std_error statistic p_value conf_low conf_high
0 (Intercept) 6.274879 0.481355 13.035859 7.650039e-39 5.331440 7.218318
1 age -0.012257 0.006957 -1.761794 7.810410e-02 -0.025894 0.001379
2 sex 0.382085 0.127473 2.997374 2.723163e-03 0.132242 0.631927