# PiecewiseExponential


Piecewise exponential survival model.


Usage

``` python
PiecewiseExponential(
    *,
    breaks=None,
    knot_strategy="aic",
    max_knots=10,
    conf_level=0.95,
)
```


Assumes the hazard is constant within each time interval but can differ between intervals. Covariates enter multiplicatively (proportional hazards within each interval). The model is fit by maximum likelihood, equivalent to a Poisson GLM on the interval-expanded dataset with `log(exposure)` as offset.

This sits between the fully nonparametric Cox model and the fully parametric AFT: it estimates the baseline hazard but restricts it to a step function. Fewer intervals give a smoother hazard. More intervals approach the Cox model's flexibility.


## Parameters


`breaks: list[float] | tuple[float, …] | None = None`  
Time points at which the hazard is allowed to change. For example, `breaks=[180, 365]` creates three intervals: `(0, 180]`, `(180, 365]`, and `(365, inf]`. When `None` (default), knots are chosen automatically by minimizing AIC.

`knot_strategy: str = ``"aic"`  
Strategy for automatic knot selection when `breaks` is `None`. `"aic"` (default) minimizes the Akaike information criterion. `"bic"` minimizes the Bayesian information criterion. Ignored when `breaks` is provided.

`max_knots: int = ``10`  
Maximum number of interior knots to consider during automatic selection (the default is `10`). Ignored when `breaks` is provided.

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


## Examples


``` python
import greenwood as gw

lung = gw.load_dataset("lung", backend="polars")
y = gw.Surv.right(lung["time"], event=(lung["status"] == 2))

pem = gw.PiecewiseExponential().fit(y, lung[["age", "sex"]])
pem
```


    PiecewiseExponential (1 intervals)

            coef  se(coef)       z         p
    age  0.01562  0.009106   1.715    0.0863
    sex  -0.4809    0.1671  -2.878  0.003999

    Intervals:
      (0, inf]

    n = 228, events = 165
    Log-likelihood = -281.9
    AIC = 569.7


With manual break points:


``` python
pem_manual = gw.PiecewiseExponential(breaks=[180, 365]).fit(y, lung[["age", "sex"]])
pem_manual.to_frame(format="polars")
```


shape: (2, 7)

| term  | estimate  | std_error | statistic | p_value  | conf_low  | conf_high |
|-------|-----------|-----------|-----------|----------|-----------|-----------|
| str   | f64       | f64       | f64       | f64      | f64       | f64       |
| "age" | 0.01579   | 0.009175  | 1.720994  | 0.085252 | -0.002193 | 0.033773  |
| "sex" | -0.507743 | 0.167172  | -3.037246 | 0.002388 | -0.835394 | -0.180091 |


## Methods

