## pairwise_logrank_test()


Pairwise log-rank tests for all group pairs with multiple-comparison correction.


Usage

``` python
pairwise_logrank_test(
    surv,
    group,
    *,
    rho=0.0,
    gamma=0.0,
    strata=None,
    correction="holm",
    format=None
)
```


Runs the log-rank test on every pair of groups, then adjusts p-values to control for multiple testing. This answers the question: "Which pairs of groups have significantly different survival?" when you have more than two groups.

After the global log-rank test (via [logrank_test](logrank_test.md#greenwood.logrank_test)) indicates groups differ, this pairwise test reveals which pairs are significantly different and by how much. P-values are adjusted across all pairs using a chosen correction method to control the false discovery rate or family-wise error rate.

**Typical workflow**: First run [logrank_test](logrank_test.md#greenwood.logrank_test) to test overall group differences. If significant, use [pairwise_logrank_test](pairwise_logrank_test.md#greenwood.pairwise_logrank_test) to identify which pairs differ. The adjusted p-values account for testing multiple pairs from the same data.


## Parameters


`surv: Surv`  
A [Surv](Surv.md#greenwood.Surv) response object representing censored survival times. Supports right-censored data or counting-process format. Constructed with [Surv.right()](Surv.md#greenwood.Surv.right), [Surv.counting()](Surv.md#greenwood.Surv.counting), or [Surv.multistate()](Surv.md#greenwood.Surv.multistate).

`group: Any`  
Group labels, one per observation. Can be a Narwhals series, 1-D array, or Python sequence. Must have at least 3 unique levels (to create multiple pairs). Must have the same length as `surv`.

`rho: float = ``0.0`  
Fleming-Harrington weight exponents for the log-rank test (same as [logrank_test](logrank_test.md#greenwood.logrank_test)). Default `(0, 0)` gives standard log-rank; `(1, 0)` gives Peto-Peto (emphasizes early events).

`gamma: float = ``0.0`  
Fleming-Harrington weight exponents for the log-rank test (same as [logrank_test](logrank_test.md#greenwood.logrank_test)). Default `(0, 0)` gives standard log-rank; `(1, 0)` gives Peto-Peto (emphasizes early events).

`strata: Any = None`  
Optional stratifying factor. When provided, each pairwise test is stratified by this factor (computed within each stratum, then combined). Use to control for confounding.

`correction: str = ``"holm"`  
Multiple-comparison adjustment method applied across all pairwise p-values:

- `"holm"` (default): Controls family-wise error rate. Conservative; recommended for small numbers of pairs (fewer than ~10).
- `"bh"`: Benjamini-Hochberg false-discovery rate. Less conservative; recommended for many pairs. Allows more false positives but focuses on their rate.
- `"bonferroni"`: Bonferroni correction. Very conservative; adjusted \\p = p \times m\\, where \\m\\ is the number of pairs.
- `"none"`: No adjustment. Use only if you're testing a single pre-planned pair (though use [logrank_test](logrank_test.md#greenwood.logrank_test) directly in that case).

`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`  
One row per pair of groups with columns:

- `group1`, `group2`: The pair of group labels being compared.
- `statistic`: Chi-square test statistic for the pair.
- `p_value`: Raw (unadjusted) log-rank p-value for the pair.
- `p_adjusted`: Adjusted p-value after multiple-comparison correction. Use this for significance testing (e.g., p_adjusted \< 0.05).


## Details

The number of pairs tested is \\C(k, 2) = k(k-1)/2\\, where \\k\\ is the number of groups. For \\k=3\\, that's 3 pairs; for \\k=5\\, that's 10 pairs. Larger numbers of pairs can reduce power per comparison (wider adjusted confidence intervals), so keep the number of groups reasonable when possible.

The adjustment method affects stringency: Holm controls false discovery more strictly (lower type-I error, higher type-II error), while Benjamini-Hochberg is more permissive (higher type-I error rate overall, but controls the proportion of false discoveries).


## Examples

Test pairwise survival differences among the four cell types in the `veteran` dataset. A global log-rank test first shows that cell types differ overall, but doesn't say which pairs differ:


``` python
import greenwood as gw

vet = gw.load_dataset("veteran", backend="polars")
y = gw.Surv.right(vet["time"], event=vet["status"])
gw.logrank_test(y, group=vet["celltype"])
```


    TestResult(method='Log-rank test', statistic=25.4037, df=3, p_value=1.271e-05)


The pairwise test compares all six pairs of cell types and returns a table with the test statistic, raw p-value, and adjusted p-value for each pair. Pass `format=` to choose the backend (here, Polars); use `p_adjusted` for significance testing:


``` python
pairs = gw.pairwise_logrank_test(y, group=vet["celltype"], format="polars")
pairs
```


shape: (6, 5)

| group1      | group2      | statistic | p_value  | p_adjusted |
|-------------|-------------|-----------|----------|------------|
| str         | str         | f64       | f64      | f64        |
| "adeno"     | "large"     | 17.669322 | 0.000026 | 0.000158   |
| "adeno"     | "smallcell" | 0.096843  | 0.755651 | 0.755651   |
| "adeno"     | "squamous"  | 12.045484 | 0.000519 | 0.002596   |
| "large"     | "smallcell" | 9.370904  | 0.002205 | 0.006614   |
| "large"     | "squamous"  | 0.822594  | 0.364423 | 0.728846   |
| "smallcell" | "squamous"  | 11.573674 | 0.000669 | 0.002676   |


Filter to significant pairs (adjusted p-value \< 0.05). Request `format="pandas"` here so we can use boolean-mask filtering:


``` python
pairs = gw.pairwise_logrank_test(y, group=vet["celltype"], format="pandas")
pairs[pairs["p_adjusted"] < 0.05]
```


|     | group1    | group2    | statistic | p_value  | p_adjusted |
|-----|-----------|-----------|-----------|----------|------------|
| 0   | adeno     | large     | 17.669322 | 0.000026 | 0.000158   |
| 2   | adeno     | squamous  | 12.045484 | 0.000519 | 0.002596   |
| 3   | large     | smallcell | 9.370904  | 0.002205 | 0.006614   |
| 5   | smallcell | squamous  | 11.573674 | 0.000669 | 0.002676   |


Use the Peto-Peto (Wilcoxon) weighting to emphasize early survival differences:


``` python
gw.pairwise_logrank_test(y, group=vet["celltype"], rho=1, format="polars")
```


shape: (6, 5)

| group1      | group2      | statistic | p_value  | p_adjusted |
|-------------|-------------|-----------|----------|------------|
| str         | str         | f64       | f64      | f64        |
| "adeno"     | "large"     | 13.831737 | 0.0002   | 0.0012     |
| "adeno"     | "smallcell" | 0.041861  | 0.837884 | 1.0        |
| "adeno"     | "squamous"  | 6.484905  | 0.010879 | 0.032638   |
| "large"     | "smallcell" | 12.292752 | 0.000455 | 0.002274   |
| "large"     | "squamous"  | 0.021484  | 0.883469 | 1.0        |
| "smallcell" | "squamous"  | 7.887925  | 0.004977 | 0.019906   |


Use Benjamini-Hochberg adjustment (less conservative) if you're interested in which pairs show evidence of differences (false-discovery rate control rather than family-wise error):


``` python
gw.pairwise_logrank_test(y, group=vet["celltype"], correction="bh", format="polars")
```


shape: (6, 5)

| group1      | group2      | statistic | p_value  | p_adjusted |
|-------------|-------------|-----------|----------|------------|
| str         | str         | f64       | f64      | f64        |
| "adeno"     | "large"     | 17.669322 | 0.000026 | 0.000158   |
| "adeno"     | "smallcell" | 0.096843  | 0.755651 | 0.755651   |
| "adeno"     | "squamous"  | 12.045484 | 0.000519 | 0.001338   |
| "large"     | "smallcell" | 9.370904  | 0.002205 | 0.003307   |
| "large"     | "squamous"  | 0.822594  | 0.364423 | 0.437307   |
| "smallcell" | "squamous"  | 11.573674 | 0.000669 | 0.001338   |
