Skip to content

Extra trees classifier - #523

Merged
Mec-iS merged 9 commits into
smartcorelib:mainfrom
slievens:extra-trees-classifier
Nov 7, 2026
Merged

Mec-iS merged 9 commits into
smartcorelib:mainfrom
slievens:extra-trees-classifier

Conversation

@slievens

@slievens slievens commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Title: feat(ensemble): add ExtraTreesClassifier

Fixes #522

Checklist

  • My branch is up-to-date with main branch.
  • Everything works and tested on latest stable Rust.
  • Coverage and Linting have been applied

Current behaviour

smartcore has ExtraTreesRegressor but no Extra Trees classifier.

New expected behaviour

ensemble::extra_trees_classifier::ExtraTreesClassifier is a new classifier. It uses BaseForest and BaseTree, the same code as the regressor and RandomForestClassifier.

  • Each tree uses random split thresholds. By default, each tree uses the whole dataset, as in scikit-learn. Set bootstrap to true to use bootstrap samples.
  • Split criteria: Gini, entropy and classification error.
  • Methods: fit, fit_with_weights, predict, predict_oob and compute_feature_importances.
  • predict_oob returns an error when the forest has no bootstrap samples.

- RandomForestRegressor, RandomForestClassifier and ExtraTreesRegressor:
  seed is now Option<u64> (search parameters: Vec<Option<u64>>).
  Default is None: the seed is chosen at fit time, as in the decision
  trees.
- Each forest uses one RNG for bootstrap sampling and for the tree
  seeds, instead of seed + tree_idx.
- Add with_seed to DecisionTreeRegressorParameters and
  DecisionTreeClassifierParameters.

BREAKING CHANGE: the seed field type changes, and results change for
the default and for a given seed.
- base_forest: forests with seeds 42 and 43 share no trees; seed None is
  deterministic without std_rand (equal to Some(0)) and differs between
  fits with std_rand.
- serde round trip of the seed parameter for RandomForestClassifier and
  ExtraTreesRegressor.
- with_seed on the decision tree parameters sets Some(seed).
- Update the seed doc comments.
- Give tree and forest tests an explicit seed (Some(42) or with_seed(..))
  so that they stay deterministic when std_rand is on.
- Add search_parameters_with_seed test for RandomForestClassifier.
- Remove comments that only repeat what the code does.
Draw seeds for trees upfront.
@slievens
slievens requested a review from Mec-iS as a code owner November 5, 2026 20:15
@codecov

codecov Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.57143% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.89%. Comparing base (9eaae9e) to head (02557f8).
⚠️ Report is 221 commits behind head on main.

Files with missing lines Patch % Lines
src/ensemble/extra_trees_classifier.rs 88.57% 12 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             main     #523       +/-   ##
===========================================
+ Coverage   43.97%   86.89%   +42.91%     
===========================================
  Files          85       99       +14     
  Lines        7281     9819     +2538     
===========================================
+ Hits         3202     8532     +5330     
+ Misses       4079     1287     -2792     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Mec-iS

Mec-iS commented Nov 6, 2026

Copy link
Copy Markdown
Collaborator

Thanks @slievens, this is a clean addition. It reuses BaseForest/BaseTree with Splitter::Random, adds no unsafe, FFI or macros, and CI is green (WASM, i686, MSRV 1.85, serde, lint, audit). The tests cover a lot: all three criteria, seed reproducibility, trees differing with and without bootstrap, OOB error paths, labels outside 0..k, single class, weight validation, zero-weight rows, feature importances and serde round-trip.

Blocking

  1. Base branch. The PR targets main. Per the project convention it should target development. Please retarget and rebase.

Suggested changes

  1. PartialEq ignores bootstrap. eq compares only forest_classifier and classes. Two forests that differ only in bootstrap compare equal. Please include bootstrap in eq, or document why it is left out.
  2. Brittle test on a message from base_forest. predict_oob_without_keep_samples_should_fail asserts the exact string "Need samples=true for OOB predictions.", which is owned by base_forest. Assert on the error kind instead, or add the check to this file. That way a rewording upstream does not break this test.
  3. Duplicated code. The class-index mapping in fit_inner and the index-to-label mapping in predict/predict_oob are the same as in RandomForestClassifier. A small shared helper in base_forest would keep the two classifiers from drifting apart. This is fine as a follow-up issue if you want to keep the PR small.
  4. Missing test: weights with bootstrap. The docs say that with bootstrap = true rows are drawn proportionally to their weights. Only the non-bootstrap path is tested. A test with bootstrap and a heavily weighted row (or a zero-weight row) would pin down that claim.
  5. Missing test: invalid m. There is no test for m = 0 or m > n_features. Please check these return Failed or behave as documented, since BaseForest is shared.

Nits

  1. Dead struct field? ExtraTreesClassifier.bootstrap only exists to guard predict_oob. That is fine, but #[serde(default)] on it means old or hand-written payloads load as bootstrap = false. That gives a clear error rather than silent misbehaviour, so this is acceptable. A one-line doc note would help.
  2. External script in the module docs. The polyfill.io <script> line copied from the other modules points to a domain that was compromised in 2024. Worth a separate issue to remove it across the ensemble docs. It is not for this PR.
  3. Comment volume. The project asks to limit comments, and several test comments could be trimmed. Fixed 100% accuracy on the 20-row iris subset is also a weak signal for a randomized learner, so a held-out or OOB threshold on a larger dataset would be more convincing.
  4. Checklist. It says the branch is up to date with main. Please update it once the PR is retargeted.

Questions

  • Is the uniform draw between node min and max for each candidate feature implemented in Splitter::Random (already in BaseTree) and covered by tests? That code is not in this diff, so I could not verify it. A direct test that thresholds fall inside [min, max) would be valuable.
  • Have you compared accuracy against scikit-learn's ExtraTreesClassifier on a standard dataset (for example full iris or wine, same hyperparameters)? A rough parity check in the PR description would make review easier.

Once the base branch is fixed and 2 and 3 are addressed, I am happy to approve. 4–6 can be follow-up issues.

- partialeq adapated
- extra test for bootstrap=true and sample weights added
- make one test not depend on exact message
@slievens

slievens commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

Points (2), (3) and (5) fixed in latest commit.

@Mec-iS Mec-iS left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏼

@Mec-iS
Mec-iS merged commit bb7c794 into smartcorelib:main Nov 7, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add ExtraTreesClassifier

2 participants