bs-python-utils 0.0.1__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.
- bs_python_utils/Timer.py +76 -0
- bs_python_utils/__init__.py +0 -0
- bs_python_utils/bs_altair.py +927 -0
- bs_python_utils/bs_logging.py +100 -0
- bs_python_utils/bs_mathstr.py +130 -0
- bs_python_utils/bs_mem.py +148 -0
- bs_python_utils/bs_opt.py +518 -0
- bs_python_utils/bs_plots.py +2 -0
- bs_python_utils/bs_seaborn.py +174 -0
- bs_python_utils/bs_sparse_gaussian.py +46 -0
- bs_python_utils/bsmplutils.py +34 -0
- bs_python_utils/bsnputils.py +957 -0
- bs_python_utils/bssputils.py +79 -0
- bs_python_utils/bsstats.py +463 -0
- bs_python_utils/bsutils.py +363 -0
- bs_python_utils/distance_covariances.py +258 -0
- bs_python_utils/example_opt.py +71 -0
- bs_python_utils/examples_altair.py +195 -0
- bs_python_utils/examples_distance_covariances.py +32 -0
- bs_python_utils/examples_mem.py +25 -0
- bs_python_utils/examples_seaborn.py +37 -0
- bs_python_utils/examples_sklearn.py +33 -0
- bs_python_utils/pandas_utils.py +239 -0
- bs_python_utils/sklearn_utils.py +74 -0
- bs_python_utils-0.0.1.dist-info/LICENSE +21 -0
- bs_python_utils-0.0.1.dist-info/METADATA +71 -0
- bs_python_utils-0.0.1.dist-info/RECORD +28 -0
- bs_python_utils-0.0.1.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""examples using my Altair functions"""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Callable
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas as pd
|
|
8
|
+
from vega_datasets import data
|
|
9
|
+
|
|
10
|
+
from bs_python_utils.bsutils import mkdir_if_needed
|
|
11
|
+
from bs_python_utils.bs_altair import (
|
|
12
|
+
alt_superposed_lineplot,
|
|
13
|
+
alt_superposed_faceted_lineplot,
|
|
14
|
+
alt_histogram_continuous,
|
|
15
|
+
alt_histogram_continuous,
|
|
16
|
+
alt_histogram_by,
|
|
17
|
+
alt_stacked_area,
|
|
18
|
+
alt_stacked_area_facets,
|
|
19
|
+
alt_scatterplot,
|
|
20
|
+
alt_linked_scatterplots,
|
|
21
|
+
alt_scatterplot_with_histo,
|
|
22
|
+
alt_density,
|
|
23
|
+
alt_faceted_densities,
|
|
24
|
+
alt_tick_plots,
|
|
25
|
+
alt_plot_fun,
|
|
26
|
+
plot_parameterized_estimates,
|
|
27
|
+
plot_true_sim2_facets,
|
|
28
|
+
plot_true_sim_facets,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
FIG_DIRECTORY = "altair_figs"
|
|
33
|
+
save_path = mkdir_if_needed(Path.cwd() / FIG_DIRECTORY)
|
|
34
|
+
|
|
35
|
+
cars = data.cars()
|
|
36
|
+
|
|
37
|
+
ch = alt_superposed_lineplot(
|
|
38
|
+
cars,
|
|
39
|
+
"Horsepower",
|
|
40
|
+
"Weight_in_lbs",
|
|
41
|
+
"Origin",
|
|
42
|
+
save=save_path / "cars_superposed_lineplot",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
ch = alt_superposed_faceted_lineplot(
|
|
46
|
+
cars,
|
|
47
|
+
"Horsepower",
|
|
48
|
+
"Weight_in_lbs",
|
|
49
|
+
"Origin",
|
|
50
|
+
"Year",
|
|
51
|
+
save=save_path / "cars_superposed_faceted_lineplot",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
ch = alt_histogram_continuous(cars, "Horsepower", save=save_path / "cars_histo_cont")
|
|
55
|
+
|
|
56
|
+
ch = alt_histogram_by(
|
|
57
|
+
cars, "Origin", "Horsepower", str_agg="median", save=save_path / "cars_histo_by"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
elec = data.iowa_electricity()
|
|
61
|
+
|
|
62
|
+
ch = alt_stacked_area(
|
|
63
|
+
elec,
|
|
64
|
+
"year",
|
|
65
|
+
"net_generation",
|
|
66
|
+
"source",
|
|
67
|
+
time_series=True,
|
|
68
|
+
title="Generators",
|
|
69
|
+
save=save_path / "elec_stacked_areas",
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
ch = alt_stacked_area_facets(
|
|
73
|
+
cars,
|
|
74
|
+
"Year",
|
|
75
|
+
"Displacement",
|
|
76
|
+
"Name",
|
|
77
|
+
"Origin",
|
|
78
|
+
time_series=True,
|
|
79
|
+
save=save_path / "cars_stacked_areas_facets",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
ch = alt_scatterplot(
|
|
83
|
+
cars,
|
|
84
|
+
"Year",
|
|
85
|
+
"Displacement",
|
|
86
|
+
time_series=True,
|
|
87
|
+
title="Average car displacement",
|
|
88
|
+
aggreg="average",
|
|
89
|
+
save=save_path / "cars_scatter",
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
ch = alt_scatterplot(
|
|
93
|
+
cars,
|
|
94
|
+
"Year",
|
|
95
|
+
"Displacement",
|
|
96
|
+
time_series=True,
|
|
97
|
+
title="Average car displacement",
|
|
98
|
+
aggreg="average",
|
|
99
|
+
save=save_path / "cars_scatter_labx",
|
|
100
|
+
xlabel="Model year",
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
ch = alt_scatterplot(
|
|
104
|
+
cars,
|
|
105
|
+
"Horsepower",
|
|
106
|
+
"Displacement",
|
|
107
|
+
title="Car displacement",
|
|
108
|
+
color="Origin",
|
|
109
|
+
selection=True,
|
|
110
|
+
save=save_path / "cars_scatter_color",
|
|
111
|
+
xlabel="Horsepower",
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
ch = alt_linked_scatterplots(
|
|
115
|
+
cars,
|
|
116
|
+
"Horsepower",
|
|
117
|
+
"Displacement",
|
|
118
|
+
"Miles_per_Gallon",
|
|
119
|
+
"Origin",
|
|
120
|
+
save=save_path / "cars_linked_scatters",
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
ch = alt_scatterplot_with_histo(
|
|
124
|
+
cars,
|
|
125
|
+
"Horsepower",
|
|
126
|
+
"Displacement",
|
|
127
|
+
"Origin",
|
|
128
|
+
save=save_path / "cars_linked_scatter_histo",
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
ch = alt_density(cars, "Horsepower", save=save_path / "horsepower_density")
|
|
132
|
+
|
|
133
|
+
ch = alt_faceted_densities(
|
|
134
|
+
cars, "Horsepower", "Origin", save=save_path / "horsepower_distribs"
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def fnp(x):
|
|
139
|
+
return x * x - 4.0
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
ch = alt_plot_fun(fnp, -2.0, 3.0, save=save_path / "plot_function")
|
|
143
|
+
|
|
144
|
+
# test plot_parameterized_estimates
|
|
145
|
+
nvals = 50
|
|
146
|
+
vals_p = np.arange(nvals) / (nvals - 1.0)
|
|
147
|
+
true_vals = np.column_stack((vals_p, np.ones(nvals)))
|
|
148
|
+
estimates_a = np.random.normal(size=((nvals, 2)), scale=0.2) + vals_p.reshape((-1, 1))
|
|
149
|
+
estimates_b = np.random.normal(size=((nvals, 2)), scale=0.2) + np.ones((nvals, 2))
|
|
150
|
+
estimates = np.zeros((nvals, 2, 2))
|
|
151
|
+
estimates[..., 0] = estimates_a
|
|
152
|
+
estimates[..., 1] = estimates_b
|
|
153
|
+
|
|
154
|
+
ch = plot_parameterized_estimates(
|
|
155
|
+
"Value of p",
|
|
156
|
+
vals_p,
|
|
157
|
+
["a", "b"],
|
|
158
|
+
true_vals,
|
|
159
|
+
["MLE", "MM"],
|
|
160
|
+
estimates,
|
|
161
|
+
colors=["black", "green", "blue"],
|
|
162
|
+
save=save_path / "ppe.html",
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
stats = np.reshape(estimates, (nvals, 4))
|
|
166
|
+
true_vals = stats + np.random.normal(loc=-0.1, scale=0.2, size=stats.shape)
|
|
167
|
+
ch = plot_true_sim_facets(
|
|
168
|
+
"Value of p",
|
|
169
|
+
vals_p,
|
|
170
|
+
["a", "b", "c", "d"],
|
|
171
|
+
true_vals,
|
|
172
|
+
stats,
|
|
173
|
+
colors=["black", "red"],
|
|
174
|
+
ncols=2,
|
|
175
|
+
save=save_path / "ptsf.html",
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
stats2 = stats + np.random.normal(loc=0.1, scale=0.2, size=stats.shape)
|
|
179
|
+
ch = plot_true_sim2_facets(
|
|
180
|
+
"Value of p",
|
|
181
|
+
vals_p,
|
|
182
|
+
["a", "b", "c", "d"],
|
|
183
|
+
true_vals,
|
|
184
|
+
stats,
|
|
185
|
+
stats2,
|
|
186
|
+
colors=["black", "red", "green"],
|
|
187
|
+
ncols=2,
|
|
188
|
+
save=save_path / "pts2f.html",
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
ch = alt_tick_plots(cars, "Weight_in_lbs", save=save_path / "weight_ticks")
|
|
192
|
+
|
|
193
|
+
ch = alt_tick_plots(
|
|
194
|
+
cars, ["Horsepower", "Weight_in_lbs"], save=save_path / "horse_weight_ticks"
|
|
195
|
+
)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""examples using distance_covariances"""
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from bs_python_utils.distance_covariances import pvalue_pdcov, dcov_dcor, pdcov_pdcor
|
|
6
|
+
|
|
7
|
+
# example page 2396 of Szekely and Rizzo 2014
|
|
8
|
+
n = 2000
|
|
9
|
+
do_bootstrap = False
|
|
10
|
+
Z1 = np.random.normal(size=n)
|
|
11
|
+
Z2 = np.random.normal(size=n)
|
|
12
|
+
Z3 = np.random.normal(size=n)
|
|
13
|
+
X = Z1 + Z3
|
|
14
|
+
Y = Z2 + Z3
|
|
15
|
+
Z = Z3
|
|
16
|
+
print("\n\n Test of page 2396")
|
|
17
|
+
dcov_XY = dcov_dcor(X, Y)
|
|
18
|
+
dcov_XZ = dcov_dcor(X, Z)
|
|
19
|
+
dcov_YZ = dcov_dcor(Y, Z)
|
|
20
|
+
pdcov_XYZ = pdcov_pdcor(X, Y, Z)
|
|
21
|
+
print(f" dCor(X, Y)={dcov_XY.dcor}, should be 0.2062 in large samples")
|
|
22
|
+
print(f" dCor(X, Z)={dcov_XZ.dcor}, should be 0.4319 in large samples")
|
|
23
|
+
print(f" dCor(Y, Z)={dcov_YZ.dcor}, should be 0.4319 in large samples")
|
|
24
|
+
print(f" pdCor(X, Y; Z)={pdcov_XYZ.pdcor}, should be 0.0242 in large samples")
|
|
25
|
+
|
|
26
|
+
if do_bootstrap:
|
|
27
|
+
ndraws = 499
|
|
28
|
+
pval = pvalue_pdcov(pdcov_XYZ)
|
|
29
|
+
print(
|
|
30
|
+
f"\n\n test stat={pdcov_XYZ.pdcov_stat: >.2f} has p-value {pval} for"
|
|
31
|
+
f" {ndraws} draws"
|
|
32
|
+
)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
""" example of using bs_mem"""
|
|
2
|
+
import numpy as np
|
|
3
|
+
import tracemalloc
|
|
4
|
+
|
|
5
|
+
from bs_python_utils.bs_mem import (
|
|
6
|
+
memory_display_top,
|
|
7
|
+
memory_display_top_diffs,
|
|
8
|
+
memory_usage,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
tracemalloc.start()
|
|
12
|
+
list_ex = list(range(10000))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
v1 = np.ones(54546)
|
|
16
|
+
snapshot1 = tracemalloc.take_snapshot()
|
|
17
|
+
memory_display_top(snapshot1)
|
|
18
|
+
d = {i: li for (i, li) in enumerate(list_ex)}
|
|
19
|
+
m = np.random.normal(size=(3498, 12))
|
|
20
|
+
memory_usage(5)
|
|
21
|
+
snapshot2 = tracemalloc.take_snapshot()
|
|
22
|
+
memory_display_top_diffs(snapshot1, snapshot2)
|
|
23
|
+
tracemalloc.stop()
|
|
24
|
+
del d
|
|
25
|
+
memory_usage(5)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""examples using my Seaborn functions"""
|
|
2
|
+
|
|
3
|
+
import seaborn as sns
|
|
4
|
+
|
|
5
|
+
from bs_python_utils.bs_seaborn import (
|
|
6
|
+
bs_sns_bar_x_byf,
|
|
7
|
+
bs_sns_bar_x_byfg,
|
|
8
|
+
bs_sns_get_legend,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
cars = sns.load_dataset("mpg")
|
|
12
|
+
|
|
13
|
+
g1 = bs_sns_bar_x_byf(
|
|
14
|
+
cars,
|
|
15
|
+
"horsepower",
|
|
16
|
+
"cylinders",
|
|
17
|
+
label_x="Horsepower",
|
|
18
|
+
label_f="Number of cylinders",
|
|
19
|
+
title="Mean HP by number of cylinders",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
g2 = bs_sns_bar_x_byfg(
|
|
23
|
+
cars,
|
|
24
|
+
"horsepower",
|
|
25
|
+
"cylinders",
|
|
26
|
+
"origin",
|
|
27
|
+
label_x="Horsepower",
|
|
28
|
+
label_f="Number of cylinders",
|
|
29
|
+
label_g="Origin",
|
|
30
|
+
title="Mean HP by number of cylinders and origin",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# change labels in legend
|
|
34
|
+
l2 = bs_sns_get_legend(g2)
|
|
35
|
+
labels2 = ["USA", "Japan", "Europe"]
|
|
36
|
+
for t, lab in zip(l2.texts, labels2, strict=True):
|
|
37
|
+
t.set_text(lab)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""examples using my sklearn code"""
|
|
2
|
+
import numpy as np
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
|
|
5
|
+
|
|
6
|
+
from bs_python_utils.sklearn_utils import skl_npreg_lasso
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
n_obs = 10000
|
|
10
|
+
X1 = -2.0 + 3.0 * np.random.uniform(size=n_obs)
|
|
11
|
+
X2 = np.random.normal(loc=1.0, scale=2.0, size=n_obs)
|
|
12
|
+
y = X1 * X2 * X2 / 100.0 - (X1 / 5.0 - X2 / 3.0) ** 3 + np.random.normal(size=n_obs)
|
|
13
|
+
|
|
14
|
+
X = np.column_stack((X1, X2))
|
|
15
|
+
|
|
16
|
+
plt.style.use("seaborn")
|
|
17
|
+
|
|
18
|
+
degree = 10
|
|
19
|
+
stdsc = StandardScaler()
|
|
20
|
+
sfit = stdsc.fit(X)
|
|
21
|
+
X_scaled = sfit.transform(X)
|
|
22
|
+
pf = PolynomialFeatures(degree)
|
|
23
|
+
# Create the features and fit
|
|
24
|
+
X_poly = pf.fit_transform(X_scaled)
|
|
25
|
+
|
|
26
|
+
y_pred = skl_npreg_lasso(y, X, alpha=0.001)
|
|
27
|
+
|
|
28
|
+
plt.clf()
|
|
29
|
+
|
|
30
|
+
ax = plt.axes()
|
|
31
|
+
ax.scatter(y, y_pred)
|
|
32
|
+
ax.plot(y, y, "-r")
|
|
33
|
+
plt.show()
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"""Utility functions for pandas """
|
|
2
|
+
|
|
3
|
+
from itertools import product
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pandas as pd
|
|
7
|
+
|
|
8
|
+
from bs_python_utils.bsnputils import bs_error_abort, check_vector_or_matrix
|
|
9
|
+
from bs_python_utils.bsutils import print_stars
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def bspd_print(
|
|
13
|
+
df: pd.DataFrame,
|
|
14
|
+
s: str | None = "",
|
|
15
|
+
max_rows: int | None = None,
|
|
16
|
+
max_cols: int | None = None,
|
|
17
|
+
precision: int | None = None,
|
|
18
|
+
) -> None:
|
|
19
|
+
"""Pretty-prints a data frame
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
df: any data frame
|
|
23
|
+
s: an optional title string
|
|
24
|
+
max_rows: maximum number of rows to print (all by default)
|
|
25
|
+
max_cols: maximum number of columns to print (all by default)
|
|
26
|
+
precision: of numbers. 3 digits by default.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
nothing.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
print_stars(s)
|
|
33
|
+
with pd.option_context(
|
|
34
|
+
"display.max_rows",
|
|
35
|
+
max_rows,
|
|
36
|
+
"display.max_columns",
|
|
37
|
+
max_cols,
|
|
38
|
+
"display.precision",
|
|
39
|
+
precision,
|
|
40
|
+
):
|
|
41
|
+
print(df)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def bspd_cross_products(
|
|
45
|
+
df: pd.DataFrame,
|
|
46
|
+
l1: list[str],
|
|
47
|
+
l2: list[str] | None = None,
|
|
48
|
+
with_squares: bool | None = True,
|
|
49
|
+
) -> pd.DataFrame:
|
|
50
|
+
"""Returns a DataFrame with cross-products of the variables of `df`
|
|
51
|
+
whose names are in `l1` and `l2`.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
df: any data frame
|
|
55
|
+
l1: a list of names of variables that belong to `df`
|
|
56
|
+
l2: ibidem; `l1` by default
|
|
57
|
+
with_squares: if `False`, we drop the squares. `True` by default.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
the data frame of cross-products with concatenated names.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
lp2 = l1 if l2 is None else l2
|
|
64
|
+
l12 = list(product(l1, lp2))
|
|
65
|
+
cross_pairs = [[x[0], x[1]] for x in l12 if x[0] != x[1]]
|
|
66
|
+
unique_pairs = []
|
|
67
|
+
for _i, c in enumerate(cross_pairs):
|
|
68
|
+
print(c)
|
|
69
|
+
c_ordered = c if c[0] < c[1] else list(reversed(c))
|
|
70
|
+
print(c_ordered)
|
|
71
|
+
if c_ordered not in unique_pairs:
|
|
72
|
+
unique_pairs.append(c_ordered)
|
|
73
|
+
print(unique_pairs)
|
|
74
|
+
|
|
75
|
+
col_names = sorted([(x[0], x[1], f"{x[0]}*{x[1]}") for x in unique_pairs])
|
|
76
|
+
|
|
77
|
+
if with_squares:
|
|
78
|
+
col_names_squares = sorted(
|
|
79
|
+
[(x[0], x[1], f"{x[0]}**2") for x in l12 if x[0] == x[1]]
|
|
80
|
+
)
|
|
81
|
+
col_names += col_names_squares
|
|
82
|
+
|
|
83
|
+
df_cprods = pd.DataFrame(
|
|
84
|
+
{col_name: df[x0] * df[x1] for (x0, x1, col_name) in col_names}
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
return df_cprods
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _list_str(names: str | list[str], suffix: str = None) -> list[str]:
|
|
91
|
+
"""make a list of strings with possibly the added suffix
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
names: a string or a list of strings
|
|
95
|
+
suffix: a string, if any
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
a list of the strings in names, with the suffix if specified
|
|
99
|
+
"""
|
|
100
|
+
if isinstance(names, str):
|
|
101
|
+
if suffix is not None:
|
|
102
|
+
return [names + suffix]
|
|
103
|
+
else:
|
|
104
|
+
return [names]
|
|
105
|
+
elif isinstance(names, list):
|
|
106
|
+
if suffix is not None:
|
|
107
|
+
return [name + suffix for name in names]
|
|
108
|
+
else:
|
|
109
|
+
return names
|
|
110
|
+
else:
|
|
111
|
+
bs_error_abort("names should be a string or a list of strings.")
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _check_colnames(col_names: str | list[str] | list[str | list[str]], n_T: int):
|
|
115
|
+
if not isinstance(col_names, list):
|
|
116
|
+
bs_error_abort("If T is a list, then col_names should be a list too.")
|
|
117
|
+
elif len(col_names) != n_T:
|
|
118
|
+
bs_error_abort(f"T has {n_T} elements but col_names has {len(col_names)}.")
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def bspd_statsdf(
|
|
122
|
+
T: np.ndarray | list[np.ndarray],
|
|
123
|
+
col_names: str | list[str] | list[str | list[str]],
|
|
124
|
+
) -> pd.DataFrame:
|
|
125
|
+
"""
|
|
126
|
+
make a dataframe with columns from the array(s) in T and names from col_names
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
T: a list of n_T matrices or vectors with N rows, or a matrix or a vector with N rows
|
|
130
|
+
col_names: a list of n_T name objects; a name object must be a string or a list of strings,
|
|
131
|
+
with the names for the column(s) of the corresponding T matrix
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
a dataframe with the named columns
|
|
135
|
+
"""
|
|
136
|
+
if isinstance(T, list):
|
|
137
|
+
n_T = len(T)
|
|
138
|
+
_check_colnames(col_names, n_T)
|
|
139
|
+
shape_T = []
|
|
140
|
+
for i in range(n_T):
|
|
141
|
+
shape_T.append(T[i].shape)
|
|
142
|
+
set_nrows = {shape_i[0] for shape_i in shape_T}
|
|
143
|
+
if len(set_nrows) > 1:
|
|
144
|
+
bs_error_abort("All T arrays should have the same number of rows.")
|
|
145
|
+
big_T = T[0]
|
|
146
|
+
big_names = _list_str(col_names[0], suffix="_1")
|
|
147
|
+
for i in range(1, n_T):
|
|
148
|
+
big_T = np.column_stack((big_T, T[i]))
|
|
149
|
+
big_names.extend(_list_str(col_names[i], suffix=f"_{i+1}"))
|
|
150
|
+
|
|
151
|
+
df = pd.DataFrame(big_T, columns=big_names, copy=True)
|
|
152
|
+
|
|
153
|
+
else: # only one element in T
|
|
154
|
+
ndims_T = check_vector_or_matrix(T)
|
|
155
|
+
if ndims_T == 1:
|
|
156
|
+
if not isinstance(col_names, str):
|
|
157
|
+
bs_error_abort(f"T is a vector but col_names is {col_names}")
|
|
158
|
+
df = pd.DataFrame(T, columns=[col_names], copy=True)
|
|
159
|
+
elif ndims_T == 2:
|
|
160
|
+
N, K = T.shape
|
|
161
|
+
K2 = len(col_names)
|
|
162
|
+
if K2 != K:
|
|
163
|
+
bs_error_abort(f"T is {T.shape} but col_names has {K2} elements")
|
|
164
|
+
df = pd.DataFrame(T, columns=col_names, copy=True)
|
|
165
|
+
|
|
166
|
+
return df
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _check_names_n(col_names: list[str]) -> list[int]:
|
|
170
|
+
"""
|
|
171
|
+
Tests that if a name in col_names ends with `_n`, with `n` an integer, then all names do
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
col_names: a list of names
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
the list of the values of `n` if all names in the list end in `_n`; `[]` if none of them does.
|
|
178
|
+
Aborts otherwise.
|
|
179
|
+
"""
|
|
180
|
+
underscore_n = []
|
|
181
|
+
ending_integers = []
|
|
182
|
+
for name in col_names:
|
|
183
|
+
split_name = name.split("_")
|
|
184
|
+
len_split = len(split_name)
|
|
185
|
+
if len_split > 1: # found at least one '_'
|
|
186
|
+
last_bit = split_name[-1]
|
|
187
|
+
try:
|
|
188
|
+
ending_int = int(last_bit)
|
|
189
|
+
underscore_n.append(True) # ends with '_n'
|
|
190
|
+
ending_integers.append(ending_int)
|
|
191
|
+
except ValueError:
|
|
192
|
+
underscore_n.append(False)
|
|
193
|
+
else:
|
|
194
|
+
underscore_n.append(False)
|
|
195
|
+
|
|
196
|
+
values_integers = set(ending_integers)
|
|
197
|
+
n_values_integers = len(values_integers)
|
|
198
|
+
|
|
199
|
+
if n_values_integers == 0 or all(
|
|
200
|
+
underscore_n
|
|
201
|
+
): # none ends in '_n' or all end in '_n'
|
|
202
|
+
return list(values_integers)
|
|
203
|
+
else:
|
|
204
|
+
bs_error_abort(
|
|
205
|
+
"If a column name ends with '_n' where n is an integer, then all should."
|
|
206
|
+
)
|
|
207
|
+
return [] # for mypy
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def bspd_prepareplot(df: pd.DataFrame) -> pd.DataFrame:
|
|
211
|
+
"""
|
|
212
|
+
Args:
|
|
213
|
+
df: any dataframe whose column names either all end in '_n' for n an integer, or none does
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
a properly melted dataframe for plotting, with columns 'Sample', 'Statistic', 'Value',
|
|
217
|
+
and 'Group' if there are several integers
|
|
218
|
+
"""
|
|
219
|
+
# check the names of the columns
|
|
220
|
+
values_integers = _check_names_n(df.columns)
|
|
221
|
+
n_values_integers = len(values_integers)
|
|
222
|
+
|
|
223
|
+
df2 = df.copy()
|
|
224
|
+
df2["Sample"] = np.arange(df.shape[0])
|
|
225
|
+
dfm = pd.melt(
|
|
226
|
+
df2,
|
|
227
|
+
id_vars="Sample",
|
|
228
|
+
value_vars=list(df.columns),
|
|
229
|
+
var_name="Statistic",
|
|
230
|
+
value_name="Value",
|
|
231
|
+
)
|
|
232
|
+
if n_values_integers in [0, 1]:
|
|
233
|
+
return dfm
|
|
234
|
+
else: # at least two different groups of statistics
|
|
235
|
+
stat_group = dfm["Statistic"].str.split("_", n=1, expand=True)
|
|
236
|
+
dfm.drop(columns=["Statistic"], inplace=True)
|
|
237
|
+
dfm["Statistic"] = stat_group[0]
|
|
238
|
+
dfm["Group"] = stat_group[1]
|
|
239
|
+
return dfm
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Cntains various `scikit-learn` utility programs.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from itertools import cycle
|
|
6
|
+
from typing import cast
|
|
7
|
+
|
|
8
|
+
import matplotlib.pyplot as plt
|
|
9
|
+
import numpy as np
|
|
10
|
+
from sklearn.linear_model import Lasso, lasso_path
|
|
11
|
+
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def skl_npreg_lasso(
|
|
15
|
+
y: np.ndarray, X: np.ndarray, alpha: float, degree: int = 4
|
|
16
|
+
) -> np.ndarray:
|
|
17
|
+
"""
|
|
18
|
+
Lasso nonparametric regression of `y` over polynomials of `X`
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
y: shape `(nobs)`
|
|
22
|
+
X: shape `(nobs, nfeatures)`
|
|
23
|
+
alpha: Lasso penalty parameter
|
|
24
|
+
degree: highest total degree
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
the `(nobs)` array `E(y\\vert X)` over the sample
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
# first scale the X variables
|
|
31
|
+
stdsc = StandardScaler()
|
|
32
|
+
sfit = stdsc.fit(X)
|
|
33
|
+
X_scaled = sfit.transform(X)
|
|
34
|
+
pf = PolynomialFeatures(degree)
|
|
35
|
+
# Create the features and fit
|
|
36
|
+
X_poly = pf.fit_transform(X_scaled)
|
|
37
|
+
# now run Lasso
|
|
38
|
+
reg = Lasso(alpha=alpha).fit(X_poly, y)
|
|
39
|
+
expy_X = reg.predict(X_poly)
|
|
40
|
+
return cast(np.ndarray, expy_X)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def plot_lasso_path(y: np.ndarray, X: np.ndarray, eps: float = 1e-3) -> None:
|
|
44
|
+
"""
|
|
45
|
+
plot Lasso coefficient paths
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
y: shape `(nobs)`
|
|
49
|
+
X: shape `(nobs, nfeatures)`
|
|
50
|
+
eps: length of path
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
plots the paths
|
|
54
|
+
"""
|
|
55
|
+
# Compute paths
|
|
56
|
+
print("Computing regularization path using the lasso...")
|
|
57
|
+
alphas_lasso, coefs_lasso, _ = lasso_path(X, y, eps)
|
|
58
|
+
|
|
59
|
+
plt.clf()
|
|
60
|
+
# Display results
|
|
61
|
+
plt.figure(1)
|
|
62
|
+
colors = cycle(["b", "r", "g", "c", "k"])
|
|
63
|
+
neg_log_alphas_lasso = -np.log10(alphas_lasso)
|
|
64
|
+
for coef_l, c in zip(coefs_lasso, colors, strict=True):
|
|
65
|
+
plt.plot(neg_log_alphas_lasso, coef_l, c=c)
|
|
66
|
+
|
|
67
|
+
plt.xlabel("-Log(alpha)")
|
|
68
|
+
plt.ylabel("coefficients")
|
|
69
|
+
plt.title("Lasso Paths")
|
|
70
|
+
plt.axis("tight")
|
|
71
|
+
|
|
72
|
+
plt.show()
|
|
73
|
+
|
|
74
|
+
return
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023, Bernard Salanie
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|