arvore 0.0.5__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.
Files changed (63) hide show
  1. arvore-0.0.5/LICENSE +21 -0
  2. arvore-0.0.5/PKG-INFO +650 -0
  3. arvore-0.0.5/README.md +579 -0
  4. arvore-0.0.5/arvore/__init__.py +11 -0
  5. arvore-0.0.5/arvore/box/__init__.py +1 -0
  6. arvore-0.0.5/arvore/box/base.py +877 -0
  7. arvore-0.0.5/arvore/cli/__init__.py +0 -0
  8. arvore-0.0.5/arvore/cli/cli.py +207 -0
  9. arvore-0.0.5/arvore/cli/predict.py +79 -0
  10. arvore-0.0.5/arvore/cli/scoring.py +193 -0
  11. arvore-0.0.5/arvore/cli/train.py +68 -0
  12. arvore-0.0.5/arvore/config/__init__.py +2 -0
  13. arvore-0.0.5/arvore/config/configs.py +401 -0
  14. arvore-0.0.5/arvore/config/instantiate.py +226 -0
  15. arvore-0.0.5/arvore/metrics/__init__.py +1 -0
  16. arvore-0.0.5/arvore/metrics/base.py +136 -0
  17. arvore-0.0.5/arvore/models/__init__.py +1 -0
  18. arvore-0.0.5/arvore/models/adapters.py +694 -0
  19. arvore-0.0.5/arvore/models/ensemble.py +353 -0
  20. arvore-0.0.5/arvore/prediction.py +146 -0
  21. arvore-0.0.5/arvore/scorers/__init__.py +28 -0
  22. arvore-0.0.5/arvore/scorers/base.py +41 -0
  23. arvore-0.0.5/arvore/scorers/distances.py +142 -0
  24. arvore-0.0.5/arvore/scorers/ensemble.py +211 -0
  25. arvore-0.0.5/arvore/scorers/leaves.py +377 -0
  26. arvore-0.0.5/arvore/scorers/persistence.py +247 -0
  27. arvore-0.0.5/arvore/scorers/reference.py +53 -0
  28. arvore-0.0.5/arvore/scorers/references.py +87 -0
  29. arvore-0.0.5/arvore/scorers/registry.py +123 -0
  30. arvore-0.0.5/arvore/scorers/runner.py +269 -0
  31. arvore-0.0.5/arvore/scorers/tanimoto.py +504 -0
  32. arvore-0.0.5/arvore/typing.py +43 -0
  33. arvore-0.0.5/arvore/utils/__init__.py +0 -0
  34. arvore-0.0.5/arvore/utils/plotting.py +70 -0
  35. arvore-0.0.5/arvore.egg-info/PKG-INFO +650 -0
  36. arvore-0.0.5/arvore.egg-info/SOURCES.txt +61 -0
  37. arvore-0.0.5/arvore.egg-info/dependency_links.txt +1 -0
  38. arvore-0.0.5/arvore.egg-info/entry_points.txt +2 -0
  39. arvore-0.0.5/arvore.egg-info/requires.txt +36 -0
  40. arvore-0.0.5/arvore.egg-info/top_level.txt +1 -0
  41. arvore-0.0.5/pyproject.toml +81 -0
  42. arvore-0.0.5/setup.cfg +4 -0
  43. arvore-0.0.5/test/test_adapters.py +154 -0
  44. arvore-0.0.5/test/test_checkpoint_training_config.py +205 -0
  45. arvore-0.0.5/test/test_cli_scoring.py +137 -0
  46. arvore-0.0.5/test/test_config.py +177 -0
  47. arvore-0.0.5/test/test_ensemble.py +283 -0
  48. arvore-0.0.5/test/test_euclidean_distance.py +176 -0
  49. arvore-0.0.5/test/test_evaluation.py +39 -0
  50. arvore-0.0.5/test/test_fitting.py +149 -0
  51. arvore-0.0.5/test/test_leaf_indices.py +210 -0
  52. arvore-0.0.5/test/test_leaf_occupancy.py +260 -0
  53. arvore-0.0.5/test/test_reference_distances.py +570 -0
  54. arvore-0.0.5/test/test_reference_percentile.py +201 -0
  55. arvore-0.0.5/test/test_scorer_architecture.py +250 -0
  56. arvore-0.0.5/test/test_scorer_cache.py +551 -0
  57. arvore-0.0.5/test/test_scorer_registry.py +144 -0
  58. arvore-0.0.5/test/test_scorers.py +303 -0
  59. arvore-0.0.5/test/test_tanimoto_distance.py +268 -0
  60. arvore-0.0.5/test/test_tree_box.py +254 -0
  61. arvore-0.0.5/test/test_tree_capabilities.py +91 -0
  62. arvore-0.0.5/test/test_tree_scoring.py +358 -0
  63. arvore-0.0.5/test/test_typed_config.py +162 -0
