Converting Log-Odds Ratios to interpretable [-1, +1] effect-size metrics for clinical research
Project description
lorbridge 
Bridging Log-Odds Ratios and Correspondence Analysis via Closeness-of-Concordance Measures
Python port of the R lorbridge package (CRAN, Kim 2026).
Overview
Logistic regression is the most widely used statistical model in clinical and biomedical research — but its core output, the log-odds ratio (LOR), is notoriously difficult for non-statisticians to interpret intuitively.
lorbridge provides a principled, one-step bridge from LOR to a set of correlation-like effect-size metrics that live on the familiar [−1, +1] scale:
| Metric | Formula | Reference |
|---|---|---|
| Yule's Q | Q = (OR − 1) / (OR + 1) | Yule (1912) |
| Yule's Y | Y = (√OR − 1) / (√OR + 1) | Yule (1914) |
| r_meta | d = LOR·√3/π, r = d/√(d²+4) | Hasselblad & Hedges (1995) |
| Cosine θ | Geometric angle in NSCA biplot space | Kim & Grochowalski (2019) |
All four live on [−1, +1]. A value of +0.68 reads the same way a correlation of r = 0.68 does — immediately, without statistical training.
The package also implements Singly-Ordered (SONSCA) and Doubly-Ordered (DONSCA) Nonsymmetric Correspondence Analysis, allowing researchers to place the cosine theta side-by-side with Q, Y, and r_meta for direct comparison.
Installation
pip install lorbridge
For the regression wrappers (blr_continuous, blr_categorical, mlr_ccm):
pip install "lorbridge[regression]" # adds statsmodels + pandas
For everything including table formatting:
pip install "lorbridge[full]"
Requirements: Python ≥ 3.9, numpy ≥ 1.24, scipy ≥ 1.10
Three analytical pathways
Pathway 1 — From raw 2×2 cell counts
The most direct entry point. Supply four cell counts from a 2×2 contingency table and receive the full set of CCMs instantly.
from lorbridge import lor_ci_2x2
# Compare Race1 vs Race2 (anchor) at IQ bin 1 vs IQ bin 4 (anchor)
r = lor_ci_2x2(a=119, b=57, c=32, d=57,
label="Race1 vs Race2 | IQ1 vs IQ4")
print(r.summary())
[Race1 vs Race2 | IQ1 vs IQ4]
LOR : +1.3218 SE = 0.2275 [+0.8759, +1.7677]
OR : 3.7500 [ 2.4016, 5.8579]
Yule's Q : +0.5797 [+0.4112, +0.7081]
Yule's Y : +0.3574 [+0.2425, +0.4661]
r_meta : +0.3433 [+0.2312, +0.4472]
Pathway 2 — From a binary logistic regression model
from lorbridge import blr_continuous, load_lorbridge_data
df = load_lorbridge_data() # N=900: VM score, VMbin, minority, Race
r = blr_continuous(outcome=df["minority"],
predictor=df["VM"],
label="VM score → Minority status (per 1 SD)")
print(r.summary())
For a categorical predictor (e.g. discretised VM bins):
from lorbridge import blr_categorical
r = blr_categorical(outcome=df["minority"],
predictor=df["VMbin"],
reference=3, # VM4 is the reference level (index 3)
label="VMbin → Minority")
print(r.summary())
Pathway 3 — From a SONSCA or DONSCA contingency table
When rows are nominal (e.g. racial groups) and columns are ordered (e.g. IQ score bins), use SONSCA to obtain the anchored cosine theta alongside all CCMs in a single call.
from lorbridge import sonsca_ccm, tab_IQ, TAB_IQ_ROW_LABELS, TAB_IQ_COL_LABELS
from lorbridge import print_table
# Build all non-anchor contrasts: Race1, Race2, Race3 vs Race4 × IQ1–IQ3, IQ5–IQ6
results = []
for i_focal in [0, 1, 2]: # Race1, Race2, Race3 (Race4 = anchor, index 3)
for j_focal in [0,1,2,4,5]: # IQ1–IQ6 except IQ4 (IQ4 = anchor, index 3)
r = sonsca_ccm(
tab_IQ,
row_focal=i_focal,
col_focal=j_focal,
row_anchor=3, # Race4
col_anchor=3, # IQ4
label=f"{TAB_IQ_ROW_LABELS[i_focal]}|{TAB_IQ_COL_LABELS[j_focal]}",
)
results.append(r)
print_table(results, digits=3, show_ci=False)
────────────────────────────────────────────────────────────────────
Contrast LOR SE OR Q Y r_meta cos_theta
────────────────────────────────────────────────────────────────────
Race1|IQ1 +1.322 0.228 3.750 +0.580 +0.357 +0.343 +0.968
Race1|IQ2 +0.828 0.234 2.289 +0.392 +0.223 +0.223 +0.968
Race1|IQ3 +0.441 0.233 1.554 +0.217 +0.120 +0.121 +0.966
Race1|IQ5 -0.714 0.286 0.490 -0.342 -0.188 -0.197 -0.756
Race1|IQ6 -1.901 0.470 0.149 -0.741 -0.504 -0.460 +0.374
...
────────────────────────────────────────────────────────────────────
When both rows and columns are ordered (e.g. IQ bins predicting VM bins),
use donsca_ccm:
from lorbridge import donsca_ccm, tab_IQ_VM
r = donsca_ccm(tab_IQ_VM,
row_focal=0, col_focal=0, # IQ1 vs VM1
row_anchor=3, col_anchor=3, # IQ4, VM4
label="IQ1 vs IQ4 | VM1 vs VM4")
print(r.summary())
Bootstrap confidence intervals for cosine theta
from lorbridge import sonsca_ccm, tab_IQ
r = sonsca_ccm(
tab_IQ,
row_focal=0, col_focal=0,
row_anchor=3, col_anchor=3,
n_boot=2000, # bootstrap replications
seed=42, # reproducibility
label="Race1|IQ1",
)
print(f"cosine θ = {r.cosine_theta:+.4f} "
f"[{r.cosine_theta_lo:+.4f}, {r.cosine_theta_hi:+.4f}]")
Inertia / variance explained
from lorbridge import sonsca, inertia_table, tab_IQ
fit = sonsca(tab_IQ, row_anchor=3, col_anchor=3)
print(inertia_table(fit))
Method : SONSCA
Total τ : 0.1523
Dim δ (sing. val.) δ² % τ Cumul. %
──────────────────────────────────────────────────────────
1 0.3755 0.1410 92.58% 92.58%
2 0.1045 0.0109 7.17% 99.75%
3 0.0194 0.0004 0.25% 100.00%
──────────────────────────────────────────────────────────
Export to DataFrame
from lorbridge import summary_table
df = summary_table(results) # list of CcmResult objects
print(df.to_string(index=False))
Built-in datasets
| Name | Shape | Description |
|---|---|---|
tab_IQ |
4 × 6 | Races × IQ bins (SONSCA Analysis A) |
tab_VM |
4 × 6 | Races × VM bins (SONSCA Analysis B) |
tab_IQ_VM |
6 × 6 | IQ bins × VM bins (DONSCA) |
load_lorbridge_data() |
900 × 4 | Individual records: VM, VMbin, minority, Race |
from lorbridge import tab_IQ, TAB_IQ_ROW_LABELS, TAB_IQ_COL_LABELS
import pandas as pd
pd.DataFrame(tab_IQ,
index=TAB_IQ_ROW_LABELS,
columns=TAB_IQ_COL_LABELS)
R package
This Python package is a faithful port of the R lorbridge package published on CRAN:
# R equivalent
install.packages("lorbridge")
library(lorbridge)
lc <- lor_ci_2x2(a=119, b=57, c=32, d=57)
ccm_row(exp(lc$lor), exp(lc$lo), exp(lc$hi), lc$lor, lc$lo, lc$hi)
CRAN page: https://cran.r-project.org/package=lorbridge
GitHub (R): https://github.com/sekangakim/lorbridge
Citation
If you use lorbridge in published research, please cite the original methodological paper:
Kim, S.-K., & Grochowalski, J. H. (2019). Gaining from discretization of continuous data: The correspondence analysis biplot approach. Behavior Research Methods, 51(2), 589–601. https://doi.org/10.3758/s13428-018-1161-1
And the software itself:
Kim, S.-K. (2026). lorbridge: Bridging Log-Odds Ratios and Correspondence Analysis via Closeness-of-Concordance Measures (v0.1.0) [Python package]. PyPI. https://pypi.org/project/lorbridge/
License
GPL-3.0 — see LICENSE for details.
Author
Se-Kang Kim, Ph.D.
Psychology Division, Department of Pediatrics
Baylor College of Medicine / Texas Children's Hospital
ORCID: 0000-0003-0928-3396
Email: se-kang.kim@bcm.edu
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lorbridge-0.1.0.tar.gz.
File metadata
- Download URL: lorbridge-0.1.0.tar.gz
- Upload date:
- Size: 32.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3a93f6e58781463a409be8ccb2389642d1dec5d29dda35ccef916ed76253b7a8
|
|
| MD5 |
47c6d22c50d338e4ac5a97e6e2202440
|
|
| BLAKE2b-256 |
b555a949e06345a608a5021d2e2ea999b6132931908b2dfa02360b4d61f20684
|
File details
Details for the file lorbridge-0.1.0-py3-none-any.whl.
File metadata
- Download URL: lorbridge-0.1.0-py3-none-any.whl
- Upload date:
- Size: 25.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
25fa7c2e757e89fe5373c49623f125e078107d2067b0138e11068d1ed33b74f4
|
|
| MD5 |
28be862e00dbee3dc202f632df1da228
|
|
| BLAKE2b-256 |
d645c554fea6857ac841ef4e5ad3de138d8dd601b1f858e10fa4056cbd809010
|