Metadata-Version: 2.4
Name: lorbridge
Version: 0.1.0
Summary: Converting Log-Odds Ratios to interpretable [-1, +1] effect-size metrics for clinical research
Author: Se-Kang Kim
License: GPL-3.0-only
Project-URL: Homepage, https://github.com/sekangakim/lorbridge
Project-URL: Documentation, https://github.com/sekangakim/lorbridge#readme
Project-URL: Bug Tracker, https://github.com/sekangakim/lorbridge/issues
Project-URL: CRAN, https://cran.r-project.org/package=lorbridge
Keywords: log-odds ratio,odds ratio,Yule Q,Yule Y,cosine theta,meta-analysis,clinical statistics,correspondence analysis,biomedical
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
Classifier: Topic :: Scientific/Engineering :: Mathematics
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.24
Requires-Dist: scipy>=1.10
Provides-Extra: regression
Requires-Dist: statsmodels>=0.14; extra == "regression"
Requires-Dist: pandas>=2.0; extra == "regression"
Provides-Extra: tables
Requires-Dist: pandas>=2.0; extra == "tables"
Provides-Extra: full
Requires-Dist: statsmodels>=0.14; extra == "full"
Requires-Dist: pandas>=2.0; extra == "full"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Requires-Dist: statsmodels>=0.14; extra == "dev"
Requires-Dist: pandas>=2.0; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: mypy; extra == "dev"
Dynamic: license-file

# lorbridge <img src="https://img.shields.io/pypi/v/lorbridge" align="right"/>

**Bridging Log-Odds Ratios and Correspondence Analysis via Closeness-of-Concordance Measures**

Python port of the [R lorbridge package](https://cran.r-project.org/package=lorbridge) (CRAN, Kim 2026).

[![PyPI](https://img.shields.io/pypi/v/lorbridge)](https://pypi.org/project/lorbridge/)
[![Python](https://img.shields.io/pypi/pyversions/lorbridge)](https://pypi.org/project/lorbridge/)
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![Tests](https://img.shields.io/badge/tests-114%20passing-brightgreen)]()
[![CRAN](https://img.shields.io/badge/R%20package-CRAN-276DC3)](https://cran.r-project.org/package=lorbridge)

---

## 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

```bash
pip install lorbridge
```

For the regression wrappers (`blr_continuous`, `blr_categorical`, `mlr_ccm`):

```bash
pip install "lorbridge[regression]"   # adds statsmodels + pandas
```

For everything including table formatting:

```bash
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.

```python
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

```python
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):

```python
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.

```python
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`:

```python
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

```python
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

```python
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

```python
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 |

```python
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
# 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](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](https://orcid.org/0000-0003-0928-3396)  
Email: se-kang.kim@bcm.edu
