311 lines
9.4 KiB
Markdown
311 lines
9.4 KiB
Markdown
---
|
|
title: "Examples: Statistical Modeling Skill"
|
|
task: ""
|
|
lineage_type: import
|
|
upstream_source: https://github.com/mims-harvard/ToolUniverse/blob/e2520a96/skills/tooluniverse-statistical-modeling/EXAMPLES.md
|
|
upstream_sha: e2520a96
|
|
imported_at: 2026-06-26
|
|
prompt_class: prompt
|
|
upstream_changes: accepted
|
|
author: upstream
|
|
validated: false
|
|
---
|
|
|
|
# Examples: Statistical Modeling Skill
|
|
|
|
## Example 1: Ordinal Logistic Regression - BCG Vaccination and COVID-19 Severity
|
|
|
|
**Pattern**: "What is the odds ratio of COVID-19 severity associated with BCG vaccination in ordinal logistic regression?"
|
|
|
|
### Setup
|
|
|
|
```python
|
|
import pandas as pd
|
|
import numpy as np
|
|
from statsmodels.miscmodels.ordinal_model import OrderedModel
|
|
|
|
# Example dataset: COVID-19 patients with severity and vaccination status
|
|
data = {
|
|
'severity': ['Mild']*40 + ['Moderate']*35 + ['Severe']*25,
|
|
'bcg_vaccinated': [1]*25 + [0]*15 + [1]*10 + [0]*25 + [1]*5 + [0]*20,
|
|
'age': np.random.normal(55, 15, 100).astype(int),
|
|
'male': np.random.binomial(1, 0.55, 100),
|
|
}
|
|
df = pd.DataFrame(data)
|
|
```
|
|
|
|
### Analysis
|
|
|
|
```python
|
|
# Step 1: Define ordinal outcome
|
|
severity_order = ['Mild', 'Moderate', 'Severe']
|
|
df['severity'] = pd.Categorical(df['severity'], categories=severity_order, ordered=True)
|
|
y = df['severity'].cat.codes
|
|
|
|
# Step 2: Prepare predictors
|
|
X = df[['bcg_vaccinated', 'age', 'male']].astype(float)
|
|
|
|
# Step 3: Fit ordinal logistic regression
|
|
model = OrderedModel(y, X, distr='logit')
|
|
fit = model.fit(method='bfgs', disp=0)
|
|
|
|
# Step 4: Extract odds ratio
|
|
bcg_coef = fit.params['bcg_vaccinated']
|
|
bcg_or = np.exp(bcg_coef)
|
|
bcg_ci = np.exp(fit.conf_int().loc['bcg_vaccinated'])
|
|
|
|
print(f"BCG Vaccination Odds Ratio: {bcg_or:.4f}")
|
|
print(f"95% CI: ({bcg_ci.iloc[0]:.4f}, {bcg_ci.iloc[1]:.4f})")
|
|
print(f"P-value: {fit.pvalues['bcg_vaccinated']:.6f}")
|
|
```
|
|
|
|
### Interpretation
|
|
|
|
- OR < 1: BCG vaccination is associated with lower odds of being in a higher severity category
|
|
- OR > 1: BCG vaccination is associated with higher odds of being in a higher severity category
|
|
- The proportional odds assumption means this OR applies uniformly across all severity cut points
|
|
|
|
---
|
|
|
|
## Example 2: Binary Logistic Regression - Treatment Response
|
|
|
|
**Pattern**: "What is the odds ratio of treatment response associated with biomarker positivity?"
|
|
|
|
```python
|
|
import statsmodels.formula.api as smf
|
|
import numpy as np
|
|
|
|
# Fit logistic regression
|
|
model = smf.logit('response ~ biomarker_positive + age + stage', data=df).fit(disp=0)
|
|
|
|
# Odds ratios with confidence intervals
|
|
or_table = np.exp(model.params)
|
|
ci = np.exp(model.conf_int())
|
|
ci.columns = ['OR_lower', 'OR_upper']
|
|
results = pd.DataFrame({
|
|
'OR': or_table,
|
|
'CI_lower': ci['OR_lower'],
|
|
'CI_upper': ci['OR_upper'],
|
|
'p_value': model.pvalues
|
|
})
|
|
print(results.round(4))
|
|
```
|
|
|
|
---
|
|
|
|
## Example 3: Percentage Reduction in Odds Ratio (Confounding Assessment)
|
|
|
|
**Pattern**: "What is the percentage reduction in odds ratio for higher severity after adjusting for confounders?"
|
|
|
|
```python
|
|
import statsmodels.formula.api as smf
|
|
import numpy as np
|
|
|
|
# Unadjusted (crude) model
|
|
model_crude = smf.logit('outcome ~ exposure', data=df).fit(disp=0)
|
|
or_crude = np.exp(model_crude.params['exposure'])
|
|
|
|
# Adjusted model (with confounders)
|
|
model_adj = smf.logit('outcome ~ exposure + age + sex + comorbidity', data=df).fit(disp=0)
|
|
or_adj = np.exp(model_adj.params['exposure'])
|
|
|
|
# Calculate percentage reduction
|
|
pct_reduction = ((or_crude - or_adj) / or_crude) * 100
|
|
|
|
print(f"Crude OR: {or_crude:.4f}")
|
|
print(f"Adjusted OR: {or_adj:.4f}")
|
|
print(f"Percentage reduction in OR: {pct_reduction:.1f}%")
|
|
print(f"Interpretation: Adjusting for confounders reduced the OR by {pct_reduction:.1f}%,")
|
|
print(f"suggesting {'substantial' if abs(pct_reduction) > 10 else 'minimal'} confounding.")
|
|
```
|
|
|
|
---
|
|
|
|
## Example 4: Interaction Effect in Ordered Logit
|
|
|
|
**Pattern**: "What is the odds ratio associated with patient interaction using ordered logit model?"
|
|
|
|
```python
|
|
import pandas as pd
|
|
import numpy as np
|
|
from statsmodels.miscmodels.ordinal_model import OrderedModel
|
|
|
|
# When "patient interaction" refers to interaction between patient-level variables:
|
|
# e.g., interaction between treatment and comorbidity
|
|
|
|
# Prepare data with interaction term
|
|
df['treatment_x_comorbidity'] = df['treatment'] * df['comorbidity']
|
|
|
|
# Predictors including interaction
|
|
X = df[['treatment', 'comorbidity', 'treatment_x_comorbidity', 'age']].astype(float)
|
|
|
|
# Ordinal outcome
|
|
y = df['severity'].cat.codes
|
|
|
|
model = OrderedModel(y, X, distr='logit')
|
|
fit = model.fit(method='bfgs', disp=0)
|
|
|
|
# Interaction OR
|
|
interaction_or = np.exp(fit.params['treatment_x_comorbidity'])
|
|
print(f"Interaction OR: {interaction_or:.4f}")
|
|
print(f"P-value: {fit.pvalues['treatment_x_comorbidity']:.6f}")
|
|
```
|
|
|
|
---
|
|
|
|
## Example 5: Cox Proportional Hazards Survival Analysis
|
|
|
|
**Pattern**: "What is the hazard ratio for drug treatment in a Cox regression model?"
|
|
|
|
```python
|
|
import pandas as pd
|
|
from lifelines import CoxPHFitter
|
|
|
|
# Load survival data
|
|
df = pd.read_csv('survival_data.csv')
|
|
# Columns: time, event, treatment, age, stage, biomarker
|
|
|
|
# Fit Cox model
|
|
cph = CoxPHFitter()
|
|
cph.fit(df[['time', 'event', 'treatment', 'age', 'stage']],
|
|
duration_col='time', event_col='event')
|
|
|
|
# Extract results
|
|
summary = cph.summary
|
|
for covar in summary.index:
|
|
hr = summary.loc[covar, 'exp(coef)']
|
|
ci_low = summary.loc[covar, 'exp(coef) lower 95%']
|
|
ci_up = summary.loc[covar, 'exp(coef) upper 95%']
|
|
p = summary.loc[covar, 'p']
|
|
print(f"{covar}: HR={hr:.4f} (95% CI: {ci_low:.4f}-{ci_up:.4f}), p={p:.6f}")
|
|
|
|
print(f"\nConcordance index: {cph.concordance_index_:.4f}")
|
|
|
|
# Check proportional hazards assumption
|
|
cph.check_assumptions(df[['time', 'event', 'treatment', 'age', 'stage']],
|
|
p_value_threshold=0.05, show_plots=False)
|
|
```
|
|
|
|
---
|
|
|
|
## Example 6: Kaplan-Meier with Group Comparison
|
|
|
|
**Pattern**: "What is the median survival time for treatment vs control group?"
|
|
|
|
```python
|
|
import pandas as pd
|
|
from lifelines import KaplanMeierFitter
|
|
from lifelines.statistics import logrank_test
|
|
|
|
df = pd.read_csv('survival_data.csv')
|
|
|
|
kmf = KaplanMeierFitter()
|
|
|
|
# Fit for each group
|
|
for group_name in ['Treatment', 'Control']:
|
|
mask = df['group'] == group_name
|
|
kmf.fit(df.loc[mask, 'time'], df.loc[mask, 'event'], label=group_name)
|
|
|
|
median_s = kmf.median_survival_time_
|
|
s_12mo = kmf.predict(12) if 12 <= df['time'].max() else None
|
|
|
|
print(f"{group_name}:")
|
|
print(f" Median survival: {median_s:.1f} months")
|
|
if s_12mo is not None:
|
|
print(f" 12-month survival: {s_12mo:.1%}")
|
|
|
|
# Log-rank test
|
|
g_treat = df['group'] == 'Treatment'
|
|
g_ctrl = df['group'] == 'Control'
|
|
lr = logrank_test(
|
|
df.loc[g_treat, 'time'], df.loc[g_ctrl, 'time'],
|
|
event_observed_A=df.loc[g_treat, 'event'],
|
|
event_observed_B=df.loc[g_ctrl, 'event']
|
|
)
|
|
print(f"\nLog-rank test: chi2={lr.test_statistic:.4f}, p={lr.p_value:.6f}")
|
|
```
|
|
|
|
---
|
|
|
|
## Example 7: Linear Mixed-Effects Model
|
|
|
|
**Pattern**: "What is the treatment effect in a mixed-effects model with random intercepts per site?"
|
|
|
|
```python
|
|
import statsmodels.formula.api as smf
|
|
|
|
# Longitudinal data with repeated measures
|
|
model = smf.mixedlm('outcome ~ treatment + time + treatment:time',
|
|
data=df, groups=df['site_id'])
|
|
fit = model.fit(reml=True)
|
|
|
|
# Fixed effects
|
|
print("Fixed Effects:")
|
|
for name in fit.fe_params.index:
|
|
coef = fit.fe_params[name]
|
|
p = fit.pvalues[name]
|
|
print(f" {name}: {coef:.4f} (p={p:.6f})")
|
|
|
|
# Random effects
|
|
group_var = float(fit.cov_re.iloc[0, 0])
|
|
resid_var = float(fit.scale)
|
|
icc = group_var / (group_var + resid_var)
|
|
print(f"\nRandom intercept variance: {group_var:.4f}")
|
|
print(f"Residual variance: {resid_var:.4f}")
|
|
print(f"ICC: {icc:.4f}")
|
|
```
|
|
|
|
---
|
|
|
|
## Example 8: Complete Analysis Pipeline
|
|
|
|
**Pattern**: Full statistical analysis from data loading to reporting.
|
|
|
|
```python
|
|
import pandas as pd
|
|
import numpy as np
|
|
import statsmodels.api as sm
|
|
import statsmodels.formula.api as smf
|
|
from statsmodels.miscmodels.ordinal_model import OrderedModel
|
|
from scipy import stats as scipy_stats
|
|
|
|
# Step 1: Load and inspect data
|
|
df = pd.read_csv('study_data.csv')
|
|
print(f"Dataset: {df.shape[0]} observations, {df.shape[1]} variables")
|
|
print(f"Outcome: {df['outcome'].value_counts().to_dict()}")
|
|
|
|
# Step 2: Determine outcome type and select model
|
|
outcome_type = 'ordinal' # Based on inspection: Mild < Moderate < Severe
|
|
levels = ['Mild', 'Moderate', 'Severe']
|
|
|
|
# Step 3: Fit appropriate model
|
|
df['outcome_cat'] = pd.Categorical(df['outcome'], categories=levels, ordered=True)
|
|
y = df['outcome_cat'].cat.codes
|
|
X = df[['treatment', 'age', 'sex']].astype(float)
|
|
|
|
model = OrderedModel(y, X, distr='logit')
|
|
fit = model.fit(method='bfgs', disp=0)
|
|
|
|
# Step 4: Extract key results
|
|
treatment_coef = fit.params['treatment']
|
|
treatment_or = np.exp(treatment_coef)
|
|
treatment_p = fit.pvalues['treatment']
|
|
treatment_ci = np.exp(fit.conf_int().loc['treatment'])
|
|
|
|
# Step 5: Report
|
|
print(f"\n=== RESULTS ===")
|
|
print(f"Model: Ordinal Logistic Regression (Proportional Odds)")
|
|
print(f"Outcome: {' < '.join(levels)}")
|
|
print(f"N = {len(y)}")
|
|
print(f"\nTreatment effect:")
|
|
print(f" Odds Ratio: {treatment_or:.4f}")
|
|
print(f" 95% CI: ({treatment_ci.iloc[0]:.4f}, {treatment_ci.iloc[1]:.4f})")
|
|
print(f" P-value: {treatment_p:.6f}")
|
|
print(f" Significant: {'Yes' if treatment_p < 0.05 else 'No'}")
|
|
|
|
if treatment_or > 1:
|
|
print(f" Interpretation: Treatment increases odds of higher severity by {(treatment_or-1)*100:.1f}%")
|
|
else:
|
|
print(f" Interpretation: Treatment decreases odds of higher severity by {(1-treatment_or)*100:.1f}%")
|
|
```
|