Skip to content

11/ Classifier fixes: naive Bayes crossovers, Fisher score, kNN exact matches, previousSelection, ensemble selection, tuning warning - #135

Merged
DarioS merged 1 commit into
review/9-coxnet-pathfrom
review/11-classifier-fixes
Oct 28, 2026
Merged

DarioS merged 1 commit into
review/9-coxnet-pathfrom
review/11-classifier-fixes

Conversation

@ellispatrick

Copy link
Copy Markdown
Member

Based on #134; independent of the other post-#134 PRs. It can be accepted or rejected on its own.

Summary

Changes results only for naive Bayes with weighting = "crossover distance" (4.7% of predictions on the asthma data). Every other change either fixes something that errored or leaves predictions unchanged: Fisher discriminant scores move but its classes don't, and weighted kNN changes only when a test sample is identical to a training sample. All 18 regression scenarios are identical to #134.

Why

Ellis Patrick decided these on 2026-10-10 (ProjectClassifyR DECISIONS.md, questions Q13, Q22–Q24, Q26):

  • Q22, naive Bayes: vertical distances used class densities scaled by class size, but crossover points used the unscaled densities, so the two disagreed about where the classes meet whenever the classes differ in size.
  • Q23, Fisher discriminant: the score was -aᵀx, not centred on the critical value. Its sign didn't match the predicted class, and scores from models trained on different folds sat on different offsets.
  • Q23, kNN: a neighbour at distance zero has infinite weight, and that was replaced by an arbitrary 999999.
  • Q24, previousSelection: it matched previous features after make.names(). Different names with the same safe name ("g-1" and "g.1") matched the wrong column; such data actually stopped with "duplicate row.names". It also needed the user to know to set intermediate = ".iteration".
  • Q26, ensemble selection: the list-of-rankings branch of .doSelection used undefined variables (featuresLists, measurementsSubset, aResult), so it always failed.
  • Q13, resubstitution tuning: when several nFeatures values fit the training set perfectly, the first is chosen silently. This is documented; the warning makes it visible.

What changes

  • naiveBayesKernel: densities are scaled by class size once, and both the vertical distances and the crossover points use them. With weighting = "height difference" (the default) predictions are unchanged.
  • fisherDiscriminant: score = critical value − aᵀx, positive for the second class. Predicted classes are unchanged.
  • kNNinterface(mode = "weighted"): if any of the k neighbours is at distance zero, those neighbours alone vote, equally. Otherwise the weights are 1/distance as before.
  • previousSelection matches on the original feature names (mcols(measurements)$feature, kept by prepareData), or on assay + feature for multi-assay data. SelectParams("previousSelection", ...) adds the .iteration intermediate itself.
  • prepareData, runTest and predict make safe feature names unique (make.names(unique = TRUE)), so colliding names no longer stop the run. Names that were already unique are unchanged.
  • Ensemble selection works: each ranking function ranks the features, and the selection is the features in the top n of at least minPresence rankings. With several nFeatures values, n is tuned by resubstitution or nested CV like the single-ranking path, and the tuning table is stored. otherParams can be one list per ranking function or shared by all of them.
  • runTests warns once per cross-validation, giving the number of training sets in which resubstitution tuning tied at a perfect fit, and suggests tuneMode = "Nested CV".

Evidence

Classifier Predictions changed Balanced accuracy AUC
naive Bayes, crossover distance, weighted 4.7% 0.746 → 0.725 0.822 → 0.822
kNN, weighted, k = 5 0% (no exact duplicates) 0.761 → 0.761 0.868 → 0.868
Fisher discriminant 0% 0.777 → 0.777 pooled-score AUC 0.860 → 0.867

The naive Bayes change lowers balanced accuracy here. Scaling by class size moves the crossover towards the smaller class, so more samples near the boundary vote for the larger one. That is the consistent behaviour, but it is not an improvement on this data. Scripts: ProjectClassifyR scripts/classifier_fixes/.

R CMD check was not rerun for this PR; no documentation files changed.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TfsjwkbDbHkX4kJZNe4ER4

…ches, previousSelection, ensemble selection, resubstitution warning

- naiveBayesKernel: crossover points use the class densities scaled by class size, the same densities as the
  vertical distances (changes results with weighting = "crossover distance").
- fisherDiscriminant: the score is centred on the critical value, so its sign gives the predicted class and scores
  from different fold models are comparable (classes unchanged).
- kNNinterface (weighted): training samples at distance zero alone vote, equally, instead of a weight of 999999.
- previousSelection matches the original feature names; SelectParams passes .iteration to it automatically.
  prepareData makes safe feature names unique, so "a-b" and "a.b" no longer collide.
- .doSelection: ensemble selection (a list of ranking functions) works, with tuning of the number of features.
- runTests warns once when resubstitution tuning finds several numbers of features with a perfect training fit.

Decided by Ellis Patrick (ProjectClassifyR DECISIONS.md, 2026-10-10, Q13, Q22-Q24, Q26).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TfsjwkbDbHkX4kJZNe4ER4
@ellispatrick
ellispatrick force-pushed the review/11-classifier-fixes branch from 8244564 to 0b31f41 Compare October 14, 2026 00:20
@DarioS
DarioS added this pull request to stack #148 October 28, 2026 02:13
@DarioS
DarioS marked this pull request as ready for review October 28, 2026 02:36

@DarioS DarioS left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

A couple of rare edge cases handled.

@DarioS
DarioS merged commit cd815ec into review/5-maintenance Oct 28, 2026
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.

2 participants