arborenum 0.2.1__tar.gz → 0.2.2__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.
- {arborenum-0.2.1/src/arborenum.egg-info → arborenum-0.2.2}/PKG-INFO +1 -1
- {arborenum-0.2.1 → arborenum-0.2.2}/pyproject.toml +1 -1
- {arborenum-0.2.1 → arborenum-0.2.2}/src/arborenum/__init__.py +509 -0
- {arborenum-0.2.1 → arborenum-0.2.2}/src/arborenum/_core.cpp +138 -6
- {arborenum-0.2.1 → arborenum-0.2.2}/src/arborenum/cpp/arborenum.cpp +1280 -16
- {arborenum-0.2.1 → arborenum-0.2.2}/src/arborenum/cpp/rid.cpp +317 -107
- {arborenum-0.2.1 → arborenum-0.2.2/src/arborenum.egg-info}/PKG-INFO +1 -1
- {arborenum-0.2.1 → arborenum-0.2.2}/MANIFEST.in +0 -0
- {arborenum-0.2.1 → arborenum-0.2.2}/README.md +0 -0
- {arborenum-0.2.1 → arborenum-0.2.2}/setup.cfg +0 -0
- {arborenum-0.2.1 → arborenum-0.2.2}/setup.py +0 -0
- {arborenum-0.2.1 → arborenum-0.2.2}/src/arborenum/_threshold_guessing.py +0 -0
- {arborenum-0.2.1 → arborenum-0.2.2}/src/arborenum.egg-info/SOURCES.txt +0 -0
- {arborenum-0.2.1 → arborenum-0.2.2}/src/arborenum.egg-info/dependency_links.txt +0 -0
- {arborenum-0.2.1 → arborenum-0.2.2}/src/arborenum.egg-info/requires.txt +0 -0
- {arborenum-0.2.1 → arborenum-0.2.2}/src/arborenum.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "arborenum"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "Exact, approximate, and anytime decision tree Rashomon sets over binary and continuous features"
|
|
9
9
|
requires-python = ">=3.9"
|
|
10
10
|
dependencies = [
|
|
@@ -10,10 +10,35 @@ from ._core import (
|
|
|
10
10
|
)
|
|
11
11
|
from ._threshold_guessing import ThresholdGuessBinarizer
|
|
12
12
|
|
|
13
|
+
import json
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from importlib.resources import files
|
|
16
|
+
|
|
13
17
|
DEFER_PREDICTION = -1
|
|
14
18
|
|
|
15
19
|
__all__ = ["ArborEnum", "ThresholdGuessBinarizer", "DEFER_PREDICTION"]
|
|
16
20
|
|
|
21
|
+
def _json_safe(x):
|
|
22
|
+
if isinstance(x, np.integer):
|
|
23
|
+
return int(x)
|
|
24
|
+
|
|
25
|
+
if isinstance(x, np.floating):
|
|
26
|
+
return float(x)
|
|
27
|
+
|
|
28
|
+
if isinstance(x, np.ndarray):
|
|
29
|
+
return x.tolist()
|
|
30
|
+
|
|
31
|
+
if isinstance(x, dict):
|
|
32
|
+
return {
|
|
33
|
+
str(k): _json_safe(v)
|
|
34
|
+
for k, v in x.items()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if isinstance(x, (list, tuple)):
|
|
38
|
+
return [_json_safe(v) for v in x]
|
|
39
|
+
|
|
40
|
+
return x
|
|
41
|
+
|
|
17
42
|
def _normalize_key(s: str) -> str:
|
|
18
43
|
# lower, trim, and make separators uniform
|
|
19
44
|
s = str(s).strip().lower()
|
|
@@ -727,6 +752,13 @@ class ArborEnum:
|
|
|
727
752
|
eta_defer=eta_defer,
|
|
728
753
|
)
|
|
729
754
|
|
|
755
|
+
self.lambda_reg_ = float(lambda_reg)
|
|
756
|
+
self.depth_budget_ = int(depth_budget)
|
|
757
|
+
self.rashomon_mult_ = float(rashomon_mult)
|
|
758
|
+
self.multiplicative_slack_ = float(multiplicative_slack)
|
|
759
|
+
self.lookahead_k_ = int(lookahead_k)
|
|
760
|
+
self.eta_defer_ = float(eta_defer)
|
|
761
|
+
|
|
730
762
|
return self.fit_prepared(
|
|
731
763
|
lambda_reg=lambda_reg,
|
|
732
764
|
depth_budget=depth_budget,
|
|
@@ -1690,6 +1722,13 @@ class ArborEnum:
|
|
|
1690
1722
|
thresh = round((1.0 + mult) * min_obj)
|
|
1691
1723
|
return sum(cnt for obj, cnt in hist if obj <= thresh)
|
|
1692
1724
|
|
|
1725
|
+
def alternating_optimization(self, max_iterations=10):
|
|
1726
|
+
return int(
|
|
1727
|
+
self._model.alternating_optimization(
|
|
1728
|
+
int(max_iterations)
|
|
1729
|
+
)
|
|
1730
|
+
)
|
|
1731
|
+
|
|
1693
1732
|
def get_continuous_starts(self):
|
|
1694
1733
|
# return the internal column index at which each continuous threshold group begins.
|
|
1695
1734
|
return list(self._model.get_continuous_starts())
|
|
@@ -1976,6 +2015,458 @@ class ArborEnum:
|
|
|
1976
2015
|
)
|
|
1977
2016
|
|
|
1978
2017
|
return names
|
|
2018
|
+
|
|
2019
|
+
def export_andor_graph(self, as_dict=True):
|
|
2020
|
+
g = self._model.export_andor_graph()
|
|
2021
|
+
|
|
2022
|
+
if not as_dict:
|
|
2023
|
+
return g
|
|
2024
|
+
|
|
2025
|
+
return {
|
|
2026
|
+
"root_trie_id": int(g.root_trie_id),
|
|
2027
|
+
|
|
2028
|
+
"trie_nodes": [
|
|
2029
|
+
{
|
|
2030
|
+
"id": int(node.id),
|
|
2031
|
+
"budget": int(node.budget),
|
|
2032
|
+
"min_objective": int(node.min_objective),
|
|
2033
|
+
"subproblem_size": int(
|
|
2034
|
+
node.subproblem_size
|
|
2035
|
+
),
|
|
2036
|
+
"leaf_ids": [
|
|
2037
|
+
int(x)
|
|
2038
|
+
for x in node.leaf_ids
|
|
2039
|
+
],
|
|
2040
|
+
"split_ids": [
|
|
2041
|
+
int(x)
|
|
2042
|
+
for x in node.split_ids
|
|
2043
|
+
],
|
|
2044
|
+
}
|
|
2045
|
+
for node in g.trie_nodes
|
|
2046
|
+
],
|
|
2047
|
+
|
|
2048
|
+
"split_nodes": [
|
|
2049
|
+
{
|
|
2050
|
+
"id": int(split.id),
|
|
2051
|
+
"parent_trie_id": int(
|
|
2052
|
+
split.parent_trie_id
|
|
2053
|
+
),
|
|
2054
|
+
"feature": int(split.feature),
|
|
2055
|
+
"left_trie_id": int(
|
|
2056
|
+
split.left_trie_id
|
|
2057
|
+
),
|
|
2058
|
+
"right_trie_id": int(
|
|
2059
|
+
split.right_trie_id
|
|
2060
|
+
),
|
|
2061
|
+
"min_objective": int(
|
|
2062
|
+
split.min_objective
|
|
2063
|
+
),
|
|
2064
|
+
}
|
|
2065
|
+
for split in g.split_nodes
|
|
2066
|
+
],
|
|
2067
|
+
|
|
2068
|
+
"leaf_nodes": [
|
|
2069
|
+
{
|
|
2070
|
+
"id": int(leaf.id),
|
|
2071
|
+
"parent_trie_id": int(
|
|
2072
|
+
leaf.parent_trie_id
|
|
2073
|
+
),
|
|
2074
|
+
"prediction": int(
|
|
2075
|
+
leaf.prediction
|
|
2076
|
+
),
|
|
2077
|
+
"loss": int(leaf.loss),
|
|
2078
|
+
"subproblem_size": int(
|
|
2079
|
+
leaf.subproblem_size
|
|
2080
|
+
),
|
|
2081
|
+
}
|
|
2082
|
+
for leaf in g.leaf_nodes
|
|
2083
|
+
],
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
def _get_builder_feature_metadata(
|
|
2087
|
+
self,
|
|
2088
|
+
feature_names=None,
|
|
2089
|
+
):
|
|
2090
|
+
if self.binary_feature_specs_ is None:
|
|
2091
|
+
raise RuntimeError(
|
|
2092
|
+
"Feature metadata is unavailable. "
|
|
2093
|
+
"Fit ArborEnum through fit(X, y, ...) first."
|
|
2094
|
+
)
|
|
2095
|
+
|
|
2096
|
+
if self.continuous_feature_indices_ is None:
|
|
2097
|
+
raise RuntimeError(
|
|
2098
|
+
"Continuous feature metadata is unavailable."
|
|
2099
|
+
)
|
|
2100
|
+
|
|
2101
|
+
if self.n_features_in_ is None:
|
|
2102
|
+
raise RuntimeError(
|
|
2103
|
+
"Original feature count is unavailable."
|
|
2104
|
+
)
|
|
2105
|
+
|
|
2106
|
+
if feature_names is None:
|
|
2107
|
+
feature_names = list(self.feature_names_in_)
|
|
2108
|
+
else:
|
|
2109
|
+
feature_names = list(feature_names)
|
|
2110
|
+
|
|
2111
|
+
if len(feature_names) != self.n_features_in_:
|
|
2112
|
+
raise ValueError(
|
|
2113
|
+
"feature_names must contain one name for "
|
|
2114
|
+
"every original input feature."
|
|
2115
|
+
)
|
|
2116
|
+
|
|
2117
|
+
# exactly aligned with our internal ids
|
|
2118
|
+
internal_feature_names = (
|
|
2119
|
+
self.get_internal_feature_names(feature_names)
|
|
2120
|
+
)
|
|
2121
|
+
|
|
2122
|
+
thresholds = {}
|
|
2123
|
+
feature_registry = []
|
|
2124
|
+
|
|
2125
|
+
# low cardinality features
|
|
2126
|
+
for internal_feature, spec in enumerate(
|
|
2127
|
+
self.binary_feature_specs_
|
|
2128
|
+
):
|
|
2129
|
+
original_feature = int(
|
|
2130
|
+
spec["original_feature"]
|
|
2131
|
+
)
|
|
2132
|
+
|
|
2133
|
+
cutpoint = float(spec["cutpoint"])
|
|
2134
|
+
|
|
2135
|
+
thresholds[internal_feature] = cutpoint
|
|
2136
|
+
|
|
2137
|
+
feature_registry.append(
|
|
2138
|
+
{
|
|
2139
|
+
"internalFeature": internal_feature,
|
|
2140
|
+
"originalFeature": original_feature,
|
|
2141
|
+
"originalName": str(
|
|
2142
|
+
feature_names[original_feature]
|
|
2143
|
+
),
|
|
2144
|
+
"threshold": cutpoint,
|
|
2145
|
+
"kind": "binary_threshold",
|
|
2146
|
+
"continuousGroup": None,
|
|
2147
|
+
}
|
|
2148
|
+
)
|
|
2149
|
+
|
|
2150
|
+
# cpp generated continuous groups
|
|
2151
|
+
continuous_groups = {}
|
|
2152
|
+
|
|
2153
|
+
starts = [
|
|
2154
|
+
int(x)
|
|
2155
|
+
for x in self._model.get_continuous_starts()
|
|
2156
|
+
]
|
|
2157
|
+
|
|
2158
|
+
for group, original_feature in enumerate(
|
|
2159
|
+
self.continuous_feature_indices_
|
|
2160
|
+
):
|
|
2161
|
+
original_feature = int(original_feature)
|
|
2162
|
+
|
|
2163
|
+
cutpoints = [
|
|
2164
|
+
float(x)
|
|
2165
|
+
for x in self.get_continuous_cutpoints(group)
|
|
2166
|
+
]
|
|
2167
|
+
|
|
2168
|
+
start = starts[group]
|
|
2169
|
+
|
|
2170
|
+
internal_columns = []
|
|
2171
|
+
|
|
2172
|
+
for offset, cutpoint in enumerate(cutpoints):
|
|
2173
|
+
internal_feature = start + offset
|
|
2174
|
+
|
|
2175
|
+
thresholds[internal_feature] = cutpoint
|
|
2176
|
+
internal_columns.append(internal_feature)
|
|
2177
|
+
|
|
2178
|
+
feature_registry.append(
|
|
2179
|
+
{
|
|
2180
|
+
"internalFeature": internal_feature,
|
|
2181
|
+
"originalFeature": original_feature,
|
|
2182
|
+
"originalName": str(
|
|
2183
|
+
feature_names[original_feature]
|
|
2184
|
+
),
|
|
2185
|
+
"threshold": cutpoint,
|
|
2186
|
+
"kind": "continuous_threshold",
|
|
2187
|
+
"continuousGroup": group,
|
|
2188
|
+
}
|
|
2189
|
+
)
|
|
2190
|
+
|
|
2191
|
+
continuous_groups[
|
|
2192
|
+
str(feature_names[original_feature])
|
|
2193
|
+
] = internal_columns
|
|
2194
|
+
|
|
2195
|
+
# feature_registry[i] describes split.feature == i. this should already be the case by how we went over.
|
|
2196
|
+
feature_registry.sort(
|
|
2197
|
+
key=lambda x: x["internalFeature"]
|
|
2198
|
+
)
|
|
2199
|
+
|
|
2200
|
+
for expected, entry in enumerate(feature_registry):
|
|
2201
|
+
if entry["internalFeature"] != expected:
|
|
2202
|
+
raise RuntimeError(
|
|
2203
|
+
"Builder feature metadata is not aligned "
|
|
2204
|
+
"with ArborEnum internal feature indices: "
|
|
2205
|
+
f"expected {expected}, got "
|
|
2206
|
+
f"{entry['internalFeature']}."
|
|
2207
|
+
)
|
|
2208
|
+
|
|
2209
|
+
if len(internal_feature_names) != len(
|
|
2210
|
+
feature_registry
|
|
2211
|
+
):
|
|
2212
|
+
raise RuntimeError(
|
|
2213
|
+
"Internal feature-name count does not match "
|
|
2214
|
+
"the builder feature registry."
|
|
2215
|
+
)
|
|
2216
|
+
|
|
2217
|
+
if len(starts) != len(
|
|
2218
|
+
self.continuous_feature_indices_
|
|
2219
|
+
):
|
|
2220
|
+
raise RuntimeError(
|
|
2221
|
+
"Continuous-group metadata is inconsistent: "
|
|
2222
|
+
"the number of C++ continuous groups does not "
|
|
2223
|
+
"match continuous_feature_indices_."
|
|
2224
|
+
)
|
|
2225
|
+
|
|
2226
|
+
|
|
2227
|
+
# feature_registry says for each threshold
|
|
2228
|
+
'''
|
|
2229
|
+
internalFeature: 0,
|
|
2230
|
+
originalFeature: 1,
|
|
2231
|
+
originalName: sex,
|
|
2232
|
+
threshold: 0.5,
|
|
2233
|
+
kind: binary_threshold,
|
|
2234
|
+
continuousGroup: None,
|
|
2235
|
+
'''
|
|
2236
|
+
|
|
2237
|
+
return {
|
|
2238
|
+
"featureRegistry": feature_registry,
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
|
|
2242
|
+
def save_builder_payload(
|
|
2243
|
+
self,
|
|
2244
|
+
path,
|
|
2245
|
+
*,
|
|
2246
|
+
feature_names=None,
|
|
2247
|
+
feature_descriptions=None,
|
|
2248
|
+
lambda_reg=None,
|
|
2249
|
+
depth_budget=None,
|
|
2250
|
+
rashomon_mult=None,
|
|
2251
|
+
multiplicative_slack=None,
|
|
2252
|
+
lookahead_k=None,
|
|
2253
|
+
root_n=None,
|
|
2254
|
+
gamma=None,
|
|
2255
|
+
eta_defer=None,
|
|
2256
|
+
indent=2,
|
|
2257
|
+
):
|
|
2258
|
+
graph = self.export_andor_graph(
|
|
2259
|
+
as_dict=True
|
|
2260
|
+
)
|
|
2261
|
+
|
|
2262
|
+
feature_meta = (
|
|
2263
|
+
self._get_builder_feature_metadata(
|
|
2264
|
+
feature_names=feature_names
|
|
2265
|
+
)
|
|
2266
|
+
)
|
|
2267
|
+
|
|
2268
|
+
if lambda_reg is None:
|
|
2269
|
+
lambda_reg = getattr(
|
|
2270
|
+
self,
|
|
2271
|
+
"lambda_reg_",
|
|
2272
|
+
None,
|
|
2273
|
+
)
|
|
2274
|
+
|
|
2275
|
+
if depth_budget is None:
|
|
2276
|
+
depth_budget = getattr(
|
|
2277
|
+
self,
|
|
2278
|
+
"depth_budget_",
|
|
2279
|
+
None,
|
|
2280
|
+
)
|
|
2281
|
+
|
|
2282
|
+
if rashomon_mult is None:
|
|
2283
|
+
rashomon_mult = getattr(
|
|
2284
|
+
self,
|
|
2285
|
+
"rashomon_mult_",
|
|
2286
|
+
None,
|
|
2287
|
+
)
|
|
2288
|
+
|
|
2289
|
+
if multiplicative_slack is None:
|
|
2290
|
+
multiplicative_slack = getattr(
|
|
2291
|
+
self,
|
|
2292
|
+
"multiplicative_slack_",
|
|
2293
|
+
None,
|
|
2294
|
+
)
|
|
2295
|
+
|
|
2296
|
+
if lookahead_k is None:
|
|
2297
|
+
lookahead_k = getattr(
|
|
2298
|
+
self,
|
|
2299
|
+
"lookahead_k_",
|
|
2300
|
+
None,
|
|
2301
|
+
)
|
|
2302
|
+
|
|
2303
|
+
if eta_defer is None:
|
|
2304
|
+
eta_defer = getattr(
|
|
2305
|
+
self,
|
|
2306
|
+
"eta_defer_",
|
|
2307
|
+
None,
|
|
2308
|
+
)
|
|
2309
|
+
|
|
2310
|
+
meta = {
|
|
2311
|
+
**feature_meta,
|
|
2312
|
+
|
|
2313
|
+
"featureDescriptions": feature_descriptions,
|
|
2314
|
+
|
|
2315
|
+
"lambda_reg": lambda_reg,
|
|
2316
|
+
"depth_budget": depth_budget,
|
|
2317
|
+
"rashomon_mult": rashomon_mult,
|
|
2318
|
+
"multiplicative_slack": multiplicative_slack,
|
|
2319
|
+
"lookahead_k": lookahead_k,
|
|
2320
|
+
"root_n": root_n,
|
|
2321
|
+
"gamma": gamma,
|
|
2322
|
+
"eta_defer": eta_defer,
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2325
|
+
root_id = int(
|
|
2326
|
+
graph.get("root_trie_id", 0)
|
|
2327
|
+
)
|
|
2328
|
+
|
|
2329
|
+
root = next(
|
|
2330
|
+
(
|
|
2331
|
+
node
|
|
2332
|
+
for node in graph.get(
|
|
2333
|
+
"trie_nodes",
|
|
2334
|
+
[],
|
|
2335
|
+
)
|
|
2336
|
+
if int(node.get("id", -1)) == root_id
|
|
2337
|
+
),
|
|
2338
|
+
None,
|
|
2339
|
+
)
|
|
2340
|
+
|
|
2341
|
+
if root is not None:
|
|
2342
|
+
meta["root_budget"] = root.get(
|
|
2343
|
+
"budget"
|
|
2344
|
+
)
|
|
2345
|
+
|
|
2346
|
+
meta["root_min_objective"] = root.get(
|
|
2347
|
+
"min_objective"
|
|
2348
|
+
)
|
|
2349
|
+
|
|
2350
|
+
meta["root_subproblem_size"] = root.get(
|
|
2351
|
+
"subproblem_size"
|
|
2352
|
+
)
|
|
2353
|
+
|
|
2354
|
+
if root_n is None:
|
|
2355
|
+
meta["root_n"] = root.get(
|
|
2356
|
+
"subproblem_size"
|
|
2357
|
+
)
|
|
2358
|
+
|
|
2359
|
+
if (
|
|
2360
|
+
gamma is None
|
|
2361
|
+
and lambda_reg is not None
|
|
2362
|
+
and meta.get("root_n") is not None
|
|
2363
|
+
):
|
|
2364
|
+
meta["gamma"] = int(
|
|
2365
|
+
round(
|
|
2366
|
+
float(lambda_reg)
|
|
2367
|
+
* int(meta["root_n"])
|
|
2368
|
+
)
|
|
2369
|
+
)
|
|
2370
|
+
|
|
2371
|
+
payload = {
|
|
2372
|
+
"graph": graph,
|
|
2373
|
+
"meta": {
|
|
2374
|
+
k: v
|
|
2375
|
+
for k, v in meta.items()
|
|
2376
|
+
if v is not None
|
|
2377
|
+
},
|
|
2378
|
+
}
|
|
2379
|
+
|
|
2380
|
+
path = Path(path)
|
|
2381
|
+
|
|
2382
|
+
path.parent.mkdir(
|
|
2383
|
+
parents=True,
|
|
2384
|
+
exist_ok=True,
|
|
2385
|
+
)
|
|
2386
|
+
|
|
2387
|
+
path.write_text(
|
|
2388
|
+
json.dumps(
|
|
2389
|
+
_json_safe(payload),
|
|
2390
|
+
indent=indent,
|
|
2391
|
+
),
|
|
2392
|
+
encoding="utf-8",
|
|
2393
|
+
)
|
|
2394
|
+
|
|
2395
|
+
return path
|
|
2396
|
+
|
|
2397
|
+
def save_builder_html(
|
|
2398
|
+
self,
|
|
2399
|
+
path,
|
|
2400
|
+
**kwargs,
|
|
2401
|
+
):
|
|
2402
|
+
payload_path = (
|
|
2403
|
+
Path(path).with_suffix(
|
|
2404
|
+
".payload.json"
|
|
2405
|
+
)
|
|
2406
|
+
)
|
|
2407
|
+
|
|
2408
|
+
self.save_builder_payload(
|
|
2409
|
+
payload_path,
|
|
2410
|
+
**kwargs,
|
|
2411
|
+
)
|
|
2412
|
+
|
|
2413
|
+
payload = json.loads(
|
|
2414
|
+
payload_path.read_text(
|
|
2415
|
+
encoding="utf-8"
|
|
2416
|
+
)
|
|
2417
|
+
)
|
|
2418
|
+
|
|
2419
|
+
payload_path.unlink(
|
|
2420
|
+
missing_ok=True
|
|
2421
|
+
)
|
|
2422
|
+
|
|
2423
|
+
payload_json = json.dumps(payload)
|
|
2424
|
+
|
|
2425
|
+
# prevent accidental closing of the script tag
|
|
2426
|
+
payload_json = payload_json.replace(
|
|
2427
|
+
"</",
|
|
2428
|
+
"<\\/",
|
|
2429
|
+
)
|
|
2430
|
+
|
|
2431
|
+
html = (
|
|
2432
|
+
files(__package__)
|
|
2433
|
+
.joinpath(
|
|
2434
|
+
"builder_static/index.html"
|
|
2435
|
+
)
|
|
2436
|
+
.read_text(
|
|
2437
|
+
encoding="utf-8"
|
|
2438
|
+
)
|
|
2439
|
+
)
|
|
2440
|
+
|
|
2441
|
+
inject = f"""
|
|
2442
|
+
<script>
|
|
2443
|
+
window.ARBORENUM_BUILDER_PAYLOAD = {payload_json};
|
|
2444
|
+
</script>
|
|
2445
|
+
"""
|
|
2446
|
+
|
|
2447
|
+
if "</head>" in html:
|
|
2448
|
+
html = html.replace(
|
|
2449
|
+
"</head>",
|
|
2450
|
+
inject + "\n</head>",
|
|
2451
|
+
1,
|
|
2452
|
+
)
|
|
2453
|
+
else:
|
|
2454
|
+
html = inject + html
|
|
2455
|
+
|
|
2456
|
+
path = Path(path)
|
|
2457
|
+
|
|
2458
|
+
path.parent.mkdir(
|
|
2459
|
+
parents=True,
|
|
2460
|
+
exist_ok=True,
|
|
2461
|
+
)
|
|
2462
|
+
|
|
2463
|
+
path.write_text(
|
|
2464
|
+
html,
|
|
2465
|
+
encoding="utf-8",
|
|
2466
|
+
)
|
|
2467
|
+
|
|
2468
|
+
return path
|
|
2469
|
+
|
|
1979
2470
|
|
|
1980
2471
|
def plot_tree(self, tree_index: int, feature_names=None, figsize=(8, 6), ax=None, title=None, show=True):
|
|
1981
2472
|
paths, preds = self.get_tree_paths(tree_index)
|
|
@@ -2396,6 +2887,8 @@ class ArborEnum:
|
|
|
2396
2887
|
lookahead_k=1,
|
|
2397
2888
|
seed=0,
|
|
2398
2889
|
memory_efficient=False,
|
|
2890
|
+
key_mode="hash",
|
|
2891
|
+
trie_cache_enabled=True,
|
|
2399
2892
|
proxy_refinement="auto",
|
|
2400
2893
|
refinement_width=1,
|
|
2401
2894
|
max_refinement_rounds=-1,
|
|
@@ -2413,6 +2906,7 @@ class ArborEnum:
|
|
|
2413
2906
|
eta_defer=0.0,
|
|
2414
2907
|
bb_pred=None,
|
|
2415
2908
|
return_joint_samples=False,
|
|
2909
|
+
lossless=False,
|
|
2416
2910
|
):
|
|
2417
2911
|
|
|
2418
2912
|
if hasattr(X, "columns"):
|
|
@@ -2607,6 +3101,8 @@ class ArborEnum:
|
|
|
2607
3101
|
int(lookahead_k),
|
|
2608
3102
|
int(seed),
|
|
2609
3103
|
bool(memory_efficient),
|
|
3104
|
+
parse_key_mode(key_mode),
|
|
3105
|
+
bool(trie_cache_enabled),
|
|
2610
3106
|
bool(early_stopping),
|
|
2611
3107
|
int(refinement_width),
|
|
2612
3108
|
int(max_refinement_rounds),
|
|
@@ -2629,6 +3125,7 @@ class ArborEnum:
|
|
|
2629
3125
|
float(eta_defer),
|
|
2630
3126
|
bb_pred_vec,
|
|
2631
3127
|
bool(return_joint_samples),
|
|
3128
|
+
bool(lossless),
|
|
2632
3129
|
)
|
|
2633
3130
|
|
|
2634
3131
|
if (
|
|
@@ -2658,11 +3155,14 @@ class ArborEnum:
|
|
|
2658
3155
|
lookahead_k=1,
|
|
2659
3156
|
seed=0,
|
|
2660
3157
|
memory_efficient=False,
|
|
3158
|
+
key_mode="hash",
|
|
3159
|
+
trie_cache_enabled=True,
|
|
2661
3160
|
binning_map=None,
|
|
2662
3161
|
use_deferral=False,
|
|
2663
3162
|
eta_defer=0.0,
|
|
2664
3163
|
bb_pred=None,
|
|
2665
3164
|
return_joint_samples=False,
|
|
3165
|
+
lossless=False,
|
|
2666
3166
|
):
|
|
2667
3167
|
X = np.asarray(X, dtype=np.uint8)
|
|
2668
3168
|
y = np.asarray(y, dtype=int)
|
|
@@ -2721,11 +3221,14 @@ class ArborEnum:
|
|
|
2721
3221
|
int(lookahead_k),
|
|
2722
3222
|
int(seed),
|
|
2723
3223
|
bool(memory_efficient),
|
|
3224
|
+
parse_key_mode(key_mode),
|
|
3225
|
+
bool(trie_cache_enabled),
|
|
2724
3226
|
binning_map,
|
|
2725
3227
|
bool(use_deferral),
|
|
2726
3228
|
float(eta_defer),
|
|
2727
3229
|
bb_pred_vec,
|
|
2728
3230
|
bool(return_joint_samples),
|
|
3231
|
+
bool(lossless),
|
|
2729
3232
|
)
|
|
2730
3233
|
|
|
2731
3234
|
if (
|
|
@@ -2760,6 +3263,8 @@ class ArborEnum:
|
|
|
2760
3263
|
lookahead_k=1,
|
|
2761
3264
|
seed=0,
|
|
2762
3265
|
memory_efficient=False,
|
|
3266
|
+
key_mode="hash",
|
|
3267
|
+
trie_cache_enabled=True,
|
|
2763
3268
|
use_anytime_fit=False,
|
|
2764
3269
|
refinement_width=1,
|
|
2765
3270
|
max_refinement_rounds=-1,
|
|
@@ -2782,6 +3287,7 @@ class ArborEnum:
|
|
|
2782
3287
|
eta_defer=0.0,
|
|
2783
3288
|
bb_pred=None,
|
|
2784
3289
|
return_joint_samples=False,
|
|
3290
|
+
lossless=False,
|
|
2785
3291
|
):
|
|
2786
3292
|
X_num = np.asarray(X_num, dtype=np.float64)
|
|
2787
3293
|
y = np.asarray(y, dtype=int)
|
|
@@ -2960,6 +3466,8 @@ class ArborEnum:
|
|
|
2960
3466
|
int(lookahead_k),
|
|
2961
3467
|
int(seed),
|
|
2962
3468
|
bool(memory_efficient),
|
|
3469
|
+
parse_key_mode(key_mode),
|
|
3470
|
+
bool(trie_cache_enabled),
|
|
2963
3471
|
bool(use_anytime_fit),
|
|
2964
3472
|
int(refinement_width),
|
|
2965
3473
|
int(max_refinement_rounds),
|
|
@@ -2984,6 +3492,7 @@ class ArborEnum:
|
|
|
2984
3492
|
float(eta_defer),
|
|
2985
3493
|
bb_pred_vec,
|
|
2986
3494
|
bool(return_joint_samples),
|
|
3495
|
+
bool(lossless),
|
|
2987
3496
|
)
|
|
2988
3497
|
|
|
2989
3498
|
if (
|