Skip to main content

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).

PyPI Python License: GPL v3 Tests CRAN


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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lorbridge-0.1.0.tar.gz (32.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

lorbridge-0.1.0-py3-none-any.whl (25.6 kB view details)

Uploaded Python 3

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

Hashes for lorbridge-0.1.0.tar.gz
Algorithm Hash digest
SHA256 3a93f6e58781463a409be8ccb2389642d1dec5d29dda35ccef916ed76253b7a8
MD5 47c6d22c50d338e4ac5a97e6e2202440
BLAKE2b-256 b555a949e06345a608a5021d2e2ea999b6132931908b2dfa02360b4d61f20684

See more details on using hashes here.

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

Hashes for lorbridge-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 25fa7c2e757e89fe5373c49623f125e078107d2067b0138e11068d1ed33b74f4
MD5 28be862e00dbee3dc202f632df1da228
BLAKE2b-256 d645c554fea6857ac841ef4e5ad3de138d8dd601b1f858e10fa4056cbd809010

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page