Buckley-James rank-based accelerated failure time regression.
BuckleyJames(
*,
max_iter=200,
tol=1e-06,
n_boot=None,
seed=None,
conf_level=0.95,
)
Fits \log(T) = X\beta + \varepsilon without assuming a distribution for \varepsilon, by alternating Kaplan-Meier-based imputation of censored log-times with ordinary least squares refitting until \beta stabilizes. This is a semiparametric alternative to AFT: it avoids committing to a parametric error distribution, at the cost of an iterative fit, occasional slow or oscillating convergence, and no closed-form standard errors (available here only via optional bootstrap resampling, n_boot=).
When there is no censoring at all, every residual is exact and the algorithm converges in a single step to the ordinary least squares fit of \log(T) on X.
Parameters
max_iter: int = 200
-
Maximum number of Buckley-James iterations (default 200).
tol: float = 1e-06
-
Convergence tolerance on the largest change in any coefficient between iterations (default 1e-6).
n_boot: int | None = None
-
Number of bootstrap resamples for standard errors and confidence intervals. If None (the default), std_error_, conf_low_, and conf_high_ are nan (Buckley-James has no closed-form variance).
seed: int | None = None
-
Random seed for bootstrap resampling (ignored if n_boot is None).
conf_level: float = 0.95
-
Confidence level for bootstrap coefficient intervals (default
0.95).
Returns
Fitted estimator
-
Call
fit() to produce a fitted estimator with cached results (coef_, std_error_, z_, p_value_, conf_low_, conf_high_, n_iter_, converged_, loglik_-free since there is no likelihood), 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, a dataframe, or a formula string with data). An intercept is added automatically.
Examples
import greenwood as gw
# Load data and build a right-censored response
lung = gw.load_dataset("lung", backend="polars")
y = gw.Surv.right(lung["time"], event=(lung["status"] == 2))
# Fit a Buckley-James model
bj = gw.BuckleyJames().fit(y, lung[["age", "sex"]])
bj
BuckleyJames (rank-based accelerated failure time regression)
coef se(coef) z p
(Intercept) 6.379 NA NA NA
age -0.02306 NA NA NA
sex 0.4912 NA NA NA
n = 228, events = 165
iterations = 13, converged = True
Methods
|
Name
|
Description
|
|
fit()
|
Fit the Buckley-James model to survival data.
|
|
predict()
|
Predict the linear predictor or survival probabilities from the fitted model.
|
|
to_frame()
|
Return the coefficient table as a DataFrame.
|
fit()
Fit the Buckley-James model to survival data.
fit(
surv,
covariates,
*,
data=None,
)
Parameters
surv: Surv
-
A right-censored Surv response. Built with Surv.right().
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
BuckleyJames
-
The fitted estimator itself (for method chaining) with cached coefficient arrays (
coef_, std_error_, z_, p_value_) and convergence diagnostics (n_iter_, converged_).
Examples
import greenwood as gw
lung = gw.load_dataset("lung", backend="polars")
y = gw.Surv.right(lung["time"], event=(lung["status"] == 2))
bj = gw.BuckleyJames().fit(y, lung[["age", "sex"]])
bj
BuckleyJames (rank-based accelerated failure time regression)
coef se(coef) z p
(Intercept) 6.379 NA NA NA
age -0.02306 NA NA NA
sex 0.4912 NA NA NA
n = 228, events = 165
iterations = 13, converged = True
predict()
Predict the linear predictor or survival probabilities from the fitted model.
predict(
newdata=None,
*,
type="survival",
times=None,
format=None,
)
Parameters
newdata: Any = None
-
Covariate values for prediction. A DataFrame (Pandas or Polars), 2-D array, or None (the default, uses the training data).
type: str = "survival"
-
"survival" (default): survival probabilities S(t \mid x) at times, evaluated by shifting the fitted residual Kaplan-Meier curve by each subject’s linear predictor. "lp": the linear predictor X\beta (log-time location) only; returns an array.
times: Any = None
-
Query times for type="survival" (ignored for "lp"). Required for "survival".
format: str | None = None
-
Output format for
type="survival": None (auto-detect), "pandas", "polars", or "pyarrow". Ignored for type="lp" (which always returns an array).
Returns
ndarray or DataFrame
-
An array of shape
(n_subjects,) for type="lp". A DataFrame with a time column and one column per subject for type="survival".
Details
Because Buckley-James makes no distributional assumption, survival predictions reuse the empirical Kaplan-Meier curve of the fitted residuals rather than a closed-form survival function: S(t \mid x) = \hat{S}_{\text{resid}}(\log t - x^\top\hat\beta). This curve is only as reliable as the residual sample size and is not smooth like AFT’s or RoystonParmar’s predictions.
Examples
import greenwood as gw
lung = gw.load_dataset("lung", backend="polars")
y = gw.Surv.right(lung["time"], event=(lung["status"] == 2))
bj = gw.BuckleyJames().fit(y, lung[["age", "sex"]])
bj.predict(lung[["age", "sex"]][:2], type="survival", times=[180, 365, 730],
format="polars")
shape: (3, 3)| time | subject_1 | subject_2 |
|---|
| f64 | f64 | f64 |
| 180.0 | 0.540495 | 0.604915 |
| 365.0 | 0.240099 | 0.278609 |
| 730.0 | 0.026538 | 0.059712 |
to_frame()
Return the coefficient table as a DataFrame.
to_frame(
*,
format=None,
)
Exports one row per term, including the intercept. std_error, p_value, conf_low, and conf_high are nan unless the model was fit with n_boot=.
Parameters
format: str | None = None
-
Output format:
None (default), "pandas", "polars", or "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.
Examples
import greenwood as gw
lung = gw.load_dataset("lung", backend="polars")
y = gw.Surv.right(lung["time"], event=(lung["status"] == 2))
bj = gw.BuckleyJames(n_boot=200, seed=0).fit(y, lung[["age", "sex"]])
bj.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.379093 | 0.562108 | 11.348514 | 7.5433e-30 | 5.277381 | 7.480805 |
| "age" | -0.023064 | 0.009091 | -2.537153 | 0.011176 | -0.040881 | -0.005247 |
| "sex" | 0.491199 | 0.150234 | 3.269557 | 0.001077 | 0.196746 | 0.785653 |