## ZPHResult


Proportional-hazards test results (Grambsch-Therneau).


Usage

``` python
ZPHResult(
    transform,
    per_term,
    global_test,
)
```


A key assumption of the Cox proportional hazards model is that the hazard ratio between any two subjects is constant over time (hence "proportional"). When this assumption is violated (for example, if a treatment effect diminishes over time) the Cox model may produce biased estimates. The Grambsch-Therneau proportional hazards test checks this assumption by testing whether scaled residuals are correlated with time.

[ZPHResult](ZPHResult.md#greenwood.ZPHResult) holds the test results obtained from a fitted Cox model's [cox_zph()](CoxPH.md#greenwood.CoxPH.cox_zph) method. It provides both per-term tests (one for each covariate) and a global test (jointly across all terms). Each test includes a chi-squared test statistic, degrees of freedom, and p-value. Results can be printed, accessed via dictionary keys, or exported to pandas/polars/ pyarrow DataFrames for further analysis or visualization.

The test uses scaled Schoenfeld residuals, which have a known asymptotic distribution under the proportional hazards assumption. Large chi-squared values or small p-values (typically p \< 0.05) suggest violation of the assumption. When the assumption is violated, stratified analysis or time-dependent covariate models may be more appropriate.


## Attributes


`transform: str`  
The transformation applied to time when computing the test (e.g., identity, log, rank).

`per_term: dict[str, dict[str, float]]`  
Dictionary mapping each covariate name to `{chisq, df, p_value}` dict.

`global_test: dict[str, float]`  
Dictionary with `{chisq, df, p_value}` for the joint test across all terms.


## Examples

A [ZPHResult](ZPHResult.md#greenwood.ZPHResult) comes from a fitted model's [cox_zph](CoxPH.md#greenwood.CoxPH.cox_zph) method. Fit a Cox model to the bundled `lung` dataset, run the proportional-hazards test, and print the result:


``` python
import greenwood as gw

lung = gw.load_dataset("lung", backend="polars")
y = gw.Surv.right(lung["time"], event=(lung["status"] == 2))
cox = gw.CoxPH().fit(y, lung[["age", "sex"]])
zph = cox.cox_zph()
zph
```


    ZPHResult(transform='identity', age: p=0.7065, sex: p=0.0992, GLOBAL p=0.2425)


## Methods

| Name | Description |
|----|----|
| [to_frame()](#to_frame) | Return the test table as a DataFrame (one row per term plus GLOBAL). |

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


#### to_frame()


Return the test table as a DataFrame (one row per term plus GLOBAL).


Usage

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


The table contains proportional hazards test statistics for each covariate plus a global test across all terms. One row represents one term in the model.


##### 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 table with columns for term, test statistic, p-value, and other diagnostics. Includes a GLOBAL row.


##### Raises


`ImportError`  
If the requested (or, when auto-detecting, any) DataFrame library is not installed.


##### Examples

Fit a Cox model, run the proportional-hazards test, and export the test 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))
cox = gw.CoxPH().fit(y, lung[["age", "sex"]])
zph = cox.cox_zph()
zph.to_frame(format="polars")
```


shape: (3, 4)

| term     | chisq    | df  | p_value  |
|----------|----------|-----|----------|
| str      | f64      | i64 | f64      |
| "age"    | 0.141748 | 1   | 0.70655  |
| "sex"    | 2.718401 | 1   | 0.099197 |
| "GLOBAL" | 2.833391 | 2   | 0.242514 |


The table shows the proportional hazards assumption test results for each term, with the GLOBAL row testing the overall assumption. Request a different backend with `format=`:


``` python
zph.to_frame(format="pandas")
```


|     | term   | chisq    | df  | p_value  |
|-----|--------|----------|-----|----------|
| 0   | age    | 0.141748 | 1   | 0.706550 |
| 1   | sex    | 2.718401 | 1   | 0.099197 |
| 2   | GLOBAL | 2.833391 | 2   | 0.242514 |
