canbiplotme 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
canbiplotme/stage2.py ADDED
@@ -0,0 +1,336 @@
1
+ """Second stage: canonical biplot of estimates measured with error (port of ``cbme``)."""
2
+ from __future__ import annotations
3
+
4
+ from dataclasses import dataclass, field
5
+ from itertools import combinations
6
+
7
+ import numpy as np
8
+ import pandas as pd
9
+
10
+ from .data import MEData
11
+
12
+
13
+ @dataclass
14
+ class Settings:
15
+ dim: int = 2
16
+ T_errors: str = "independent"
17
+ mean_errors: str = "joint"
18
+ eps: float = 1e-6
19
+ metric: str = "mean"
20
+ weights: str = "n"
21
+ S_idx: list | None = None
22
+ T_fixed: np.ndarray | None = None
23
+
24
+
25
+ class CBMEFit:
26
+ """Result of :func:`cbme`. Attributes mirror the R object.
27
+
28
+ ``T``, ``T_raw``, ``T_eigen_raw``, ``mu`` (G x K), ``mu_cov`` (G x K x K),
29
+ ``mu0``, ``n``, ``W``, ``Vbar``, ``H``, ``mbar``, ``lambda_`` (eigenvalues),
30
+ ``V`` (canonical directions), ``A`` (group markers), ``Afull``, ``C``
31
+ (component markers), ``cos2``, ``quality``, ``ind`` (individuals with every
32
+ component identified), ``ind_ids``, ``stats`` and ``D2``.
33
+ """
34
+
35
+ def __init__(self, core: dict, data: MEData, settings: Settings, cache: dict):
36
+ self.__dict__.update(core)
37
+ self.data = data
38
+ self.settings = settings
39
+ self.cache = cache
40
+
41
+ def _update(self, core: dict):
42
+ self.__dict__.update(core)
43
+
44
+ # convenient labelled views -------------------------------------------
45
+ def markers(self) -> pd.DataFrame:
46
+ cols = [f"Dim{d + 1}" for d in range(self.A.shape[1])]
47
+ out = pd.DataFrame(self.A, index=self.data.levels, columns=cols)
48
+ out["quality"] = self.quality
49
+ return out
50
+
51
+ def component_markers(self) -> pd.DataFrame:
52
+ return pd.DataFrame(self.C, index=self.data.components,
53
+ columns=[f"Dim{d + 1}" for d in range(self.C.shape[1])])
54
+
55
+ def means(self) -> pd.DataFrame:
56
+ return pd.DataFrame(self.mu, index=self.data.levels, columns=self.data.components)
57
+
58
+ def se_means(self) -> pd.DataFrame:
59
+ se = np.sqrt(np.einsum("gkk->gk", self.mu_cov))
60
+ return pd.DataFrame(se, index=self.data.levels, columns=self.data.components)
61
+
62
+ def summary(self) -> dict:
63
+ lam = self.lambda_
64
+ return {
65
+ "eigen": pd.DataFrame({"lambda": lam, "inertia": np.cumsum(lam) / lam.sum()},
66
+ index=range(1, len(lam) + 1)),
67
+ "heterogeneity": pd.DataFrame({"T": np.diag(self.T), "T_raw": np.diag(self.T_raw),
68
+ "Vbar": np.diag(self.Vbar)}, index=self.data.components),
69
+ "T_eigen_raw": self.T_eigen_raw,
70
+ "condition_W": np.linalg.cond(self.W),
71
+ "means": self.means(), "se_means": self.se_means(),
72
+ "structure": pd.DataFrame(self.cos2[:, :self.A.shape[1]], index=self.data.components),
73
+ "D2": self.D2, "stats": self.stats,
74
+ }
75
+
76
+ def plot(self, *args, **kwargs):
77
+ from .plot import plot_cbme
78
+ return plot_cbme(self, *args, **kwargs)
79
+
80
+ def __repr__(self) -> str:
81
+ L = min(len(self.n) - 1, self.W.shape[0])
82
+ lam = self.lambda_[:L]
83
+ s = ["Canonical biplot of estimates measured with error",
84
+ f"{int(sum(self.n))} individuals, {len(self.n)} groups, {self.W.shape[0]} components", "",
85
+ "Eigenvalues: " + ", ".join(f"{v:.3f}" for v in lam),
86
+ "Cumulative inertia: " + ", ".join(f"{v:.3f}" for v in np.cumsum(lam) / lam.sum()), "",
87
+ "Statistics: " + ", ".join(f"{k} = {v:.4g}" for k, v in self.stats.items()), "",
88
+ "Group markers:", self.markers().round(3).to_string(), "",
89
+ "Component markers:", self.component_markers().round(3).to_string()]
90
+ return "\n".join(s)
91
+
92
+
93
+ def cbme(x: MEData, dim: int = 2, T_errors: str = "independent", mean_errors: str = "joint",
94
+ eps: float = 1e-6, metric: str = "mean", weights: str = "n", S_vars=None,
95
+ T_fixed=None) -> CBMEFit:
96
+ """Canonical biplot of estimates measured with error (chapter 3 of the thesis).
97
+
98
+ Estimates the heterogeneity T by moments (Definition 3.3.1) and projects it
99
+ onto the PSD cone, computes the precision-weighted group means and their
100
+ covariance (Definition 3.3.2), the metric W = T + Vbar, the between matrix H
101
+ and the canonical solution H v = lambda W v with v'Wv = 1.
102
+
103
+ Parameters mirror the R function ``cbme``.
104
+ """
105
+ if T_errors not in ("independent", "joint"):
106
+ raise ValueError("T_errors must be 'independent' or 'joint'")
107
+ if mean_errors not in ("joint", "independent"):
108
+ raise ValueError("mean_errors must be 'joint' or 'independent'")
109
+ if metric not in ("mean", "median", "group"):
110
+ raise ValueError("metric must be 'mean', 'median' or 'group'")
111
+ if weights not in ("n", "equal"):
112
+ raise ValueError("weights must be 'n' or 'equal'")
113
+ st = Settings(dim=dim, T_errors=T_errors, mean_errors=mean_errors, eps=eps, metric=metric,
114
+ weights=weights, T_fixed=None if T_fixed is None else np.asarray(T_fixed, float))
115
+ if not x.has_joint:
116
+ st.T_errors = "independent"
117
+ st.mean_errors = "independent"
118
+ if S_vars is not None:
119
+ st.S_idx = [x.components.index(v) if isinstance(v, str) else int(v) for v in S_vars]
120
+ cache = make_cache(x)
121
+ core = fit_core(x, x.beta, np.arange(x.N), st, cache)
122
+ return CBMEFit(core, x, st, cache)
123
+
124
+
125
+ def make_cache(x: MEData) -> dict:
126
+ pos = np.zeros(x.N, dtype=int)
127
+ for lev in x.levels:
128
+ idx = np.where(x.group == lev)[0]
129
+ pos[idx] = np.arange(len(idx))
130
+ Om = {lev: x.group_omega(lev) for lev in x.levels} if x.has_joint else None
131
+ out = {"pos": pos, "Omega": Om}
132
+ if x.block is not None and x.has_joint:
133
+ bpos = np.zeros(x.N, dtype=int)
134
+ for b in x.block_levels:
135
+ idx = np.where(x.block == b)[0]
136
+ bpos[idx] = np.arange(len(idx))
137
+ out["bpos"] = bpos
138
+ out["OmB"] = {b: x.block_omega(b) for b in x.block_levels}
139
+ return out
140
+
141
+
142
+ def _omega_cross(x: MEData, cache: dict, r1: np.ndarray, r2: np.ndarray) -> np.ndarray:
143
+ b = x.block[r1[0]]
144
+ K = x.K
145
+ c1 = (cache["bpos"][r1][:, None] * K + np.arange(K)[None, :]).ravel()
146
+ c2 = (cache["bpos"][r2][:, None] * K + np.arange(K)[None, :]).ravel()
147
+ return cache["OmB"][b][np.ix_(c1, c2)]
148
+
149
+
150
+ def _omega_rows(x: MEData, cache: dict, rows: np.ndarray) -> np.ndarray:
151
+ lev = x.group[rows[0]]
152
+ K = x.K
153
+ cols = (cache["pos"][rows][:, None] * K + np.arange(K)[None, :]).ravel()
154
+ return cache["Omega"][lev][np.ix_(cols, cols)]
155
+
156
+
157
+ def fit_core(x: MEData, beta: np.ndarray, rows: np.ndarray, st: Settings, cache: dict,
158
+ fill_missing: bool = True) -> dict:
159
+ K = x.K
160
+ rows = np.asarray(rows)
161
+ grp = x.group[rows]
162
+ levs = x.levels
163
+ Va = x.V[rows]
164
+ ok = np.isfinite(beta)
165
+ joint_T = st.T_errors == "joint"
166
+ joint_m = st.mean_errors == "joint"
167
+ Om = None
168
+ if joint_T or joint_m:
169
+ Om = {}
170
+ for lev in levs:
171
+ r = np.where(grp == lev)[0]
172
+ Om[lev] = _omega_rows(x, cache, rows[r]) if len(r) else None
173
+ # ---- heterogeneity (Definition 3.3.1) ----
174
+ if st.T_fixed is None:
175
+ Traw = np.zeros((K, K))
176
+ for k in range(K):
177
+ for l in range(k, K):
178
+ S = E = den = 0.0
179
+ for lev in levs:
180
+ r = np.where((grp == lev) & ok[:, k] & ok[:, l])[0]
181
+ m = len(r)
182
+ if m < 2:
183
+ continue
184
+ dk = beta[r, k] - beta[r, k].mean()
185
+ dl = beta[r, l] - beta[r, l].mean()
186
+ S += np.sum(dk * dl)
187
+ if joint_T:
188
+ rg = np.where(grp == lev)[0]
189
+ j = np.searchsorted(rg, r)
190
+ Okl = Om[lev][np.ix_(j * K + k, j * K + l)]
191
+ Okl = np.where(np.isfinite(Okl), Okl, 0.0)
192
+ E += np.trace(Okl) - Okl.sum() / m
193
+ else:
194
+ E += (1 - 1 / m) * np.sum(Va[r, k, l])
195
+ den += m - 1
196
+ Traw[k, l] = Traw[l, k] = (S - E) / den if den > 0 else 0.0
197
+ ev, evec = np.linalg.eigh(Traw)
198
+ ev, evec = ev[::-1], evec[:, ::-1]
199
+ T = evec @ np.diag(np.maximum(ev, st.eps)) @ evec.T
200
+ T_eig = ev
201
+ else:
202
+ T = Traw = st.T_fixed
203
+ T_eig = np.linalg.eigvalsh(T)[::-1]
204
+ # ---- weights and means (Definition 3.3.2) ----
205
+ w = np.zeros((len(rows), K))
206
+ for k in range(K):
207
+ w[ok[:, k], k] = 1.0 / (T[k, k] + Va[ok[:, k], k, k])
208
+ bz = np.where(ok, beta, 0.0)
209
+ mu0 = (w * bz).sum(0) / w.sum(0)
210
+ G = len(levs)
211
+ mu = np.full((G, K), np.nan)
212
+ cv = np.zeros((G, K, K))
213
+ n = np.array([np.sum(grp == lev) for lev in levs], dtype=float)
214
+ for gi, lev in enumerate(levs):
215
+ r = np.where(grp == lev)[0]
216
+ if not len(r):
217
+ continue
218
+ wg = w[r]
219
+ sw = wg.sum(0)
220
+ with np.errstate(invalid="ignore", divide="ignore"):
221
+ mu[gi] = np.where(sw > 0, (wg * bz[r]).sum(0) / np.where(sw > 0, sw, 1), np.nan)
222
+ for k in range(K):
223
+ for l in range(k, K):
224
+ if sw[k] == 0 or sw[l] == 0:
225
+ cv[gi, k, l] = cv[gi, l, k] = np.nan
226
+ continue
227
+ num = T[k, l] * np.sum(wg[:, k] * wg[:, l])
228
+ if joint_m:
229
+ jj = np.arange(len(r))
230
+ Okl = Om[lev][np.ix_(jj * K + k, jj * K + l)]
231
+ Okl = np.where(np.isfinite(Okl), Okl, 0.0)
232
+ num += wg[:, k] @ Okl @ wg[:, l]
233
+ else:
234
+ vv = np.where(np.isfinite(Va[r, k, l]), Va[r, k, l], 0.0)
235
+ num += np.sum(wg[:, k] * wg[:, l] * vv)
236
+ cv[gi, k, l] = cv[gi, l, k] = num / (sw[k] * sw[l])
237
+ # covariance between the means of two groups of the same error block
238
+ cx = None
239
+ if joint_m and "OmB" in cache:
240
+ cx = np.zeros((G, G, K, K))
241
+ blk = x.block[rows]
242
+ blk_g = [blk[grp == lev][0] if np.any(grp == lev) else None for lev in levs]
243
+ for gi in range(G - 1):
244
+ for hi in range(gi + 1, G):
245
+ if blk_g[gi] is None or blk_g[gi] != blk_g[hi]:
246
+ continue
247
+ rg = np.where(grp == levs[gi])[0]
248
+ rh = np.where(grp == levs[hi])[0]
249
+ Ogh = _omega_cross(x, cache, rows[rg], rows[rh])
250
+ Ogh = np.where(np.isfinite(Ogh), Ogh, 0.0)
251
+ wg, wh = w[rg], w[rh]
252
+ sg, sh = wg.sum(0), wh.sum(0)
253
+ for k in range(K):
254
+ for l in range(K):
255
+ if sg[k] == 0 or sh[l] == 0:
256
+ cx[gi, hi, k, l] = np.nan
257
+ continue
258
+ blkm = Ogh[np.ix_(np.arange(len(rg)) * K + k, np.arange(len(rh)) * K + l)]
259
+ cx[gi, hi, k, l] = wg[:, k] @ blkm @ wh[:, l] / (sg[k] * sh[l])
260
+ cx[hi, gi] = cx[gi, hi].T
261
+ missing = np.isnan(mu)
262
+ if fill_missing and missing.any():
263
+ # a component without individuals in a group takes the common mean (section 3.6.6)
264
+ for gi in np.where(missing.any(1))[0]:
265
+ for k in np.where(missing[gi])[0]:
266
+ mu[gi, k] = mu0[k]
267
+ cv[gi, k, :] = 0.0
268
+ cv[gi, :, k] = 0.0
269
+ cv[gi, k, k] = T[k, k]
270
+ # ---- metric ----
271
+ Vbar = np.zeros((K, K))
272
+ for k in range(K):
273
+ for l in range(K):
274
+ r = np.where(ok[:, k] & ok[:, l])[0]
275
+ v = Va[r, k, l]
276
+ if st.metric == "mean":
277
+ Vbar[k, l] = v.mean()
278
+ elif st.metric == "median":
279
+ Vbar[k, l] = np.median(v)
280
+ else:
281
+ gm = [v[grp[r] == lev].mean() for lev in levs if np.any(grp[r] == lev)]
282
+ Vbar[k, l] = np.mean(gm)
283
+ Vbar = (Vbar + Vbar.T) / 2
284
+ W = T + Vbar
285
+ # ---- between matrix and canonical solution ----
286
+ nw = n if st.weights == "n" else np.full(G, n.sum() / G)
287
+ keep = n > 0
288
+ mbar = (mu[keep] * nw[keep, None]).sum(0) / nw[keep].sum()
289
+ Dm = mu - mbar
290
+ Dm[~keep] = 0.0
291
+ H = (Dm * np.sqrt(nw)[:, None]).T @ (Dm * np.sqrt(nw)[:, None]) / (keep.sum() - 1)
292
+ L = np.linalg.cholesky(W)
293
+ Li = np.linalg.inv(L)
294
+ ev, evec = np.linalg.eigh(Li @ H @ Li.T)
295
+ ev, evec = ev[::-1], evec[:, ::-1]
296
+ Vc = Li.T @ evec
297
+ s = np.sign(Vc[np.abs(Vc).argmax(0), np.arange(K)])
298
+ s[s == 0] = 1
299
+ s[min(st.dim, K):] = 1 # sign fixed only for the first `dim` axes, as in the thesis
300
+ Vc = Vc * s
301
+ lam = np.maximum(ev, 0.0)
302
+ d = st.dim
303
+ Afull = Dm @ Vc
304
+ A = Afull[:, :d]
305
+ WV = W @ Vc
306
+ C = WV[:, :d]
307
+ cos2 = WV ** 2 / np.diag(W)[:, None]
308
+ with np.errstate(invalid="ignore", divide="ignore"):
309
+ quality = (A ** 2).sum(1) / (Afull ** 2).sum(1)
310
+ comp = np.where(ok.sum(1) == K)[0]
311
+ ind = (beta[comp] - mbar) @ Vc[:, :d]
312
+ stats = {"trace": lam.sum(), "lambda1": lam[0], "Pi2": lam[:min(2, K)].sum() / lam.sum()}
313
+ if st.S_idx is not None:
314
+ p = cos2[:, 0] / cos2[:, 0].sum()
315
+ c2 = cos2[:, :min(2, K)]
316
+ stats["S"] = p[st.S_idx].sum()
317
+ stats["S_plane"] = c2[st.S_idx].sum() / c2.sum()
318
+ D2 = pairwise_D2(mu, cv, levs, cx)
319
+ return {"T": T, "T_raw": Traw, "T_eigen_raw": T_eig, "mu": mu, "mu_cov": cv, "mu_cross": cx, "mu0": mu0,
320
+ "missing_components": missing, "n": n, "W": W, "Vbar": Vbar, "H": H, "mbar": mbar,
321
+ "lambda_": lam, "V": Vc, "A": A, "Afull": Afull, "C": C, "cos2": cos2,
322
+ "quality": quality, "ind": ind, "ind_ids": [x.id[i] for i in rows[comp]],
323
+ "stats": stats, "D2": D2, "weights_means": w}
324
+
325
+
326
+ def pairwise_D2(mu: np.ndarray, cv: np.ndarray, levs, cx=None) -> pd.Series:
327
+ out = {}
328
+ for a, b in combinations(range(mu.shape[0]), 2):
329
+ dlt = mu[a] - mu[b]
330
+ S = cv[a] + cv[b]
331
+ if cx is not None:
332
+ C = np.where(np.isfinite(cx[a, b]), cx[a, b], 0.0)
333
+ S = S - C - C.T
334
+ ok = np.isfinite(dlt) & np.isfinite(np.diag(S))
335
+ out[f"{levs[a]}-{levs[b]}"] = float(dlt[ok] @ np.linalg.solve(S[np.ix_(ok, ok)], dlt[ok]))
336
+ return pd.Series(out)
@@ -0,0 +1,107 @@
1
+ Metadata-Version: 2.4
2
+ Name: canbiplotme
3
+ Version: 0.3.0
4
+ Summary: Canonical biplot for grouped estimates measured with error (Python port of the R package gcbiplot, Gravity Canonical Biplot)
5
+ Author-email: Carlos Freire-Cadme <cfreirec@unemi.edu.ec>
6
+ License: GPL-3.0-or-later
7
+ Project-URL: Homepage, https://github.com/cfreirec/canbiplotme
8
+ Project-URL: Source, https://github.com/cfreirec/canbiplotme
9
+ Project-URL: Issues, https://github.com/cfreirec/canbiplotme/issues
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
12
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
13
+ Requires-Python: >=3.9
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: numpy>=1.22
17
+ Requires-Dist: scipy>=1.8
18
+ Requires-Dist: pandas>=1.4
19
+ Provides-Extra: gravity
20
+ Requires-Dist: pyfixest>=0.30; extra == "gravity"
21
+ Provides-Extra: plot
22
+ Requires-Dist: matplotlib>=3.5; extra == "plot"
23
+ Provides-Extra: all
24
+ Requires-Dist: pyfixest>=0.30; extra == "all"
25
+ Requires-Dist: matplotlib>=3.5; extra == "all"
26
+ Provides-Extra: test
27
+ Requires-Dist: pytest; extra == "test"
28
+ Dynamic: license-file
29
+
30
+ # canbiplotme
31
+
32
+ Canonical biplot for grouped estimates measured with error — Python port of the
33
+ R package **gcbiplot** (Gravity Canonical Biplot, formerly canbiplotME; Freire-Cadme, 2026, doctoral thesis *Biplot canónico y
34
+ modelo gravitacional de comercio: regímenes comerciales y exportadores medidos con error*).
35
+
36
+ The individuals of a canonical (MANOVA) biplot are estimated coefficient vectors,
37
+ each with its own measurement-error covariance, correlated within groups and with
38
+ components that may be undefined for some individuals.
39
+
40
+ ```bash
41
+ pip install canbiplotme # core
42
+ pip install canbiplotme[all] # + pyfixest (first stage) and matplotlib
43
+ ```
44
+
45
+ ## Reproducing the thesis
46
+
47
+ ```python
48
+ import canbiplotme as cb
49
+ x = cb.load_gcb_trade() # 115 exporters, 6 regimes, 5 frictions
50
+ fit = cb.gcb(x, S_vars=["log_dist", "comlang_off"]) # trace 18.579
51
+ t = cb.gcb_test(fit, B=1999, seed=20251025 + 17000000)
52
+ print(t) # p(trace) = 0.096; 2 of 15 pairs (Holm)
53
+ ```
54
+
55
+ With an integer `seed`, `gcb_test` draws replicate `b` with `cb.RRNG(seed + b)`,
56
+ a generator that reproduces R's `set.seed()` and `rnorm()`: the replicates are
57
+ those of `gcbiplot::gcb_test()` and of the thesis (differences below 1e-11).
58
+ The `gcb*` names are aliases of the `cbme*` ones.
59
+
60
+ ## Minimal example
61
+
62
+ ```python
63
+ import canbiplotme as cb
64
+ x = cb.load_sim_me()
65
+ fit = cb.cbme(x, S_vars=["z1", "z2"]) # second stage
66
+ tt = cb.cbme_test(fit, B=1999, seed=1) # G0 bootstrap: trace, largest root, pairs (Holm), S
67
+ rg = cb.cbme_regions(fit, seed=1) # regions R1-R4
68
+ fit.plot(rg, region="R3")
69
+ cb.cbme_influence(fit, B_ref=200) # jackknife + reference
70
+ cb.cbme_simulate_tests(fit, nsim=1000, B=199) # validate the size in your own design
71
+ ```
72
+
73
+ First stage from trade flows (needs `pyfixest`):
74
+
75
+ ```python
76
+ d = cb.load_sim_trade()
77
+ s1 = cb.gravity_by_individual(d, "flow", "orig", "dest", "year", ["ldist", "lang", "fta"], "regime", controls=["contig"])
78
+ bt = cb.pairs_bootstrap(s1, d, R=199)
79
+ cnt = cb.support_counts(d, "orig", "dest", ["lang", "fta"])
80
+ idn = cb.identify_d6(s1.beta, bt["V"], s1.id, cnt, ["lang", "fta"], components=s1.spec["frictions"])
81
+ fit = cb.cbme(cb.as_me_data(s1, bt, idn))
82
+ ```
83
+
84
+ ## Validation against R
85
+
86
+ `tests/test_vs_r.py` compares with fixtures produced by the R package
87
+ (`tests/fixtures/make_fixtures.R`):
88
+
89
+ * second stage (heterogeneity, means and covariances, metric, eigenvalues,
90
+ markers, structure correlations, statistics, D²) in four configurations: max.
91
+ difference ~1e-14;
92
+ * regions R1–R3 and jackknife ρ: < 1e-10;
93
+ * first stage with pyfixest vs fixest: coefficients < 1e-7, also on a resampled
94
+ bootstrap dataset and in the dynamic extension. Clustered covariances differ by
95
+ ~0.1 % because of the small-sample degrees-of-freedom convention of each library
96
+ (the analysis uses the bootstrap covariance).
97
+
98
+ Random draws (bootstrap, G0) use NumPy's generator, so p-values agree with R in
99
+ distribution, not replicate by replicate. The REML comparator `cbme_reml` reproduces `metafor::rma.mv` (T < 6e-7, means
100
+ < 2e-6, LRT < 1e-8). `cbme_permutation_test` is the permutation comparator.
101
+
102
+ `mvmeta` is a general multivariate random-effects meta-analysis and
103
+ meta-regression (unstructured between-study covariance, missing outcomes,
104
+ REML / ML / fixed), validated against `mixmeta` 1.2.2: coefficients, Psi,
105
+ covariance of the coefficients, log-likelihood and Cochran's Q agree to
106
+ < 1e-6 (REML with a badly scaled moderator, where the surface is flat: 4e-5 in
107
+ the coefficients at the same log-likelihood).
@@ -0,0 +1,22 @@
1
+ canbiplotme/__init__.py,sha256=EDN7iB98LAPX5Ln8_4d2h9-X9oL2iR25v8LV0V-Ke0E,1957
2
+ canbiplotme/data.py,sha256=xw4M6FVCdMw99QFUjpf8omeESD5cLSxwPZulboPMZos,13222
3
+ canbiplotme/dynamic.py,sha256=SuwBZ8P9vjOXeVqfAvEyT8C1H2ATH6Eyy2Y8aecT7eg,4778
4
+ canbiplotme/inference.py,sha256=8kYyC2lijrlUEgWCoXstqySGZQ9MTEnb42zhTj5XWsk,11833
5
+ canbiplotme/meta.py,sha256=Ng_sUbBVssWDanG4DCZvjiICA3vD_mS5w-zR1k71kFA,5234
6
+ canbiplotme/plot.py,sha256=j7gw5TcuiCJzco9xUDSGcliwiElxy4loCgkoNGBNc30,2506
7
+ canbiplotme/regions.py,sha256=SNsXGRKRerGqEwZ2D6elUKxqmiDuBokEJBVW9kktiqU,3888
8
+ canbiplotme/reml.py,sha256=S1F5bTd1nAK73034WsjfK_uPSt-nYis4tgg9GpKN7Q4,5196
9
+ canbiplotme/rng.py,sha256=f5jfFbhsGwgnynwaETjxwvVa1CnZfUEBlxZnpD7EIfU,4747
10
+ canbiplotme/simulation.py,sha256=nhTnsw3c4qhEuueEHuh2Nme5LgWrLfhViiGjZyE-fFU,3644
11
+ canbiplotme/stage1.py,sha256=rdvTsTL3Db1o2Y6-mVv4BjrfsulGMk0-fxItN_TajbE,8197
12
+ canbiplotme/stage2.py,sha256=515qOD9RcCtuWWB3YnMuyV7_WMLhB5TPTxGkgEzwy2Y,14193
13
+ canbiplotme/datasets/__init__.py,sha256=gcX9ECNBsGZ4f-SIZF-lz73wdTQEjGaXXSpNwp27lQE,2688
14
+ canbiplotme/datasets/gcb_trade.npz,sha256=oSWUy9DPjpHzCuP-3rSIaH8TkNtLhsmkZ40BO5lCfdQ,699067
15
+ canbiplotme/datasets/gcb_trade_a21.npz,sha256=m7dBHbpJkzx-hTxE-6fxX8DjbwrM0B5OEjx5ML8LVZo,699580
16
+ canbiplotme/datasets/sim_me.npz,sha256=xXLuoNkXmQKtn41lrk7-4gZJCEi-l4JP72N03_PYgsY,128338
17
+ canbiplotme/datasets/sim_trade.csv,sha256=9qegJSkU9iTOqbtbQxbtCbjKQbwduZX1QQASoR_AQnk,120664
18
+ canbiplotme-0.3.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
19
+ canbiplotme-0.3.0.dist-info/METADATA,sha256=kA395qIeHJS3ttzbufwKdpPd87hfWgSnLOdCStiNeTk,4773
20
+ canbiplotme-0.3.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
21
+ canbiplotme-0.3.0.dist-info/top_level.txt,sha256=VsxDhM0lVq9n4PGzIucb_XheIKROJ7Zzhbgbz_ysxAI,12
22
+ canbiplotme-0.3.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+