bs-python-utils 1.0.1__tar.gz → 1.1.1__tar.gz
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.
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/PKG-INFO +6 -2
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/README.md +3 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/numerical/bsnputils.py +1 -3
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/opt/bs_opt.py +12 -12
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/stats/bivariate_quantiles.py +95 -28
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/stats/bsstats.py +1 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils.egg-info/PKG-INFO +6 -2
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils.egg-info/requires.txt +1 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/pyproject.toml +7 -2
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/tests/test_bivariate_quantiles.py +20 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/LICENSE +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid1prec13.txt +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid1prec17.txt +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid1prec9.txt +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid2prec13.txt +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid2prec17.txt +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid2prec9.txt +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid3prec13.txt +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid3prec17.txt +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid3prec9.txt +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid4prec13.txt +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid4prec17.txt +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid4prec9.txt +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid5prec13.txt +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid5prec17.txt +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid5prec9.txt +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/Timer.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/__init__.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/bivariate_quantiles.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/bs_altair.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/bs_logging.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/bs_mathstr.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/bs_mem.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/bs_opt.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/bs_plots.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/bs_seaborn.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/bs_sparse_gaussian.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/bsmplutils.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/bsnputils.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/bssputils.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/bsstats.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/bsutils.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/chebyshev.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/core/Timer.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/core/__init__.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/core/bs_logging.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/core/bs_mathstr.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/core/bs_mem.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/core/bsutils.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/data_anal/__init__.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/data_anal/pandas_utils.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/data_anal/sklearn_utils.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/distance_covariances.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/examples/examples_altair.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/examples/examples_distance_covariances.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/examples/examples_mem.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/examples/examples_mpl.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/examples/examples_opt.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/examples/examples_seaborn.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/examples/examples_sklearn.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/examples/examples_sparse_gaussian.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/numerical/__init__.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/numerical/bs_sparse_gaussian.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/numerical/bssputils.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/numerical/chebyshev.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/opt/__init__.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/pandas_utils.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/sklearn_utils.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/stats/__init__.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/stats/distance_covariances.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/streamlit_utils.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/viz/__init__.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/viz/bs_altair.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/viz/bs_plots.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/viz/bs_seaborn.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/viz/bsmplutils.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/viz/streamlit_utils.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils.egg-info/SOURCES.txt +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils.egg-info/dependency_links.txt +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils.egg-info/top_level.txt +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/setup.cfg +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/tests/test_bs_opt.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/tests/test_bs_sparse_gaussian.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/tests/test_bsnputils.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/tests/test_bssputils.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/tests/test_bsstats.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/tests/test_bsutils.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/tests/test_chebyshev.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/tests/test_nptri.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/tests/test_random_draws.py +0 -0
- {bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/tests/test_sklearn_utils.py +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bs-python-utils
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: Utilities programs for my Python code
|
|
5
|
-
Requires-Python: >=3.
|
|
5
|
+
Requires-Python: >=3.13
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
7
|
License-File: LICENSE
|
|
8
8
|
Requires-Dist: altair>=5.5.0
|
|
@@ -26,6 +26,7 @@ Requires-Dist: streamlit>=1.49.1
|
|
|
26
26
|
Requires-Dist: vega-datasets>=0.9.0
|
|
27
27
|
Requires-Dist: colorama>=0.4.6
|
|
28
28
|
Requires-Dist: pyarrow>=23.0.0
|
|
29
|
+
Requires-Dist: scipy-stubs~=1.16.2
|
|
29
30
|
Dynamic: license-file
|
|
30
31
|
|
|
31
32
|
## bs-python-utils
|
|
@@ -49,6 +50,9 @@ Dynamic: license-file
|
|
|
49
50
|
|
|
50
51
|
### Release notes
|
|
51
52
|
|
|
53
|
+
#### 1.1.1 (September 04, 2026)
|
|
54
|
+
Added simulation-based bivariate ranks.
|
|
55
|
+
|
|
52
56
|
#### 1.0.1 (May 9, 2026)
|
|
53
57
|
Fixed badges.
|
|
54
58
|
|
|
@@ -300,9 +300,7 @@ def npmaxabs(arr: np.ndarray) -> float:
|
|
|
300
300
|
return cast(float, np.max(np.abs(arr)))
|
|
301
301
|
|
|
302
302
|
|
|
303
|
-
def rice_stderr(
|
|
304
|
-
y: np.ndarray, x: np.ndarray, is_sorted: bool = False
|
|
305
|
-
) -> np.ndarray | float:
|
|
303
|
+
def rice_stderr(y: np.ndarray, x: np.ndarray, is_sorted: bool = False) -> np.ndarray:
|
|
306
304
|
"""
|
|
307
305
|
computes the Rice local estimators of the standard error of y | x
|
|
308
306
|
|
|
@@ -25,6 +25,7 @@ import numpy as np
|
|
|
25
25
|
import scipy.linalg as spla
|
|
26
26
|
import scipy.optimize as spopt
|
|
27
27
|
|
|
28
|
+
|
|
28
29
|
from bs_python_utils.numerical.bsnputils import TwoArrays, check_vector, npmaxabs
|
|
29
30
|
from bs_python_utils.core.bsutils import bs_error_abort, print_stars
|
|
30
31
|
from bs_python_utils.core.Timer import timeit
|
|
@@ -221,11 +222,11 @@ def check_gradient_scalar_function(
|
|
|
221
222
|
fg: should return the scalar value, and the gradient if its `gr` argument is `True`
|
|
222
223
|
p: where we are checking the gradient
|
|
223
224
|
args: other arguments passed to `fg`
|
|
224
|
-
mode: "central" or "forward"
|
|
225
|
-
EPS: the step for forward or central
|
|
225
|
+
mode: ``"central"`` or ``"forward"``
|
|
226
|
+
EPS: the step for forward or central finite differences
|
|
226
227
|
|
|
227
228
|
Returns:
|
|
228
|
-
the analytic and numeric gradients.
|
|
229
|
+
the analytic and numeric (or AD) gradients.
|
|
229
230
|
"""
|
|
230
231
|
f0, f_grad = fg(p, args, gr=True) # type: ignore
|
|
231
232
|
f0 = cast(float, f0)
|
|
@@ -285,7 +286,6 @@ def acc_grad_descent(
|
|
|
285
286
|
Returns:
|
|
286
287
|
A pair ``(x_star, status)`` where ``status`` is ``0`` on convergence and ``1`` otherwise.
|
|
287
288
|
"""
|
|
288
|
-
|
|
289
289
|
# no proximal projection if no h
|
|
290
290
|
local_prox_h: ProximalFunction = prox_h if prox_h else lambda x, t, p: x
|
|
291
291
|
|
|
@@ -420,8 +420,8 @@ def minimize_some_fixed(
|
|
|
420
420
|
obj,
|
|
421
421
|
x_init,
|
|
422
422
|
method="L-BFGS-B",
|
|
423
|
-
args=args,
|
|
424
|
-
options=options,
|
|
423
|
+
args=(args,),
|
|
424
|
+
options=cast(Any, options),
|
|
425
425
|
jac=grad_obj,
|
|
426
426
|
bounds=bounds,
|
|
427
427
|
)
|
|
@@ -453,8 +453,8 @@ def minimize_some_fixed(
|
|
|
453
453
|
fixed_obj,
|
|
454
454
|
t_init,
|
|
455
455
|
method="L-BFGS-B",
|
|
456
|
-
args=args,
|
|
457
|
-
options=options,
|
|
456
|
+
args=(args,),
|
|
457
|
+
options=cast(Any, options),
|
|
458
458
|
jac=fixed_grad_obj,
|
|
459
459
|
bounds=t_bounds,
|
|
460
460
|
)
|
|
@@ -508,8 +508,8 @@ def minimize_free(
|
|
|
508
508
|
obj,
|
|
509
509
|
x_init,
|
|
510
510
|
method="BFGS",
|
|
511
|
-
args=args,
|
|
512
|
-
options=options,
|
|
511
|
+
args=(args,),
|
|
512
|
+
options=cast(Any, options),
|
|
513
513
|
jac=grad_obj,
|
|
514
514
|
)
|
|
515
515
|
else:
|
|
@@ -517,8 +517,8 @@ def minimize_free(
|
|
|
517
517
|
obj,
|
|
518
518
|
x_init,
|
|
519
519
|
method="L-BFGS-B",
|
|
520
|
-
args=args,
|
|
521
|
-
options=options,
|
|
520
|
+
args=(args,),
|
|
521
|
+
options=cast(Any, options),
|
|
522
522
|
jac=grad_obj,
|
|
523
523
|
bounds=bounds,
|
|
524
524
|
)
|
{bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/stats/bivariate_quantiles.py
RENAMED
|
@@ -15,6 +15,8 @@ References:
|
|
|
15
15
|
Quantiles, Ranks and Signs." *Annals of Statistics* 45(1), 2017.
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
18
20
|
from typing import cast
|
|
19
21
|
|
|
20
22
|
import numpy as np
|
|
@@ -23,6 +25,74 @@ from bs_python_utils.core.bsutils import bs_error_abort
|
|
|
23
25
|
from bs_python_utils.numerical.bsnputils import TwoArrays, npmaxabs
|
|
24
26
|
from bs_python_utils.numerical.chebyshev import Interval, cheb_get_nodes_1d
|
|
25
27
|
from bs_python_utils.opt.bs_opt import minimize_free, print_optimization_results
|
|
28
|
+
from bs_python_utils.numerical.bsnputils import check_matrix
|
|
29
|
+
from scipy.special import logsumexp
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def bivariate_ranks_simul(
|
|
33
|
+
x: np.ndarray,
|
|
34
|
+
rng: np.random.Generator,
|
|
35
|
+
n_draws: int = 10_000,
|
|
36
|
+
h_mult: float = 100.0,
|
|
37
|
+
) -> np.ndarray:
|
|
38
|
+
"""This computes bivariate ranks for a matrix ``x` using simulations
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
x: Input matrix of shape ``(n, 2)``.
|
|
42
|
+
rng: Random number generator for reproducibility.
|
|
43
|
+
n_draws: Number of random draws for the simulation.
|
|
44
|
+
h_mult: we set the bandwidth h as std(v_init)/h_mult, default is 100.0
|
|
45
|
+
"""
|
|
46
|
+
n, d = check_matrix(x)
|
|
47
|
+
if d != 2:
|
|
48
|
+
bs_error_abort("Input matrix x must have 2 columns for bivariate ranks.")
|
|
49
|
+
|
|
50
|
+
v_init = np.mean(x, axis=1)[:-1]
|
|
51
|
+
h = np.std(v_init) / 100.0
|
|
52
|
+
|
|
53
|
+
tau_draws = rng.uniform(low=0.0, high=1.0, size=2 * n_draws).reshape((n_draws, 2))
|
|
54
|
+
|
|
55
|
+
def objv_and_grad(
|
|
56
|
+
v: np.ndarray,
|
|
57
|
+
ret: int = 0,
|
|
58
|
+
) -> float | np.ndarray | None:
|
|
59
|
+
vn = np.zeros(n)
|
|
60
|
+
vn[:-1] = v.copy()
|
|
61
|
+
vn[-1] = -np.sum(v)
|
|
62
|
+
psi_vals = tau_draws @ x.T - vn
|
|
63
|
+
lse_vals = logsumexp(psi_vals / h, axis=1)
|
|
64
|
+
psi_maxs = h * lse_vals
|
|
65
|
+
obj_val = np.mean(psi_maxs)
|
|
66
|
+
probs = np.exp(psi_vals / h - lse_vals.reshape((-1, 1)))
|
|
67
|
+
mean_probs = np.mean(probs, axis=0)
|
|
68
|
+
if ret == 0:
|
|
69
|
+
return cast(float, obj_val)
|
|
70
|
+
elif ret == 1:
|
|
71
|
+
grad = -mean_probs[:-1] + mean_probs[-1]
|
|
72
|
+
return cast(np.ndarray, grad)
|
|
73
|
+
elif ret == 2:
|
|
74
|
+
ranks = (probs.T @ tau_draws / n_draws) / mean_probs.reshape((-1, 1))
|
|
75
|
+
return cast(np.ndarray, ranks)
|
|
76
|
+
else:
|
|
77
|
+
bs_error_abort("Invalid value for ret. Must be 0, 1, or 2.")
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
def objv(v: np.ndarray, args: list) -> float:
|
|
81
|
+
return cast(float, objv_and_grad(v, ret=0))
|
|
82
|
+
|
|
83
|
+
def grad_objv(v: np.ndarray, args: list) -> np.ndarray:
|
|
84
|
+
return cast(np.ndarray, objv_and_grad(v, ret=1))
|
|
85
|
+
|
|
86
|
+
def ranks(v: np.ndarray, args: list) -> np.ndarray:
|
|
87
|
+
return cast(np.ndarray, objv_and_grad(v, ret=2))
|
|
88
|
+
|
|
89
|
+
resv = minimize_free(objv, grad_objv, v_init, args=[])
|
|
90
|
+
if not resv.success:
|
|
91
|
+
bs_error_abort(f"Optimization failed, message: {resv.message}")
|
|
92
|
+
|
|
93
|
+
v_sol = resv.x
|
|
94
|
+
r_x = ranks(v_sol, args=[])
|
|
95
|
+
return r_x
|
|
26
96
|
|
|
27
97
|
|
|
28
98
|
def _compute_ad(y: np.ndarray) -> TwoArrays:
|
|
@@ -54,16 +124,11 @@ def _compute_m_M(
|
|
|
54
124
|
Args:
|
|
55
125
|
v: Dual weights of length ``n``.
|
|
56
126
|
a_mat: Pairwise slope matrix returned by :func:`_compute_ad`.
|
|
57
|
-
dy2: Pairwise differences in the second coordinate.
|
|
127
|
+
dy2: Pairwise differences in the second coordinate (1 on diagonal).
|
|
58
128
|
tau1_nodes: Quadrature nodes for the first coordinate.
|
|
59
129
|
|
|
60
130
|
Returns:
|
|
61
131
|
A tuple ``(m, M)`` with arrays of shape ``(n, len(tau1_nodes))``.
|
|
62
|
-
|
|
63
|
-
Warning:
|
|
64
|
-
The (rare) ``dy2 == 0`` case currently raises warnings in computing ``b_mat``.
|
|
65
|
-
That logic works for the present implementation but should be refactored into a cleaner, more explicit
|
|
66
|
-
branch.
|
|
67
132
|
"""
|
|
68
133
|
dv = np.subtract.outer(v, v)
|
|
69
134
|
b_mat = dv / dy2
|
|
@@ -71,17 +136,12 @@ def _compute_m_M(
|
|
|
71
136
|
EPS = 1e-12
|
|
72
137
|
maskp = dy2 < EPS
|
|
73
138
|
maskm = dy2 > -EPS
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
f_matm = f_mat.copy()
|
|
81
|
-
f_matp[maskp] = 1
|
|
82
|
-
f_matm[maskm] = 0
|
|
83
|
-
m_low[:, i] = np.max(f_matm, axis=1)
|
|
84
|
-
m_high[:, i] = np.min(f_matp, axis=1)
|
|
139
|
+
# Vectorised over all quadrature nodes at once: (n_nodes, n, n)
|
|
140
|
+
f_mats = tau1_nodes[:, None, None] * a_mat[None, :, :] - b_mat[None, :, :]
|
|
141
|
+
f_mats_p = np.where(maskp, 1.0, f_mats)
|
|
142
|
+
f_mats_m = np.where(maskm, 0.0, f_mats)
|
|
143
|
+
m_low = np.max(f_mats_m, axis=2).T # (n, n_nodes)
|
|
144
|
+
m_high = np.min(f_mats_p, axis=2).T
|
|
85
145
|
return np.clip(m_low, 0.0, 1.0), np.clip(m_high, 0.0, 1.0)
|
|
86
146
|
|
|
87
147
|
|
|
@@ -132,11 +192,6 @@ def _objgrad(
|
|
|
132
192
|
tau1_weights = args[4]
|
|
133
193
|
vs1 = np.append(v, -np.sum(v))
|
|
134
194
|
m, M = _compute_m_M(vs1, a_mat, dy2, tau1_nodes)
|
|
135
|
-
# print(f"m is {m}")
|
|
136
|
-
# print(f"M is {M}")
|
|
137
|
-
# import sys
|
|
138
|
-
|
|
139
|
-
# sys.exit(1)
|
|
140
195
|
|
|
141
196
|
EPS = 1e-12
|
|
142
197
|
bivrank = np.zeros((n, 2))
|
|
@@ -190,7 +245,11 @@ def _grad(v: np.ndarray, args: list):
|
|
|
190
245
|
return grad_val
|
|
191
246
|
|
|
192
247
|
|
|
193
|
-
def _solve_for_v(
|
|
248
|
+
def _solve_for_v(
|
|
249
|
+
y: np.ndarray,
|
|
250
|
+
n_nodes: int = 32,
|
|
251
|
+
verbose: bool = False,
|
|
252
|
+
) -> TwoArrays:
|
|
194
253
|
"""Solve the dual optimization problem for the sample.
|
|
195
254
|
|
|
196
255
|
Args:
|
|
@@ -221,16 +280,21 @@ def _solve_for_v(y: np.ndarray, n_nodes: int = 32, verbose: bool = False) -> Two
|
|
|
221
280
|
argsog = [y, a_mat, dy2, tau1_nodes, tau1_weights, verbose]
|
|
222
281
|
|
|
223
282
|
res = minimize_free(_obj, _grad, v0, args=argsog)
|
|
283
|
+
|
|
224
284
|
if verbose:
|
|
225
285
|
print_optimization_results(res, "Minimizing over v")
|
|
226
286
|
|
|
227
287
|
if not res.success:
|
|
228
288
|
bs_error_abort("Problem! the optimization failed.")
|
|
229
|
-
|
|
289
|
+
|
|
290
|
+
vstar_free = res.x
|
|
291
|
+
|
|
230
292
|
if verbose:
|
|
231
293
|
print(f"The final gradient over v is close to 0: error {npmaxabs(res.jac)}")
|
|
232
|
-
|
|
233
|
-
|
|
294
|
+
|
|
295
|
+
_, _, bivranks = cast(tuple, _objgrad(vstar_free, argsog, gr=True))
|
|
296
|
+
|
|
297
|
+
vstar = np.append(vstar_free, -np.sum(vstar_free))
|
|
234
298
|
return cast(np.ndarray, vstar), cast(np.ndarray, bivranks)
|
|
235
299
|
|
|
236
300
|
|
|
@@ -255,11 +319,14 @@ def bivariate_ranks(
|
|
|
255
319
|
bs_error_abort(f"only works for 2-dimensional y, not for {d}")
|
|
256
320
|
|
|
257
321
|
_, bivranks = _solve_for_v(y, n_nodes, verbose)
|
|
258
|
-
return
|
|
322
|
+
return bivranks
|
|
259
323
|
|
|
260
324
|
|
|
261
325
|
def bivariate_quantiles(
|
|
262
|
-
y: np.ndarray,
|
|
326
|
+
y: np.ndarray,
|
|
327
|
+
tau: np.ndarray,
|
|
328
|
+
n_nodes: int = 32,
|
|
329
|
+
verbose: bool = False,
|
|
263
330
|
) -> np.ndarray:
|
|
264
331
|
"""Solve for the dual weights and evaluate bivariate quantiles.
|
|
265
332
|
|
|
@@ -342,6 +342,7 @@ def bs_multivariate_normal_pdf(
|
|
|
342
342
|
bs_error_abort(
|
|
343
343
|
f"cov_mat should be a matrix ({nvars}, {nvars}) not ({nrows}, {ncols})"
|
|
344
344
|
)
|
|
345
|
+
cov_mat = cast(np.ndarray, cov_mat)
|
|
345
346
|
resid = values_x - means_x
|
|
346
347
|
argresid = spla.solve(cov_mat, resid.T)
|
|
347
348
|
argexp = np.zeros(n)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bs-python-utils
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: Utilities programs for my Python code
|
|
5
|
-
Requires-Python: >=3.
|
|
5
|
+
Requires-Python: >=3.13
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
7
|
License-File: LICENSE
|
|
8
8
|
Requires-Dist: altair>=5.5.0
|
|
@@ -26,6 +26,7 @@ Requires-Dist: streamlit>=1.49.1
|
|
|
26
26
|
Requires-Dist: vega-datasets>=0.9.0
|
|
27
27
|
Requires-Dist: colorama>=0.4.6
|
|
28
28
|
Requires-Dist: pyarrow>=23.0.0
|
|
29
|
+
Requires-Dist: scipy-stubs~=1.16.2
|
|
29
30
|
Dynamic: license-file
|
|
30
31
|
|
|
31
32
|
## bs-python-utils
|
|
@@ -49,6 +50,9 @@ Dynamic: license-file
|
|
|
49
50
|
|
|
50
51
|
### Release notes
|
|
51
52
|
|
|
53
|
+
#### 1.1.1 (September 04, 2026)
|
|
54
|
+
Added simulation-based bivariate ranks.
|
|
55
|
+
|
|
52
56
|
#### 1.0.1 (May 9, 2026)
|
|
53
57
|
Fixed badges.
|
|
54
58
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "bs-python-utils"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.1.1"
|
|
4
4
|
description = "Utilities programs for my Python code"
|
|
5
5
|
readme = "README.md"
|
|
6
|
-
requires-python = ">=3.
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
7
|
dependencies = [
|
|
8
8
|
"altair>=5.5.0",
|
|
9
9
|
"altair-saver>=0.5.0",
|
|
@@ -26,8 +26,13 @@ dependencies = [
|
|
|
26
26
|
"vega-datasets>=0.9.0",
|
|
27
27
|
"colorama>=0.4.6",
|
|
28
28
|
"pyarrow>=23.0.0",
|
|
29
|
+
"scipy-stubs~=1.16.2",
|
|
29
30
|
]
|
|
30
31
|
|
|
32
|
+
[build-system]
|
|
33
|
+
requires = ["setuptools>=61"]
|
|
34
|
+
build-backend = "setuptools.build_meta"
|
|
35
|
+
|
|
31
36
|
[tool.setuptools.packages.find]
|
|
32
37
|
where = ["."]
|
|
33
38
|
include = ["bs_python_utils*"]
|
|
@@ -4,6 +4,7 @@ from bs_python_utils.numerical.bsnputils import npmaxabs
|
|
|
4
4
|
from bs_python_utils.stats.bivariate_quantiles import (
|
|
5
5
|
_compute_ad,
|
|
6
6
|
_solve_for_v,
|
|
7
|
+
bivariate_ranks_simul,
|
|
7
8
|
)
|
|
8
9
|
|
|
9
10
|
|
|
@@ -27,6 +28,25 @@ def test_bivariate_ranks():
|
|
|
27
28
|
assert npmaxabs(bivranks - bivranks_th) < 0.1
|
|
28
29
|
|
|
29
30
|
|
|
31
|
+
def test_bivariate_ranks_simul():
|
|
32
|
+
rng = np.random.default_rng(123)
|
|
33
|
+
n = 500
|
|
34
|
+
y = rng.normal(size=(n, 2))
|
|
35
|
+
|
|
36
|
+
bivranks = bivariate_ranks_simul(y, rng)
|
|
37
|
+
|
|
38
|
+
bivranks_th = np.empty_like(bivranks)
|
|
39
|
+
for k in range(n):
|
|
40
|
+
bivranks_th[k, 0] = np.mean(y[:, 0] < y[k, 0]) + 0.5 * np.mean(
|
|
41
|
+
y[:, 0] == y[k, 0]
|
|
42
|
+
)
|
|
43
|
+
bivranks_th[k, 1] = np.mean(y[:, 1] < y[k, 1]) + 0.5 * np.mean(
|
|
44
|
+
y[:, 1] == y[k, 1]
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
assert npmaxabs(bivranks - bivranks_th) < 0.1
|
|
48
|
+
|
|
49
|
+
|
|
30
50
|
def test_compute_ad():
|
|
31
51
|
y = np.array([[0.0, 1.0], [1.0, 1.3], [-1.0, 3.0]])
|
|
32
52
|
a_mat, dy2 = _compute_ad(y)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/examples/examples_seaborn.py
RENAMED
|
File without changes
|
{bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/examples/examples_sklearn.py
RENAMED
|
File without changes
|
{bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/examples/examples_sparse_gaussian.py
RENAMED
|
File without changes
|
|
File without changes
|
{bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/numerical/bs_sparse_gaussian.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils/stats/distance_covariances.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bs_python_utils-1.0.1 → bs_python_utils-1.1.1}/bs_python_utils.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|