RAPID Logistic Regression — Tutorial Notebook¶
This notebook walks you through using the RAPID logistic regression pipeline from end to end.
By the end of this tutorial you will know how to:
- Instantiate a logistic regression model using the RAPID Pipeline Factory
- Fit the model and customise variable labels
- Interpret every assumption test, performance metric, and plot
- Understand and interpret odds ratios in an epidemiological context
- Choose the right link function for your binary outcome
This pipeline is designed for use in epidemiological and clinical research contexts where the outcome of interest is binary — such as mortality, ICU admission, disease diagnosis, readmission, or treatment response.
!!! note "Outcome variable requirements"
The outcome variable must be binary and coded strictly as 0 (absent) and 1 (present). Any other coding will raise a validation error before fitting.
1. Setup and Installation¶
Before starting, ensure the RAPID package is installed in your environment.
pip install 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-"
gdown.download(url, 'isaric-0.1.0-py3-none-any.whl', quiet=False)
!pip install isaric-0.1.0-py3-none-any.whl
from isaric.pipelines.pipeline_factory import RAPID_PipelineFactory
import pandas as pd
import numpy as np
2. The Pipeline Factory¶
All RAPID pipelines are created through the RAPID_PipelineFactory. Rather than importing and instantiating regression classes directly, the factory provides a single, consistent entry point for creating any supported pipeline by name.
# Instantiate the factory — no arguments needed
factory = RAPID_PipelineFactory()
# See all pipelines available out of the box
print(factory.available())
To create a logistic regression model, call factory.create() with "logistic" as the first argument, followed by your data and modelling parameters.
3. Preparing Your Data¶
The pipeline accepts a pandas DataFrame. Rows with missing values are dropped automatically before fitting.
Your data should have:
- One column representing the binary outcome, coded as
0and1(e.g. 0 = survived, 1 = died) - One or more columns representing the independent_vars (e.g. age, sex, comorbidity score)
Below is an example using a simulated clinical dataset modelling in-hospital mortality.
np.random.seed(42)
n = 600
age = np.random.randint(18, 90, size=n)
sex = np.random.choice([0, 1], size=n)
comorbidity_score = np.random.poisson(lam=2, size=n)
icu_admission = np.random.choice([0, 1], size=n, p=[0.65, 0.35])
# Generate binary outcome with a realistic log-odds structure
log_odds = -4 + 0.04 * age + 0.3 * sex + 0.5 * comorbidity_score + 1.2 * icu_admission
prob = 1 / (1 + np.exp(-log_odds))
mortality = np.random.binomial(1, prob)
df = pd.DataFrame({
"mortality": mortality,
"age": age,
"sex": sex,
"comorbidity_score": comorbidity_score,
"icu_admission": icu_admission
})
print(f"Outcome prevalence: {mortality.mean():.1%}")
df.head()
4. Creating and Fitting the Model¶
Pass your DataFrame, outcome variable name, and predictor list to factory.create(). The regression_type parameter controls whether this is a univariable ("Uni") or multivariable ("Multi") regression — this affects column naming in the results table but not the underlying model.
The classification_threshold parameter controls the probability cutoff used to convert predicted probabilities into binary class predictions for classification metrics such as accuracy, precision, recall, and F1 score. The default is 0.5, but in clinical settings with imbalanced outcomes (e.g. rare events) you may wish to adjust this.
model = factory.create(
"logistic",
data=df,
dependent_var="mortality",
independent_vars=["age", "sex", "comorbidity_score", "icu_admission"],
regression_type="Multi",
classification_threshold=0.5
)
Fitting the Model¶
Call .fit() to run the regression. Pass a labels dictionary to map raw column names to human-readable display names.
model.fit(
labels={
"age": "Age (years)",
"sex": "Sex (Male=1)",
"comorbidity_score": "Comorbidity Score",
"icu_admission": "ICU Admission"
},
cross_val=True,
n_splits=5
)
During fit(), the pipeline automatically:
- Fits the Binomial GLM
- Runs all assumption tests
- Computes all performance and classification metrics
- Runs k-fold cross-validation (if
cross_val=True)**
5. Results Table — Odds Ratios¶
The results table is stored in model.summary_df. Each row represents one predictor. Unlike linear regression, logistic regression reports odds ratios rather than raw coefficients.
model.summary_df
Understanding Odds Ratios¶
An odds ratio (OR) expresses how the odds of the outcome change for a one-unit increase in a predictor, holding all other independent_vars constant.
| Odds Ratio | Interpretation |
|---|---|
| OR = 1.0 | No association — the predictor has no effect on the odds of the outcome |
| OR > 1.0 | Increased odds — the predictor is associated with higher odds of the outcome |
| OR < 1.0 | Decreased odds — the predictor is associated with lower odds of the outcome |
Example: An OR of 1.8 for ICU admission means that patients admitted to ICU have 1.8 times the odds of dying compared to non-ICU patients, after adjusting for all other independent_vars in the model. Equivalently, this represents an 80% increase in odds.
Odds vs Risk: Odds ratios are not the same as risk ratios (relative risks). When the outcome is rare (prevalence < 10%), the OR approximates the risk ratio well. When the outcome is common, the OR will overestimate the risk ratio. Always be explicit about which measure you are reporting.
Confidence Intervals and p-values¶
- LowerCI / UpperCI: The 95% confidence interval around the odds ratio. If this interval does not include 1.0, the association is statistically significant at p < 0.05.
- p-value: The probability of observing an OR this large (or larger) under the null hypothesis of no association.
Important: Statistical significance alone is not sufficient for clinical interpretation. An OR of 1.02 may be statistically significant in a large dataset but clinically trivial. Always consider the magnitude of the effect alongside the p-value.
6. Assumption Tests¶
Logistic regression has fewer distributional assumptions than linear regression — crucially, it does not assume normally distributed residuals. However, several important assumptions still apply.
Display all assumption results with:
model.summary(assumptions="all")
Or access the underlying dataframe directly:
model.assumption_metrics_df
6.1 Events Per Variable (EPV)¶
What it tests: Whether there are enough outcome events relative to the number of independent_vars in the model. EPV is calculated as the number of events (outcome = 1) divided by the number of predictor variables.
Why it matters in epidemiology: Logistic regression requires a minimum number of events to produce stable, reliable coefficient estimates. With too few events relative to the number of independent_vars, the model may overfit — producing extreme odds ratios, very wide confidence intervals, or even failure to converge. This is a common problem in studies of rare outcomes such as mortality in low-risk populations.
How to interpret:
| EPV | Interpretation |
|---|---|
| ≥ 10 | Acceptable — coefficient estimates are likely stable |
| < 10 | Warning — estimates may be unstable; consider reducing the number of independent_vars or collecting more data |
| < 5 | Serious concern — results should be interpreted with great caution |
What to do if EPV is low: Reduce the number of independent_vars (prioritise clinically important ones), combine categories, use penalised regression (e.g. Firth logistic regression), or collect more data if possible.
print(f"EPV: {model.epv:.2f}")
model.summary(assumptions=["Events Per Variable (EPV)"])
6.2 VIF — Multicollinearity¶
What it tests: Whether predictor variables are highly correlated with each other. The Variance Inflation Factor (VIF) measures how much the variance of a coefficient estimate is inflated due to its correlation with other independent_vars.
Why it matters in epidemiology: Multicollinearity makes individual coefficient estimates unreliable and difficult to interpret, even when the overall model fits well. It is a common problem when modelling related risk factors — for example, including both BMI and weight, or both age and age-squared without centring.
How to interpret:
| VIF | Interpretation |
|---|---|
| 1–5 | Acceptable |
| 5–10 | Potential concern — consider whether both variables are needed |
| > 10 | Serious multicollinearity — odds ratios are unreliable |
What to do if violated: Remove one of the correlated independent_vars, create a composite variable, or centre continuous independent_vars before creating interaction terms.
# View VIF for all independent_vars
model.vif_df
# Display via summary with a custom threshold
model.summary(assumptions=["VIF"], vif_threshold=5.0)
6.3 Influential Outliers — Cook's Distance¶
What it tests: Whether any individual observations have a disproportionate influence on the model's coefficient estimates. Cook's distance summarises how much all fitted values change when a given observation is deleted from the model.
Why it matters in epidemiology: In a clinical dataset, a small number of patients with unusual covariate profiles or outcomes can substantially alter the estimated odds ratios. Identifying these observations is essential before drawing clinical conclusions.
How to interpret:
The threshold is set at 4/n. Observations exceeding this are flagged as potentially influential.
Flagged observations should be:
- Investigated for data entry errors or miscoding
- Assessed for whether they are clinically meaningful extreme cases
- Used in a sensitivity analysis — re-run the model without them and compare results
What to do: Never remove influential observations without justification. If they represent genuine data, report their presence transparently and consider whether robust methods are appropriate.
print(f"Threshold (4/n): {model.influential_outliers_threshold:.4f}")
print(f"Number of influential points: {len(model.influential_points)}")
print(f"Indices: {model.influential_points}")
model.summary(assumptions=["Influential Outliers", "Number of Influential Points"])
7. Diagnostic Plots¶
Visual diagnostics complement the numerical assumption tests and are essential for understanding model behaviour.
7.1 Forest Plot¶
What it shows: Odds ratios and their 95% confidence intervals displayed as horizontal lines with central point estimates. A vertical reference line is drawn at 1.0 — the null value for odds ratios (no association).
What to look for:
- Confidence interval entirely to the right of 1.0: Significant positive association — the predictor increases the odds of the outcome
- Confidence interval entirely to the left of 1.0: Significant negative association — the predictor decreases the odds of the outcome
- Confidence interval crossing 1.0: No statistically significant association at p < 0.05
- Width of the interval: Wide intervals indicate imprecise estimates — common with small sample sizes, rare outcomes, or variables with low variance
- Log scale: The forest plot uses a log scale on the x-axis, which ensures that ORs of 0.5 and 2.0 are displayed symmetrically around 1.0
In epidemiological practice: The forest plot is a standard figure in observational studies and clinical trials, providing an immediate visual summary of all adjusted associations in the model.
model.summary(plots=["forest_plot"])
7.2 ROC Curve¶
What it shows: The Receiver Operating Characteristic (ROC) curve plots the true positive rate (sensitivity) against the false positive rate (1 − specificity) across all possible classification thresholds. The Area Under the Curve (AUC) summarises overall discrimination ability as a single number.
What to look for:
- Curve hugging the top-left corner: Excellent discrimination — the model correctly identifies cases and non-cases across a wide range of thresholds
- Curve close to the diagonal: Poor discrimination — the model performs no better than chance
- AUC value: See the table below for interpretation
| AUC | Interpretation |
|---|---|
| 0.5 | No discrimination — equivalent to random guessing |
| 0.6–0.7 | Poor discrimination |
| 0.7–0.8 | Acceptable discrimination |
| 0.8–0.9 | Good discrimination |
| > 0.9 | Excellent discrimination |
Why AUC matters in epidemiology: AUC is threshold-independent — it tells you how well the model ranks cases above non-cases regardless of which classification threshold you choose. This makes it particularly useful for comparing models. However, AUC alone does not tell you how well a model performs at any specific threshold, nor does it reflect calibration (whether predicted probabilities match observed event rates).
Choosing a threshold: The default classification_threshold is 0.5, but in clinical contexts this may not be optimal. For a rare outcome (e.g. 5% mortality), a threshold of 0.5 may classify almost everyone as a non-case. Consider adjusting the threshold based on the clinical costs of false positives versus false negatives.
model.summary(plots=["roc_curve"])
7.3 Confusion Matrix¶
What it shows: A 2×2 table of predicted versus actual class labels at the chosen classification_threshold.
| Predicted Negative | Predicted Positive | |
|---|---|---|
| Actual Negative | True Negatives (TN) | False Positives (FP) |
| Actual Positive | False Negatives (FN) | True Positives (TP) |
What to look for:
- High TN and TP counts: The model is correctly classifying both non-events and events
- High FN count: The model is missing many true cases — in clinical contexts, false negatives are often the most dangerous error (e.g. missing a patient who will deteriorate)
- High FP count: The model is over-predicting events — less dangerous clinically but may lead to unnecessary interventions
In epidemiological practice: The confusion matrix should always be interpreted in the context of the outcome's prevalence and the clinical implications of each error type. A model predicting a rare outcome (e.g. 3% mortality) can achieve 97% accuracy by predicting zero deaths — the confusion matrix exposes this failure mode that accuracy alone conceals.
model.summary(plots=["confusion_matrix"])
# Access the confusion matrix directly as a 2x2 array
print(model.cm)
print(f"True Negatives: {model.cm[0, 0]}")
print(f"False Positives: {model.cm[0, 1]}")
print(f"False Negatives: {model.cm[1, 0]}")
print(f"True Positives: {model.cm[1, 1]}")
8. Performance Metrics¶
Logistic regression produces a richer set of performance metrics than linear regression, reflecting both its role as a statistical model and as a binary classifier. Display all of them with:
model.summary(performance="all")
# Or access the dataframe directly
model.performance_metrics_df
8.1 Classification Metrics¶
These metrics evaluate the model's performance as a binary classifier at the chosen classification_threshold.
Accuracy: The proportion of all observations correctly classified (both events and non-events). This is the most intuitive metric but is misleading when the outcome is imbalanced. A model predicting all patients as non-events achieves 90% accuracy in a dataset with 10% mortality.
Precision: Of all patients predicted to have the outcome, what proportion actually had it? High precision means few false positives. Also known as Positive Predictive Value (PPV) in clinical contexts.
Recall (Sensitivity): Of all patients who actually had the outcome, what proportion did the model correctly identify? High recall means few false negatives. This is often the most clinically critical metric — missing true cases is usually the more dangerous error.
F1 Score: The harmonic mean of precision and recall. Useful when you need a single metric that balances both, particularly for imbalanced outcomes. Ranges from 0 (worst) to 1 (best).
In epidemiological practice: For rare outcomes, focus on recall (sensitivity) and precision (PPV) rather than accuracy. Consider whether the clinical cost of a false negative (missing a sick patient) outweighs the cost of a false positive (unnecessarily treating a well patient), and adjust your classification_threshold accordingly.
print(f"Accuracy: {model.accuracy:.4f}")
print(f"Precision: {model.precision:.4f}")
print(f"Recall: {model.recall:.4f}")
print(f"F1 Score: {model.f1:.4f}")
model.summary(performance=["Accuracy", "Precision", "Recall", "F1 Score", "Confusion Matrix"])
8.2 AUC-ROC¶
AUC (Area Under the ROC Curve) measures the model's ability to discriminate between cases and non-cases across all possible thresholds. It is the probability that a randomly chosen case will be assigned a higher predicted probability than a randomly chosen non-case.
AUC is threshold-independent and prevalence-independent, making it the preferred metric for comparing models across different datasets or outcome prevalences.
See Section 7.2 for a full interpretation guide.
print(f"AUC-ROC: {model.auc:.4f}")
model.summary(performance=["AUC-ROC"])
8.3 Log Loss¶
Log Loss (also called binary cross-entropy) measures the quality of the predicted probabilities rather than binary classifications. It penalises confident wrong predictions heavily.
A perfect model has a log loss of 0. Unlike AUC, log loss is sensitive to calibration — it penalises a model that correctly ranks cases but assigns poorly calibrated probabilities (e.g. predicting 0.99 when the true probability is 0.6).
In epidemiological practice: Log loss is useful when the predicted probabilities themselves are the output of interest — for example, when building a clinical risk score where individual probability estimates are communicated to clinicians or patients.
print(f"Log Loss: {model.logloss:.4f}")
8.4 Pseudo R² Metrics¶
Because logistic regression does not minimise residual variance, the standard R² from linear regression does not apply. Several pseudo R² measures have been developed to fill this role. None of them should be interpreted as the proportion of variance explained in the same way as linear R².
McFadden R²: Compares the log-likelihood of the fitted model to that of a null model (intercept only). Values between 0.2 and 0.4 are generally considered indicative of good model fit in epidemiological research. Unlike linear R², values above 0.4 are uncommon and not necessarily expected.
Adjusted McFadden R²: Penalises for the number of independent_vars — equivalent to Adjusted R² in the likelihood framework.
Efron R²: Computed from the residuals relative to the outcome mean. Tends to give values closer to the linear R² intuition.
Cox-Snell R²: Based on the likelihood ratio statistic. Has the limitation that it cannot reach a maximum of 1.0 for discrete outcomes.
Nagelkerke R²: A rescaled version of Cox-Snell R² that can reach 1.0.
Tjur R²: The difference between the mean predicted probability for cases (outcome = 1) and non-cases (outcome = 0). Intuitive and directly interpretable — a value of 0.2 means the model assigns predicted probabilities that are on average 0.2 higher for cases than non-cases.
Which to report: Nagelkerke R² is most widely recognised in clinical epidemiology. Tjur R² is increasingly recommended for its direct interpretability. Report at least one alongside AUC.
print(f"McFadden R²: {model.mcfadden_r2:.4f}")
print(f"Adjusted McFadden R²: {model.mcfadden_adj_r2:.4f}")
print(f"Efron R²: {model.efron_r2:.4f}")
print(f"Cox-Snell R²: {model.cox_snell_r2:.4f}")
print(f"Nagelkerke R²: {model.nagelkerke_r2:.4f}")
print(f"Tjur R²: {model.tjur_r2:.4f}")
8.5 Information Criteria — AIC and BIC¶
AIC and BIC are used for model comparison, not for evaluating a single model in isolation. Lower values indicate a better trade-off between model fit and complexity.
- AIC penalises complexity lightly — preferred when prediction is the primary goal
- BIC penalises complexity more heavily — preferred when model parsimony matters, which is common in epidemiological research where interpretability is important
Rule of thumb: A difference in AIC or BIC of more than 10 between two models is considered strong evidence in favour of the model with the lower value.
print(f"AIC: {model.aic:.4f}")
print(f"BIC: {model.bic:.4f}")
print(f"LLF: {model.llf:.4f}")
9. Cross-Validation¶
K-fold cross-validation estimates how well the model generalises to new, unseen patients. The dataset is split into k equal folds; the model is trained on k-1 folds and evaluated on the held-out fold, cycling through all folds.
Cross-validation accuracy is measured at the chosen classification_threshold.
Why this matters in epidemiology: Logistic regression models are often developed to generate clinical risk scores or decision tools. A model that overfits the development dataset will perform poorly when applied to new patients, potentially causing harm if used clinically. Cross-validation is an essential check before any model is considered for deployment.
model.summary(cross_val="all")
# Or access directly
model.cv_df
print(f"Mean CV Accuracy: {model.cross_val_scores.mean():.4f}")
print(f"Std CV Accuracy: {model.cross_val_scores.std():.4f}")
print(f"Fold Accuracies: {model.cross_val_scores}")
Interpreting Cross-Validation Results¶
- Mean Accuracy: The average classification accuracy across all folds. Compare this to the training accuracy — if the training accuracy is substantially higher, the model may be overfitting.
- Standard Deviation: Measures stability. A high standard deviation suggests the model performs inconsistently across different subsets of the data — a concern in smaller datasets or with rare outcomes.
- Individual Fold Accuracies: Inspect these to identify whether any single fold is an outlier, which might indicate imbalanced class distribution across folds.
10. Choosing a Link Function¶
All logistic regression models in RAPID use the Binomial family. The choice of link function determines how the linear predictor (the weighted sum of independent_vars) is transformed into a probability.
The three supported link functions produce different shapes of the probability curve and have different coefficient interpretations. The default — and by far the most commonly used in epidemiology — is the logit link.
Pass the desired link when creating the model:
model = factory.create(
"logistic",
data=df,
dependent_var="mortality",
independent_vars=["age", "sex", "comorbidity_score", "icu_admission"],
link="logit" # or "probit", "cloglog"
)
model.fit()
10.1 Binomial + Logit (Default)¶
Family: "binomial" | Link: "logit"
The standard logistic regression model. The logit link models the log-odds of the outcome as a linear function of the independent_vars. Coefficients are log-odds ratios and are exponentiated to produce odds ratios.
When to use:
- The default choice for almost all binary outcome analyses in clinical epidemiology
- When you want to report odds ratios — the standard effect measure in case-control and cohort studies
- When the outcome is not extremely rare or extremely common
Probability curve shape: Symmetric S-curve — the transition from low to high probability is symmetric around the 50% point.
Coefficient interpretation: Exponentiate to get odds ratios (already done for you in model.summary_df).
Epidemiological examples: Mortality, ICU admission, disease diagnosis, readmission within 30 days, treatment response.
model_logit = factory.create(
"logistic",
data=df,
dependent_var="mortality",
independent_vars=["age", "sex", "comorbidity_score", "icu_admission"],
link="logit"
)
model_logit.fit()
10.2 Binomial + Probit¶
Family: "binomial" | Link: "probit"
The probit link models the outcome probability through the inverse of the standard normal cumulative distribution function (CDF). It assumes the binary outcome arises from an underlying continuous normally distributed latent variable crossing a threshold.
When to use:
- When there is theoretical motivation to assume an underlying normally distributed latent variable
- Common in econometrics and some psychometric applications
- When comparing results with a probit model from another software package or literature
Probability curve shape: Very similar to the logit — a symmetric S-curve. The two models produce nearly identical predicted probabilities in most practical settings, with differences only at the extremes.
Coefficient interpretation: Probit coefficients are not directly interpretable as odds ratios. They represent the change in the z-score (standard normal deviate) of the underlying latent variable. For most epidemiological applications, the logit link is preferred because odds ratios are more clinically interpretable.
Epidemiological examples: Rare in mainstream clinical epidemiology, but used in genetic epidemiology (liability threshold models) and health economics.
model_probit = factory.create(
"logistic",
data=df,
dependent_var="mortality",
independent_vars=["age", "sex", "comorbidity_score", "icu_admission"],
link="probit"
)
model_probit.fit()
10.3 Binomial + Complementary Log-Log (CLogLog)¶
Family: "binomial" | Link: "cloglog"
The complementary log-log link models the outcome as an extreme value distribution and is the natural link when the binary outcome arises from a Poisson process — for example, whether at least one event occurred during a follow-up period.
When to use:
- The outcome is a binary indicator of whether a rare event occurred (e.g. at least one hospitalisation, at least one adverse event)
- The outcome probability is very low (close to 0) — the cloglog curve is asymmetric and rises steeply from low probabilities
- Modelling discrete-time survival data — the cloglog link is the natural choice for hazard models in discrete time
- When the assumption of a symmetric probability curve (logit/probit) seems unjustified
Probability curve shape: Asymmetric S-curve — it rises more steeply from low probabilities than it approaches 1.0. This means the model is more sensitive to increases in predictor values when the outcome is rare.
Coefficient interpretation: Coefficients from a cloglog model can be interpreted as log hazard ratios under a complementary log-log model, which is closely related to the Cox proportional hazards model. This makes it particularly useful as a bridge between logistic and survival analysis.
Epidemiological examples: Any-cause hospitalisation during a study period, occurrence of at least one infection, binary indicator of disease incidence in a cohort study where the outcome is rare.
model_cloglog = factory.create(
"logistic",
data=df,
dependent_var="mortality",
independent_vars=["age", "sex", "comorbidity_score", "icu_admission"],
link="cloglog"
)
model_cloglog.fit()
10.4 Comparing Link Functions¶
For most epidemiological analyses, the logit, probit, and cloglog models will produce very similar predicted probabilities and the same substantive conclusions. The choice of link rarely changes the findings meaningfully unless the outcome is very rare or very common.
Use RAPID_ModelComparator to compare AIC and BIC across link functions:
from isaric.pipelines.model_comparison import RAPID_ModelComparator
comparator = RAPID_ModelComparator({
"Logit": model_logit,
"Probit": model_probit,
"CLogLog": model_cloglog,
})
comparator.report()
Guidance for choosing:
- Default to logit unless you have a specific theoretical reason to prefer another link
- Use cloglog when the outcome is a rare binary event arising from a Poisson process or when you want to bridge to survival analysis
- Use probit when matching methods with an existing analysis or when a latent variable interpretation is theoretically motivated
- When uncertain, compare AIC/BIC — the link with the lowest value provides the best fit for your data
11. Displaying Selected Results¶
You do not need to display everything at once. Pass lists of specific metric names to display only what is relevant for your analysis.
# A focused publication-ready summary
model.summary(
assumptions=["Events Per Variable (EPV)", "VIF", "Influential Outliers"],
performance=["AUC-ROC", "Accuracy", "Precision", "Recall", "F1 Score",
"Nagelkerke R2", "AIC", "BIC", "Confusion Matrix"],
cross_val=["Mean Accuracy", "Standard Deviation"],
plots=["forest_plot", "roc_curve", "confusion_matrix"],
vif_threshold=5.0
)
12. Accessing Results Programmatically¶
All results are stored as attributes on the model object and can be accessed directly.
# Odds ratio table
model.summary_df
# Performance metrics dataframe
model.performance_metrics_df
# Assumption tests dataframe
model.assumption_metrics_df
# VIF table
model.vif_df
# Cross-validation dataframe
model.cv_df
# Confusion matrix (2x2 NumPy array)
model.cm
# Individual metrics
print(model.auc)
print(model.accuracy)
print(model.precision)
print(model.recall)
print(model.f1)
print(model.logloss)
print(model.nagelkerke_r2)
print(model.tjur_r2)
print(model.aic)
print(model.epv)
print(model.influential_points)
print(model.cross_val_scores)
Summary¶
This tutorial has covered the full RAPID logistic regression workflow:
| Step | What you did |
|---|---|
| Factory | Created the model via RAPID_PipelineFactory |
| fit() | Fitted the model with labels and cross-validation |
| Results | Interpreted the odds ratio table |
| Assumptions | Tested and interpreted EPV, VIF, and Cook's distance |
| Plots | Interpreted the forest plot, ROC curve, and confusion matrix |
| Performance | Understood accuracy, precision, recall, F1, AUC, log loss, pseudo R², AIC, and BIC |
| Cross-validation | Assessed generalisation using k-fold CV |
| Link functions | Chose between logit, probit, and cloglog links |
For the linear regression equivalent of this tutorial, see the Linear Regression Tutorial notebook.