## AFT


Parametric accelerated failure time model.


Usage

``` python
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()](AFT.md#greenwood.AFT.fit) with a right-censored [Surv](Surv.md#greenwood.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()](AFT.md#greenwood.AFT.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](Surv.md#greenwood.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()](AFT.md#greenwood.AFT.to_frame) (optionally `format=`) and `greenwood.tidy`.


## Examples

Build a [Surv](Surv.md#greenwood.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.


``` python
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) | Fit the accelerated failure time model to survival data. |
| [predict()](#predict) | Predict survival times, quantiles, or survival probabilities from the AFT model. |
| [to_frame()](#to_frame) | Return the coefficient table as a DataFrame. |

------------------------------------------------------------------------


#### fit()


Fit the accelerated failure time model to survival data.


Usage

``` python
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](Surv.md#greenwood.Surv) response. Built with [Surv.right()](Surv.md#greenwood.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:


``` python
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:


``` python
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

``` python
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:


``` python
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=`):


``` python
aft.predict(lung[["age", "sex"]][:2], type="quantile", p=[0.25, 0.5, 0.75],
            format="polars")
```


shape: (3, 3)

| p    | subject_1  | subject_2  |
|------|------------|------------|
| f64  | f64        | f64        |
| 0.25 | 122.785921 | 132.156509 |
| 0.5  | 238.303411 | 256.489886 |
| 0.75 | 401.903952 | 432.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:


``` python
aft.predict(lung[["age", "sex"]][:2], type="survival", times=[180, 365],
            format="polars")
```


shape: (2, 3)

| time  | subject_1 | subject_2 |
|-------|-----------|-----------|
| f64   | f64       | f64       |
| 180.0 | 0.620163  | 0.648318  |
| 365.0 | 0.295211  | 0.330653  |


Predict conditional survival given already having survived to 100 days:


``` python
aft.predict(lung[["age", "sex"]][:2], type="survival", times=[180, 365],
            conditional_after=100, format="polars")
```


shape: (2, 3)

| time  | subject_1 | subject_2 |
|-------|-----------|-----------|
| f64   | f64       | f64       |
| 180.0 | 0.772093  | 0.790875  |
| 365.0 | 0.367533  | 0.40336   |


------------------------------------------------------------------------


#### to_frame()


Return the coefficient table as a DataFrame.


Usage

``` python
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:


``` python
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)

| term          | estimate  | std_error | statistic | p_value    | conf_low  | conf_high |
|---------------|-----------|-----------|-----------|------------|-----------|-----------|
| str           | f64       | f64       | f64       | f64        | f64       | f64       |
| "(Intercept)" | 6.274879  | 0.481355  | 13.035859 | 7.6500e-39 | 5.33144   | 7.218318  |
| "age"         | -0.012257 | 0.006957  | -1.761794 | 0.078104   | -0.025894 | 0.001379  |
| "sex"         | 0.382085  | 0.127473  | 2.997374  | 0.002723   | 0.132242  | 0.631927  |


Request a different backend with `format=`:


``` python
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  |
