Metadata-Version: 2.4
Name: canbiplotme
Version: 0.3.0
Summary: Canonical biplot for grouped estimates measured with error (Python port of the R package gcbiplot, Gravity Canonical Biplot)
Author-email: Carlos Freire-Cadme <cfreirec@unemi.edu.ec>
License: GPL-3.0-or-later
Project-URL: Homepage, https://github.com/cfreirec/canbiplotme
Project-URL: Source, https://github.com/cfreirec/canbiplotme
Project-URL: Issues, https://github.com/cfreirec/canbiplotme/issues
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
Classifier: Topic :: Scientific/Engineering :: Mathematics
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.22
Requires-Dist: scipy>=1.8
Requires-Dist: pandas>=1.4
Provides-Extra: gravity
Requires-Dist: pyfixest>=0.30; extra == "gravity"
Provides-Extra: plot
Requires-Dist: matplotlib>=3.5; extra == "plot"
Provides-Extra: all
Requires-Dist: pyfixest>=0.30; extra == "all"
Requires-Dist: matplotlib>=3.5; extra == "all"
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Dynamic: license-file

# canbiplotme

Canonical biplot for grouped estimates measured with error — Python port of the
R package **gcbiplot** (Gravity Canonical Biplot, formerly canbiplotME; Freire-Cadme, 2026, doctoral thesis *Biplot canónico y
modelo gravitacional de comercio: regímenes comerciales y exportadores medidos con error*).

The individuals of a canonical (MANOVA) biplot are estimated coefficient vectors,
each with its own measurement-error covariance, correlated within groups and with
components that may be undefined for some individuals.

```bash
pip install canbiplotme            # core
pip install canbiplotme[all]       # + pyfixest (first stage) and matplotlib
```

## Reproducing the thesis

```python
import canbiplotme as cb
x = cb.load_gcb_trade()                               # 115 exporters, 6 regimes, 5 frictions
fit = cb.gcb(x, S_vars=["log_dist", "comlang_off"])   # trace 18.579
t = cb.gcb_test(fit, B=1999, seed=20251025 + 17000000)
print(t)                                              # p(trace) = 0.096; 2 of 15 pairs (Holm)
```

With an integer `seed`, `gcb_test` draws replicate `b` with `cb.RRNG(seed + b)`,
a generator that reproduces R's `set.seed()` and `rnorm()`: the replicates are
those of `gcbiplot::gcb_test()` and of the thesis (differences below 1e-11).
The `gcb*` names are aliases of the `cbme*` ones.

## Minimal example

```python
import canbiplotme as cb
x = cb.load_sim_me()
fit = cb.cbme(x, S_vars=["z1", "z2"])        # second stage
tt = cb.cbme_test(fit, B=1999, seed=1)       # G0 bootstrap: trace, largest root, pairs (Holm), S
rg = cb.cbme_regions(fit, seed=1)            # regions R1-R4
fit.plot(rg, region="R3")
cb.cbme_influence(fit, B_ref=200)            # jackknife + reference
cb.cbme_simulate_tests(fit, nsim=1000, B=199)  # validate the size in your own design
```

First stage from trade flows (needs `pyfixest`):

```python
d = cb.load_sim_trade()
s1 = cb.gravity_by_individual(d, "flow", "orig", "dest", "year", ["ldist", "lang", "fta"], "regime", controls=["contig"])
bt = cb.pairs_bootstrap(s1, d, R=199)
cnt = cb.support_counts(d, "orig", "dest", ["lang", "fta"])
idn = cb.identify_d6(s1.beta, bt["V"], s1.id, cnt, ["lang", "fta"], components=s1.spec["frictions"])
fit = cb.cbme(cb.as_me_data(s1, bt, idn))
```

## Validation against R

`tests/test_vs_r.py` compares with fixtures produced by the R package
(`tests/fixtures/make_fixtures.R`):

* second stage (heterogeneity, means and covariances, metric, eigenvalues,
  markers, structure correlations, statistics, D²) in four configurations: max.
  difference ~1e-14;
* regions R1–R3 and jackknife ρ: < 1e-10;
* first stage with pyfixest vs fixest: coefficients < 1e-7, also on a resampled
  bootstrap dataset and in the dynamic extension. Clustered covariances differ by
  ~0.1 % because of the small-sample degrees-of-freedom convention of each library
  (the analysis uses the bootstrap covariance).

Random draws (bootstrap, G0) use NumPy's generator, so p-values agree with R in
distribution, not replicate by replicate. The REML comparator `cbme_reml` reproduces `metafor::rma.mv` (T < 6e-7, means
< 2e-6, LRT < 1e-8). `cbme_permutation_test` is the permutation comparator.

`mvmeta` is a general multivariate random-effects meta-analysis and
meta-regression (unstructured between-study covariance, missing outcomes,
REML / ML / fixed), validated against `mixmeta` 1.2.2: coefficients, Psi,
covariance of the coefficients, log-likelihood and Cochran's Q agree to
< 1e-6 (REML with a badly scaled moderator, where the surface is flat: 4e-5 in
the coefficients at the same log-likelihood).
