RAPID Survival (Cox) — Tutorial Notebook¶
This notebook walks you through using the RAPID Survival Cox pipeline from end to end.
By the end of this tutorial, you will know how to:
- Instantiate a survival model using the RAPID Pipeline Factory.
- Fit the model using duration and binary event variables.
- Interpret assumption tests, performance metrics, and domain-specific plots like Kaplan-Meier curves.
- Use custom formulas to test interactions between variables.
This pipeline is designed for epidemiological and clinical research, focusing on "time-to-event" outcomes (e.g., time to discharge, time to death, or time to recovery).
1. Setup and Installation¶
Before starting, ensure the RAPID package is installed in your environment.
pip install "isaric3.0-rapid/dist/isaric-0.1.0-py3-none-any.whl
Then import the factory and any other dependencies you need.
!pip install gdown
import gdown
#url = "https://drive.google.com/uc?export=download&id=1OpP-r3YzKGTKYHijHE-yAgFNYLG-5vj-"
url = "https://drive.google.com/uc?export=download&id=1gBMCRwaHcjJ4e4rH1ggLUF4kKCr16mck"
gdown.download(url, 'isaric-0.1.0-py3-none-any.whl', quiet=False)
!pip install isaric-0.1.0-py3-none-any.whl
import pandas as pd
import warnings
from isaric.pipelines.pipeline_factory import RAPID_PipelineFactory
# Ignore standard runtime warnings during optimization steps
warnings.filterwarnings('ignore', category=RuntimeWarning)
2. The Pipeline Factory¶
All RAPID pipelines are created through the RAPID_PipelineFactory. This provides a single, consistent entry point for creating any supported pipeline by name, ensuring your code remains clean and standardized.
# Instantiate the factory
factory = RAPID_PipelineFactory()
# See all pipelines available out of the box
print(f"Available pipelines: {factory.available()}")
3. Preparing Your Data¶
Survival models require two mandatory outcome columns:
- Duration: A continuous variable representing the follow-up time.
- Event: A binary indicator (1 if the event occurred, 0 for censoring/not occurred).
Below is an example using a simulated clinical dataset.
import numpy as np
np.random.seed(42)
n = 500
df_survival = pd.DataFrame({
"days_to_event": np.random.exponential(scale=30, size=n),
"outcome_death": np.random.choice([0, 1], size=n, p=[0.7, 0.3]),
"age": np.random.randint(18, 90, size=n),
"sex": np.random.choice([0, 1], size=n),
"comorbidity": np.random.choice([0, 1], size=n, p=[0.6, 0.4])
})
df_survival.head()
4. Creating and Fitting the Model¶
Pass your DataFrame, the time variable (duration_var), the event variable (dependent_var), and your predictors to factory.create().
model = factory.create(
"survival",
data=df_survival,
duration_var="days_to_event",
dependent_var="outcome_death",
independent_vars=["age", "sex", "comorbidity"]
)
Fitting with Labels and Penalization¶
The .fit() method estimates the Hazard Ratios. You can pass a labels dictionary for clean reporting and a penalizer for L2 regularization to handle multicollinearity or small sample sizes.
model.fit(
labels={
"age": "Age (years)",
"sex": "Sex (Male=1)",
"comorbidity": "Comorbidity Present"
},
penalizer=0.1
)
5. Results Table (Hazard Ratios)¶
In survival analysis, we interpret the Hazard Ratio (HR) rather than linear coefficients.
model.summary_df
How to interpret:¶
- Hazard Ratio (HR) > 1: Indicates an increased risk of the event (e.g., higher probability of death).
- Hazard Ratio (HR) < 1: Indicates a protective factor (lower risk of the event).
- 95% Confidence Interval: If the interval does not include 1.0, the association is statistically significant at $p < 0.05$.
6. Assumption Tests¶
The core assumption of the Cox model is Proportional Hazards.
model.summary(assumptions=True)
6.1 Schoenfeld Residuals Test¶
This test checks if the hazards remain constant over time.
- $p > 0.05$: The assumption is met.
- $p \leq 0.05$: The assumption is violated; the effect of that variable changes over time.
7. Diagnostic and Visualization Plots¶
Survival analysis requires specific plots to understand the dynamics of the event.
7.1 Forest Plot¶
Displays Hazard Ratios and their 95% CIs. It is ideal for comparing the relative impact of different predictors at a glance.
model.summary(plots=["forest_plot"])
7.2 Kaplan-Meier / Survival Curve¶
Shows the probability of "surviving" (not experiencing the event) over time.
model.summary(plots=["survival_curve"])
---
## 8. Performance Metrics
Unlike GLM, survival performance is primarily measured using the **C-index (Concordance Index)**.
```python
model.summary(performance=True)
```
### Concordance Index (C-index)
Measures the discriminative power of the model.
* **0.5**: No better than random chance.
* **0.7**: Good discrimination.
* **1.0**: Perfect discrimination.
---
## 9. Advanced Use: Custom Formulas
You can test complex relationships, such as interactions (e.g., does the effect of sex depend on age?), using formula syntax.
```python
# Formula: time + event ~ predictors
custom_formula = "days_to_event + outcome_death ~ age * sex + comorbidity"
print("Fitting Model with Formula...")
model.fit(formula=custom_formula, penalizer=0.1)
# Martingale residuals help check the linearity of continuous variables
model.summary(plots=['martingale'])
```
---
## Summary Table
| Step | Action |
| --- | --- |
| **Factory** | Created via `RAPID_PipelineFactory`. |
| **Data Prep** | Defined `duration_var` (time) and `dependent_var` (event). |
| **Assumptions** | Tested Proportional Hazards via Schoenfeld residuals. |
| **Performance** | Evaluated via C-Index and Log-Likelihood. |
| **Plots** | Forest plots, Kaplan-Meier curves, and Martingale residuals. |
Would you like me to add a section on **Cross-Validation** for the C-index, similar to how the GLM tutorial handles MSE?
8. Performance Metrics¶
Unlike GLM, survival performance is primarily measured using the C-index (Concordance Index).
model.summary(performance=True)
Concordance Index (C-index)¶
Measures the discriminative power of the model.
- 0.5: No better than random chance.
- 0.7: Good discrimination.
- 1.0: Perfect discrimination.
9. Advanced Use: Custom Formulas¶
You can test complex relationships, such as interactions (e.g., does the effect of sex depend on age?), using formula syntax.
# Formula: time + event ~ predictors
custom_formula = "days_to_event + outcome_death ~ age * sex + comorbidity"
print("Fitting Model with Formula...")
model.fit(formula=custom_formula, penalizer=0.1)
# Martingale residuals help check the linearity of continuous variables
model.summary(plots=['martingale'])
Summary Table¶
| Step | Action |
|---|---|
| Factory | Created via RAPID_PipelineFactory. |
| Data Prep | Defined duration_var (time) and dependent_var (event). |
| Assumptions | Tested Proportional Hazards via Schoenfeld residuals. |
| Performance | Evaluated via C-Index and Log-Likelihood. |
| Plots | Forest plots, Kaplan-Meier curves, and Martingale residuals. |
Would you like me to add a section on Cross-Validation for the C-index, similar to how the GLM tutorial handles MSE?
## 2. The Pipeline Factory
All RAPID pipelines are created through the **`RAPID_PipelineFactory`**. This provides a single, consistent entry point for creating any supported pipeline by name, ensuring your code remains clean and standardized.
```python
# Instantiate the factory
factory = RAPID_PipelineFactory()
# See all pipelines available out of the box
print(f"Available pipelines: {factory.available()}")
```
## 3. Preparing Your Data
Survival models require two mandatory outcome columns:
* **Duration**: A continuous variable representing the follow-up time.
* **Event**: A binary indicator (1 if the event occurred, 0 for censoring/not occurred).
Below is an example using a simulated clinical dataset.
```python
np.random.seed(42)
n = 500
df_survival = pd.DataFrame({
"days_to_event": np.random.exponential(scale=30, size=n),
"outcome_death": np.random.choice([0, 1], size=n, p=[0.7, 0.3]),
"age": np.random.randint(18, 90, size=n),
"sex": np.random.choice([0, 1], size=n),
"comorbidity": np.random.choice([0, 1], size=n, p=[0.6, 0.4])
})
df_survival.head()
```
## 4. Creating and Fitting the Model
Pass your DataFrame, the time variable (`duration_var`), the event variable (`dependent_var`), and your predictors to `factory.create()`.
```python
model = factory.create(
"survival",
data=df_survival,
duration_var="days_to_event",
dependent_var="outcome_death",
independent_vars=["age", "sex", "comorbidity"]
)
```
### Fitting with Labels and Penalization
The `.fit()` method estimates the Hazard Ratios. You can pass a `labels` dictionary for clean reporting and a `penalizer` for L2 regularization to handle multicollinearity or small sample sizes.
```python
model.fit(
labels={
"age": "Age (years)",
"sex": "Sex (Male=1)",
"comorbidity": "Comorbidity Present"
},
penalizer=0.1
)
```
---
## 5. Results Table (Hazard Ratios)
In survival analysis, we interpret the **Hazard Ratio (HR)** rather than linear coefficients.
```python
model.summary_df
```
### How to interpret:
* **Hazard Ratio (HR) > 1**: Indicates an increased risk of the event (e.g., higher probability of death).
* **Hazard Ratio (HR) < 1**: Indicates a protective factor (lower risk of the event).
* **95% Confidence Interval**: If the interval **does not include 1.0**, the association is statistically significant at $p < 0.05$.
---
## 6. Assumption Tests
The core assumption of the Cox model is **Proportional Hazards**.
```python
model.summary(assumptions=True)
```
### 6.1 Schoenfeld Residuals Test
This test checks if the hazards remain constant over time.
* **$p > 0.05$**: The assumption is met.
* **$p \leq 0.05$**: The assumption is violated; the effect of that variable changes over time.
---
## 7. Diagnostic and Visualization Plots
Survival analysis requires specific plots to understand the dynamics of the event.
### 7.1 Forest Plot
Displays Hazard Ratios and their 95% CIs. It is ideal for comparing the relative impact of different predictors at a glance.
```python
model.summary(plots=["forest_plot"])
```
### 7.2 Kaplan-Meier / Survival Curve
Shows the probability of "surviving" (not experiencing the event) over time.
```python
model.summary(plots=["survival_curve"])
```
---
## 8. Performance Metrics
Unlike GLM, survival performance is primarily measured using the **C-index (Concordance Index)**.
```python
model.summary(performance=True)
```
### Concordance Index (C-index)
Measures the discriminative power of the model.
* **0.5**: No better than random chance.
* **0.7**: Good discrimination.
* **1.0**: Perfect discrimination.
---
## 9. Advanced Use: Custom Formulas
You can test complex relationships, such as interactions (e.g., does the effect of sex depend on age?), using formula syntax.
```python
# Formula: time + event ~ predictors
custom_formula = "days_to_event + outcome_death ~ age * sex + comorbidity"
print("Fitting Model with Formula...")
model.fit(formula=custom_formula, penalizer=0.1)
# Martingale residuals help check the linearity of continuous variables
model.summary(plots=['martingale'])
```
---
## Summary Table
| Step | Action |
| --- | --- |
| **Factory** | Created via `RAPID_PipelineFactory`. |
| **Data Prep** | Defined `duration_var` (time) and `dependent_var` (event). |
| **Assumptions** | Tested Proportional Hazards via Schoenfeld residuals. |
| **Performance** | Evaluated via C-Index and Log-Likelihood. |
| **Plots** | Forest plots, Kaplan-Meier curves, and Martingale residuals. |
Would you like me to add a section on **Cross-Validation** for the C-index, similar to how the GLM tutorial handles MSE?