| Name | Description |
|----|----|
| [baseline_hazard()](#baseline_hazard) | Return the piecewise-constant baseline hazard as a DataFrame. |
| [fit()](#fit) | Fit the piecewise exponential model. |
| [predict()](#predict) | Predict survival or cumulative hazard for new subjects. |
| [to_frame()](#to_frame) | Return the coefficient table as a DataFrame. |

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


### baseline_hazard()


Return the piecewise-constant baseline hazard as a DataFrame.


Usage

``` python
baseline_hazard(
    *,
    format=None,
)
```


Each row gives the interval boundaries and the constant hazard rate within that interval.


#### Parameters


`format: str | None = None`  
Output format: `None`, `"pandas"`, `"polars"`, or `"pyarrow"`.


#### Returns


`pandas.DataFrame, polars.DataFrame, or pyarrow.Table`  


#### Examples


``` python
import greenwood as gw

lung = gw.load_dataset("lung", backend="polars")
y = gw.Surv.right(lung["time"], event=(lung["status"] == 2))
pem = gw.PiecewiseExponential(breaks=[180, 365]).fit(y, lung[["age", "sex"]])
pem.baseline_hazard(format="polars")
```


shape: (3, 4)

| start | stop  | hazard   | log_hazard |
|-------|-------|----------|------------|
| f64   | f64   | f64      | f64        |
| 0.0   | 180.0 | 0.001299 | -6.646077  |
| 180.0 | 365.0 | 0.002215 | -6.112317  |
| 365.0 | inf   | 0.002392 | -6.035744  |


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


### fit()


Fit the piecewise exponential model.


Usage

``` python
fit(
    surv,
    covariates,
    *,
    data=None,
    max_iter=50,
    tol=1e-09,
)
```


#### Parameters


`surv: Surv`  
A right-censored or counting-process [Surv](Surv.md#greenwood.Surv) response.

`covariates: Any`  
A dataframe (pandas or polars), a 2-D array, or a formula string.

`data: Any = None`  
A dataframe for formula evaluation (ignored otherwise).

`max_iter: int = ``50`  
Maximum IRLS iterations (default 50).

`tol: float = ``1e-09`  
Convergence tolerance on the step size (default 1e-9).


#### Returns


`PiecewiseExponential`  
The fitted model (for method chaining).


#### Examples


``` python
import greenwood as gw

lung = gw.load_dataset("lung", backend="polars")
y = gw.Surv.right(lung["time"], event=(lung["status"] == 2))
pem = gw.PiecewiseExponential(breaks=[180, 365]).fit(y, lung[["age", "sex"]])
pem.to_frame(format="polars")
```


shape: (2, 7)

| term  | estimate  | std_error | statistic | p_value  | conf_low  | conf_high |
|-------|-----------|-----------|-----------|----------|-----------|-----------|
| str   | f64       | f64       | f64       | f64      | f64       | f64       |
| "age" | 0.01579   | 0.009175  | 1.720994  | 0.085252 | -0.002193 | 0.033773  |
| "sex" | -0.507743 | 0.167172  | -3.037246 | 0.002388 | -0.835394 | -0.180091 |


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


### predict()


Predict survival or cumulative hazard for new subjects.


Usage

``` python
predict(
    newdata=None,
    *,
    type="survival",
    times=None,
    format=None,
)
```


#### Parameters


`newdata: Any = None`  
Covariate values for new subjects. If `None`, uses the training data.

`type: str = ``"survival"`  
`"survival"` (default), `"cumhaz"` (cumulative hazard), `"lp"` (linear predictor), or `"risk"` (exp of linear predictor).

`times: list[float] | Array | None = None`  
Times at which to evaluate survival or cumulative hazard. Required for `"survival"` and `"cumhaz"`.

`format: str | None = None`  
Output format for tabular results.


#### Returns


`Array or DataFrame`  
For `"lp"` and `"risk"`, a 1-D array. For `"survival"` and `"cumhaz"`, a DataFrame with one column per subject and one row per time.


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


### to_frame()


Return the coefficient table as a DataFrame.


Usage

``` python
to_frame(
    *,
    format=None,
)
```


One row per covariate (excluding interval parameters). Includes coefficient estimates, standard errors, Wald statistics, p-values, and confidence limits.


#### Parameters


`format: str | None = None`  
Output format: `None`, `"pandas"`, `"polars"`, or `"pyarrow"`.


#### Returns


`pandas.DataFrame, polars.DataFrame, or pyarrow.Table`  


#### Examples


``` python
import greenwood as gw

lung = gw.load_dataset("lung", backend="polars")
y = gw.Surv.right(lung["time"], event=(lung["status"] == 2))
pem = gw.PiecewiseExponential(breaks=[180, 365]).fit(y, lung[["age", "sex"]])
pem.to_frame(format="polars")
```


shape: (2, 7)

| term  | estimate  | std_error | statistic | p_value  | conf_low  | conf_high |
|-------|-----------|-----------|-----------|----------|-----------|-----------|
| str   | f64       | f64       | f64       | f64      | f64       | f64       |
| "age" | 0.01579   | 0.009175  | 1.720994  | 0.085252 | -0.002193 | 0.033773  |
| "sex" | -0.507743 | 0.167172  | -3.037246 | 0.002388 | -0.835394 | -0.180091 |