arvore-0.0.5/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) [year] [fullname]
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.
arvore-0.0.5/PKG-INFO ADDED
@@ -0,0 +1,650 @@
1
+ Metadata-Version: 2.4
2
+ Name: arvore
3
+ Version: 0.0.5
4
+ Summary: Reproducible tree-based machine learning.
5
+ Author-email: Eachan Johnson <eachan.johnson@crick.ac.uk>
6
+ License: MIT License
7
+
8
+ Copyright (c) [year] [fullname]
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ Project-URL: Homepage, https://__PRESERVE_ARVORE_REPO_URL__
28
+ Project-URL: Bug Tracker, https://__PRESERVE_ARVORE_REPO_URL__/issues
29
+ Keywords: ai,active-learning,bayesian-optimization,data,deep-learning,machine-learning
30
+ Classifier: Development Status :: 3 - Alpha
31
+ Classifier: Intended Audience :: Science/Research
32
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
33
+ Classifier: License :: OSI Approved :: MIT License
34
+ Classifier: Programming Language :: Python :: 3.11
35
+ Classifier: Programming Language :: Python :: 3.12
36
+ Classifier: Programming Language :: Python :: 3.13
37
+ Classifier: Programming Language :: Python :: 3.14
38
+ Classifier: Programming Language :: Python :: 3.15
39
+ Classifier: Programming Language :: Python :: 3 :: Only
40
+ Requires-Python: >=3.11
41
+ Description-Content-Type: text/markdown
42
+ License-File: LICENSE
43
+ Requires-Dist: aspect-data>=0.0.7
44
+ Requires-Dist: carabiner-tools>=0.0.5.post3
45
+ Requires-Dist: datasets>=3.5.0
46
+ Requires-Dist: joblib>=1.3
47
+ Requires-Dist: numpy>=2.0
48
+ Requires-Dist: scikit-learn>=1.7
49
+ Requires-Dist: skops>=0.12
50
+ Provides-Extra: chem
51
+ Requires-Dist: schemist>=0.0.7; extra == "chem"
52
+ Provides-Extra: bio
53
+ Requires-Dist: vectome>=0.0.7; extra == "bio"
54
+ Provides-Extra: cat
55
+ Requires-Dist: catboost>=1.2; extra == "cat"
56
+ Provides-Extra: lgbm
57
+ Requires-Dist: lightgbm[arrow,plotting,scikit-learn]>=4.0; extra == "lgbm"
58
+ Provides-Extra: xgb
59
+ Requires-Dist: xgboost_cpu; extra == "xgb"
60
+ Provides-Extra: xgb-cuda12
61
+ Requires-Dist: xgboost-cu12; extra == "xgb-cuda12"
62
+ Provides-Extra: xgb-cuda13
63
+ Requires-Dist: xgboost>=3.0; extra == "xgb-cuda13"
64
+ Provides-Extra: trees
65
+ Requires-Dist: arvore[lgbm,xgb]; extra == "trees"
66
+ Provides-Extra: dev
67
+ Requires-Dist: arvore[bio,chem,trees]; extra == "dev"
68
+ Requires-Dist: pytest>=7.4; extra == "dev"
69
+ Requires-Dist: flake8; extra == "dev"
70
+ Dynamic: license-file
71
+
72
+ # 🌳 Arvore
73
+
74
+ ![GitHub Actions](https://img.shields.io/github/actions/workflow/status/scbirlab/arvore/python-test.yml)
75
+ ![Python versions](https://img.shields.io/pypi/pyversions/arvore)
76
+ ![PyPI](https://img.shields.io/pypi/v/arvore)
77
+
78
+ **Arvore makes tree-based machine learning simple, reproducible and shareable.**
79
+
80
+ Train random forests, XGBoost, LightGBM, CatBoost and other scikit-learn-compatible estimators through a consistent interface. Combine models into ensembles, evaluate predictions, measure uncertainty and novelty, and share complete experiments as portable checkpoints.
81
+
82
+ Arvore brings together model training, feature preprocessing, data provenance and prediction diagnostics without imposing a particular modelling workflow. Its composable scoring interface makes it straightforward to compare uncertainty measures or integrate methods from other packages.
83
+
84
+ Use it interactively from Python or through a reproducible command-line interface, from a laptop to an HPC cluster.
85
+
86
+ Arvore currently focuses on regression and is under active alpha development.
87
+
88
+ ## Installation
89
+
90
+ Python 3.11 or later is required.
91
+
92
+ ```bash
93
+ pip install arvore
94
+ ```
95
+
96
+ Install optional model backends as needed:
97
+
98
+ ```bash
99
+ pip install "arvore[trees]" # XGBoost and LightGBM
100
+ pip install "arvore[cat]" # CatBoost
101
+ pip install "arvore[chem]" # Molecular fingerprints and descriptors
102
+ pip install "arvore[bio]" # Biological feature tools
103
+ ```
104
+
105
+ For the latest development version:
106
+
107
+ ```bash
108
+ pip install "git+https://github.com/scbirlab/treeton.git@dev"
109
+ ```
110
+
111
+ ## Quick start
112
+
113
+ ### Command-line interface
114
+
115
+ Train and save a model using a simple JSON configuration.
116
+
117
+ Create `experiment.json`:
118
+
119
+ ```json
120
+ {
121
+ "box": {
122
+ "model": {
123
+ "class_path": "sklearn.ensemble.RandomForestRegressor",
124
+ "init_args": {
125
+ "n_estimators": 200,
126
+ "random_state": 42
127
+ }
128
+ },
129
+ "features": ["x1", "x2"],
130
+ "target": "y",
131
+ "pipeline": {}
132
+ },
133
+ "fit": {
134
+ "skip_eval": true
135
+ }
136
+ }
137
+ ```
138
+
139
+ Train a model from a CSV file:
140
+
141
+ ```bash
142
+ arvore train train.csv \
143
+ --config experiment.json \
144
+ --checkpoint model.ao
145
+ ```
146
+
147
+ Make predictions on new data:
148
+
149
+ ```bash
150
+ arvore predict model.ao \
151
+ --trust-sklearn-trees \
152
+ --data candidates.csv \
153
+ --output predictions.csv
154
+ ```
155
+
156
+ Add uncertainty and novelty diagnostics with one argument:
157
+
158
+ ```bash
159
+ arvore predict model.ao \
160
+ --trust-sklearn-trees \
161
+ --data candidates.csv \
162
+ --score tree_sd feature_distance \
163
+ --output scored.csv
164
+ ```
165
+
166
+ The resulting table retains the input columns and adds predictions and requested scores:
167
+
168
+ | x1 | x2 | prediction:y | score:tree_sd:y | score:feature_distance |
169
+ | --- | --- | --- | --- | --- |
170
+ | ... | ... | ... | ... | ... |
171
+
172
+ The same configuration can be reused across datasets and overridden from the command line.
173
+
174
+ ### Python API
175
+
176
+ The equivalent workflow takes only a few lines:
177
+
178
+ ```python
179
+ from arvore import Box
180
+
181
+ box = Box.from_config({
182
+ "model": {
183
+ "class_path": "sklearn.ensemble.RandomForestRegressor",
184
+ "init_args": {
185
+ "n_estimators": 200,
186
+ "random_state": 42
187
+ }
188
+ },
189
+ "features": ["x1", "x2"],
190
+ "target": "y",
191
+ "pipeline": {}
192
+ })
193
+
194
+ box.fit("train.csv", skip_eval=True)
195
+
196
+ results = box.predict(
197
+ "candidates.csv",
198
+ score=["tree_sd", "feature_distance"]
199
+ )
200
+
201
+ box.save("model.ao")
202
+ ```
203
+
204
+ Restore the complete fitted experiment elsewhere:
205
+
206
+ ```python
207
+ from arvore import Box
208
+
209
+ box = Box.load(
210
+ "model.ao",
211
+ trusted_types=("sklearn.tree._tree.Tree",)
212
+ )
213
+
214
+ predictions = box.predict("new_data.csv")
215
+ ```
216
+
217
+ The checkpoint preserves the trained estimator, its configuration, fitted preprocessing and training-data provenance. Prepared reference resources for uncertainty and novelty scoring can also be checkpointed and reused.
218
+
219
+ ## Models and ensembles
220
+
221
+ Arvore supports scikit-learn-compatible estimators through a common training, prediction and persistence interface.
222
+
223
+ Supported backends include:
224
+
225
+ - Random forests and other scikit-learn estimators
226
+ - XGBoost
227
+ - LightGBM
228
+ - CatBoost
229
+ - Homogeneous and heterogeneous ensembles
230
+
231
+ Additional estimators can be integrated through model adapters.
232
+
233
+ ### Ensembles
234
+
235
+ Combine estimators directly through the Python API:
236
+
237
+ ```python
238
+ from arvore import Ensemble
239
+
240
+ from sklearn.ensemble import RandomForestRegressor
241
+ from xgboost import XGBRegressor
242
+
243
+ model = Ensemble(
244
+ {
245
+ "rf": RandomForestRegressor(
246
+ n_estimators=200,
247
+ random_state=42
248
+ ),
249
+ "xgb": XGBRegressor(
250
+ n_estimators=300,
251
+ random_state=43
252
+ )
253
+ },
254
+ weights=[1, 2]
255
+ )
256
+
257
+ model.fit(X_train, y_train)
258
+
259
+ predictions = model.predict(X_test)
260
+ members = model.predict_members(X_test)
261
+ ```
262
+
263
+ Arvore also supports ensembles of repeated estimators defined through JSON configurations.
264
+
265
+ Individual member predictions can be inspected alongside aggregate predictions, including through the CLI's `--disaggregate` option.
266
+
267
+ ## Prediction scoring
268
+
269
+ Arvore supports composable prediction diagnostics through the `score` argument of `Box.predict()`.
270
+
271
+ Scores can quantify model disagreement, variability between trees, or distance from observations in the training dataset.
272
+
273
+ | Scorer | Description |
274
+ | --- | --- |
275
+ | `ensemble_sd` | Standard deviation of predictions across ensemble members |
276
+ | `tree_sd` | Variation in predictions across individual trees |
277
+ | `inverse_leaf_occupancy` | Sparsity of training observations in the leaves reached by a candidate |
278
+ | `feature_distance` | Distance to nearest training observations in transformed feature space |
279
+ | `tanimoto_distance` | Distance to training compounds using molecular fingerprints |
280
+
281
+ Scores are diagnostics with different scales and interpretations. They should not automatically be interpreted as calibrated prediction errors.
282
+
283
+ ### Composing scorers
284
+
285
+ Request several scores in the same prediction:
286
+
287
+ ```python
288
+ results = box.predict(
289
+ "candidates.csv",
290
+ score=[
291
+ "tree_sd",
292
+ "feature_distance",
293
+ "inverse_leaf_occupancy"
294
+ ]
295
+ )
296
+ ```
297
+
298
+ Scorers can also be configured as objects:
299
+
300
+ ```python
301
+ from arvore.scorers import (
302
+ FeatureDistance,
303
+ InverseLeafOccupancy
304
+ )
305
+
306
+ results = box.predict(
307
+ "candidates.csv",
308
+ score={
309
+ "nearest": FeatureDistance(n_neighbors=1),
310
+ "neighbourhood": FeatureDistance(n_neighbors=10),
311
+ "leaf_percentile": InverseLeafOccupancy(
312
+ normalization="reference_percentile"
313
+ )
314
+ }
315
+ )
316
+ ```
317
+
318
+ Using a dictionary assigns each scorer a custom output name, allowing different configurations of the same method to be evaluated together.
319
+
320
+ ### Molecular novelty
321
+
322
+ Chemical novelty can be evaluated independently of the features used by a predictive model.
323
+
324
+ ```python
325
+ from arvore.scorers import TanimotoDistance
326
+
327
+ results = box.predict(
328
+ "compounds.csv",
329
+ score=TanimotoDistance(
330
+ structure_column="smiles",
331
+ column="fingerprint"
332
+ )
333
+ )
334
+ ```
335
+
336
+ Arvore can reuse existing molecular fingerprints or generate them from molecular structures through its preprocessing integrations.
337
+
338
+ ### External scoring methods
339
+
340
+ **Any callable accepting an Arvore `ScoreContext` can be used as a scorer.** This allows existing uncertainty estimators, distance metrics or custom diagnostics to be integrated without modifying Arvore.
341
+
342
+ For example, to use a function from another package:
343
+
344
+ ```python
345
+ import numpy as np
346
+
347
+ from external_package import novelty_score
348
+
349
+ def external_novelty(context):
350
+ return np.asarray(
351
+ novelty_score(context.X)
352
+ )
353
+
354
+ results = box.predict(
355
+ "candidates.csv",
356
+ score={"novelty": external_novelty}
357
+ )
358
+ ```
359
+
360
+ The callable receives the transformed feature matrix and can access the fitted model, predictions, candidate data and other available context.
361
+
362
+ For methods requiring training-set references, calibration data or additional model capabilities, Arvore provides a more structured scorer interface described under [Extending Arvore](#extending-arvore).
363
+
364
+ ### Reference-based scoring
365
+
366
+ Some diagnostics require information from the fitted training dataset, such as nearest-neighbour indices, molecular fingerprints or tree-leaf occupancy distributions.
367
+
368
+ Arvore prepares these resources lazily and caches them on the fitted `Box`. Repeated scoring requests reuse compatible references rather than recomputing them.
369
+
370
+ ```python
371
+ box.predict(
372
+ candidates,
373
+ score="feature_distance"
374
+ )
375
+
376
+ # Reuses the same prepared training reference.
377
+ box.predict(
378
+ more_candidates,
379
+ score="feature_distance"
380
+ )
381
+ ```
382
+
383
+ Prepared references can be retained in checkpoints:
384
+
385
+ ```python
386
+ box.save(
387
+ "model.ao",
388
+ score=[
389
+ "feature_distance",
390
+ "inverse_leaf_occupancy",
391
+ "tanimoto_distance"
392
+ ]
393
+ )
394
+ ```
395
+
396
+ References are identified by deterministic specifications, allowing compatible scorers and repeated predictions to share resources.
397
+
398
+ ## Data and preprocessing
399
+
400
+ Arvore works with datasets resolved through [Providence](https://github.com/scbirlab/providence), including CSV, Parquet and Hugging Face datasets.
401
+
402
+ Feature transformations are managed through [Aspect](https://github.com/scbirlab/aspect). Preprocessing is fitted alongside the estimator and retained in its checkpoint.
403
+
404
+ A `Box` specifies the input features, prediction targets, model and optional transformation pipeline.
405
+
406
+ For example, a pipeline can construct a log-transformed feature:
407
+
408
+ ```json
409
+ {
410
+ "column_transforms": {
411
+ "log_x": ["x", "log"]
412
+ }
413
+ }
414
+ ```
415
+
416
+ The resulting feature can then be selected for model training.
417
+
418
+ Arvore also supports batched predictions, making it suitable for evaluating large candidate datasets without materialising every prediction batch simultaneously.
419
+
420
+ ## Evaluation and reproducibility
421
+
422
+ Evaluate models against held-out data using standard regression metrics:
423
+
424
+ ```python
425
+ predictions, metrics = box.evaluate(
426
+ "validation.csv",
427
+ metrics=[
428
+ "rmse",
429
+ "mae",
430
+ "pearson",
431
+ "spearman"
432
+ ]
433
+ )
434
+ ```
435
+
436
+ ### Portable checkpoints
437
+
438
+ A saved `Box` retains the information needed to recover a fitted experiment:
439
+
440
+ - Model configuration and fitted estimator
441
+ - Feature definitions and fitted preprocessing
442
+ - Training-data provenance and retained training references
443
+ - Prepared uncertainty and novelty resources, when requested
444
+
445
+ Training data may be embedded or retained by reference, depending on its source and the checkpoint settings.
446
+
447
+ Model persistence uses backend-native formats where supported and skops for scikit-learn models. Some scikit-learn checkpoints require explicitly trusting internal estimator types during loading. An unsafe joblib fallback is available for otherwise unsupported estimators.
448
+
449
+ Only load checkpoints from trusted sources.
450
+
451
+ ### Reproducible command-line experiments
452
+
453
+ Configurations can be overridden without editing the original experiment definition:
454
+
455
+ ```bash
456
+ arvore train train.csv \
457
+ --config experiment.json \
458
+ --checkpoint experiment.ao \
459
+ --set box.model.init_args.n_estimators=500
460
+ ```
461
+
462
+ This makes the same experiment definition reusable across model hyperparameters, datasets and automated workflows.
463
+
464
+ ## Extending Arvore
465
+
466
+ Arvore is designed to accommodate external estimators and prediction diagnostics with minimal integration code.
467
+
468
+ ### Box
469
+
470
+ `Box` is the central interface for a fitted experiment.
471
+
472
+ It brings together:
473
+
474
+ | Component | Responsibility |
475
+ | --- | --- |
476
+ | Model | A fitted estimator or ensemble |
477
+ | Features and targets | Definitions of model inputs and outputs |
478
+ | Pipeline | Feature transformations and preprocessing |
479
+ | Data provenance | Resolution and retention of training data |
480
+ | Scorers | Optional uncertainty and novelty diagnostics |
481
+ | References | Reusable training-dependent resources |
482
+ | Checkpoint | Persistence of the complete fitted experiment |
483
+
484
+ The main operations are `fit()`, `predict()`, `evaluate()`, `save()` and `load()`.
485
+
486
+ Model-specific operations are delegated to adapters, keeping `Box` independent of a particular estimation library.
487
+
488
+ ### Writing a custom Scorer
489
+
490
+ A minimal scorer only needs to be callable:
491
+
492
+ ```python
493
+ import numpy as np
494
+
495
+ def feature_norm(context):
496
+ return np.linalg.norm(context.X, axis=1)
497
+
498
+ results = box.predict(
499
+ candidates,
500
+ score={"feature_norm": feature_norm}
501
+ )
502
+ ```
503
+
504
+ The `ScoreContext` provides backend-independent access to information such as:
505
+
506
+ - `X`: transformed candidate features
507
+ - `predictions`: model predictions
508
+ - `model`: fitted model or ensemble
509
+ - `batch`: original candidate columns
510
+ - `targets`: prediction target names
511
+ - `member_predictions`: optional ensemble-member predictions
512
+ - `tree_predictions`: optional individual-tree predictions
513
+ - `state`: scorer-specific prepared resources
514
+ - `box`: the fitted `Box`
515
+
516
+ More advanced methods can declare requirements and implement preparation hooks.
517
+
518
+ For example, a scorer wrapping an external uncertainty implementation:
519
+
520
+ ```python
521
+ from dataclasses import dataclass
522
+ from typing import ClassVar
523
+
524
+ from arvore.scorers import (
525
+ Requirements,
526
+ ScoreContext,
527
+ register_scorer
528
+ )
529
+
530
+ from external_package import ExternalUncertainty
531
+
532
+
533
+ @register_scorer
534
+ @dataclass(frozen=True)
535
+ class ExternalScore:
536
+ name: ClassVar[str] = "external_score"
537
+
538
+ @property
539
+ def requirements(self):
540
+ return Requirements(
541
+ outputs=("prediction",)
542
+ )
543
+
544
+ def prepare(self, box, *, batch_size=1024):
545
+ # Construct or retrieve any resources needed
546
+ # by the external implementation.
547
+ return ExternalUncertainty(box.model)
548
+
549
+ def __call__(self, context: ScoreContext):
550
+ return context.state.score(context.X)
551
+ ```
552
+
553
+ The scorer can then be invoked by name:
554
+
555
+ ```python
556
+ results = box.predict(
557
+ candidates,
558
+ score="external_score"
559
+ )
560
+ ```
561
+
562
+ The external implementation above is illustrative; its constructor and `score()` method should be adapted to the actual library.
563
+
564
+ ### Scorer extension points
565
+
566
+ Scorers can implement several optional hooks:
567
+
568
+ | Hook | Purpose |
569
+ | --- | --- |
570
+ | `requirements` | Declare required candidate columns, model capabilities and prediction outputs |
571
+ | `prepare_candidates(data, box=...)` | Generate or resolve auxiliary candidate columns |
572
+ | `validate(box, prepared)` | Check compatibility before scoring |
573
+ | `prepare(box, batch_size=...)` | Initialise training references, indices or calibration resources |
574
+ | `__call__(context)` | Evaluate the scorer for a prediction batch |
575
+ | `finalize(result, name=..., box=...)` | Inspect the completed result or issue diagnostics |
576
+
577
+ Arvore validates declared requirements and supplies common inference resources, including ensemble-member predictions and individual-tree predictions, through model adapters where supported.
578
+
579
+ A scorer can return one value per sample or per target. It may also return a mapping containing a primary score and additional named outputs.
580
+
581
+ ### Reusing reference resources
582
+
583
+ Scorers that depend on training data can request reusable resources through `box.references.get_or_build()`.
584
+
585
+ ```python
586
+ reference = box.references.get_or_build(
587
+ {
588
+ "type": "external_reference",
589
+ "version": 1,
590
+ "metric": "euclidean"
591
+ },
592
+ builder
593
+ )
594
+ ```
595
+
596
+ Here, `builder` is a callable that constructs the resource when it has not already been prepared.
597
+
598
+ Arvore hashes the canonical resource specification to obtain a deterministic cache key. Equivalent requests reuse the same prepared resource, including across scorer instances.
599
+
600
+ Reference types supporting Arvore's checkpoint persistence can be saved and restored alongside the fitted model.
601
+
602
+ This enables integration with external nearest-neighbour libraries, model-specific uncertainty implementations, density estimators or calibration methods without embedding their algorithms in `Box`.
603
+
604
+ ### Model adapters
605
+
606
+ Custom estimators can be supported through Arvore's adapter registry.
607
+
608
+ Adapters handle estimator-specific fitting, prediction, model capabilities and persistence. This allows scorers to request capabilities such as individual-tree predictions without depending directly on XGBoost, LightGBM, CatBoost or scikit-learn internals.
609
+
610
+ ## Development
611
+
612
+ Clone the repository and install development dependencies:
613
+
614
+ ```bash
615
+ git clone https://github.com/scbirlab/treeton.git
616
+ cd treeton
617
+ pip install -e ".[dev]"
618
+ ```
619
+
620
+ Run the test suite:
621
+
622
+ ```bash
623
+ pytest -q
624
+ ```
625
+
626
+ Run module doctests:
627
+
628
+ ```bash
629
+ pytest --doctest-modules -q arvore
630
+ ```
631
+
632
+ The project is developed using Python 3.11 and later.
633
+
634
+ ### Related projects
635
+
636
+ Arvore integrates with other packages in the [scbirlab](https://github.com/scbirlab) ecosystem:
637
+
638
+ - [Providence](https://github.com/scbirlab/providence) — Data resolution, provenance and checkpointing.
639
+ - [Aspect](https://github.com/scbirlab/aspect) — Feature transformation and processing.
640
+ - [Eluent](https://github.com/scbirlab/eluent) — Dataset grouping and splitting.
641
+ - [Schemist](https://github.com/scbirlab/schemist) — Molecular featurisation.
642
+ - [Vectome](https://github.com/scbirlab/vectome) — Biological feature representations.
643
+
644
+ ### Issues, problems, suggestions
645
+
646
+ Please report bugs, feature requests and suggestions through the [GitHub issue tracker](https://github.com/scbirlab/arvore/issues).
647
+
648
+ ### License
649
+
650
+ MIT. See [LICENSE](https://github.com/scbirlab/arvore/blob/main/LICENSE).