Skip to content

add new outlier method - #507

Merged
ivokwee merged 7 commits into
edgyfrom
feat-outlier-isoforest
Aug 10, 2026
Merged

add new outlier method#507
ivokwee merged 7 commits into
edgyfrom
feat-outlier-isoforest

Conversation

@ivokwee

@ivokwee ivokwee commented Aug 1, 2026

Copy link
Copy Markdown
Member

Add extra 'isolation forest' outlier detection method. Seems to be popular.

PS: Need isotree dependency.

@ivokwee
ivokwee requested a review from ESCRI11 August 1, 2026 18:18

@ESCRI11 ESCRI11 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

  1. R/pgx-outlier.R:10 — The default value of col is col. Thus R cannot find a value, and every call with plot = TRUE fails. Change the default to col = "grey70".

  2. R/pgx-outlier.R:46 — The subset of Z has no drop = FALSE. Thus one method makes Z a vector, and rowMeans() fails. Add drop = FALSE.

@ESCRI11

ESCRI11 commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Fixing. Will be in the next commit

ESCRI11 and others added 4 commits August 10, 2026 10:04
- col default was `col=col`, so any plot=TRUE call errored on a
  missing argument. Default to "grey70", matching plotOutlierScores.
- subsetting Z dropped to a vector for single-method calls, breaking
  rowMeans(). Added drop = FALSE.
- gate the isoforest behind "z.isoforest" %in% methods. It ran on
  every call, including the default where its column is discarded,
  which made isotree a hard dependency of the default path.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- expose outlier_methods on pgx.preprocess() so z.isoforest is
  reachable. Defaults to the current three methods, not NULL:
  detectOutlierSamples() reads NULL as "all methods", which would
  enable the isoforest for every existing caller.
- validate methods with match.arg(). An unmatched name silently
  produced a zero-column Z and NaN z-scores (reading as "no
  outliers") with plot=FALSE, and died in plotOutlierScores() with
  "subscript out of bounds" with plot=TRUE.
- swap irlba for stdlib svd() in outlier.isoforest_zscore. nv=3 was
  hardcoded, so it aborted at <=3 samples and warned at 4. Callers
  cap the input at 1000 rows, so a truncated solver gains nothing.
- clamp ndim against ncol(V), the matrix it is applied to, not the
  input dims.
- drop the unused scale argument.
- seq_len() instead of 1:ncol(Z).
- cover all of the above in test-pgx-outlier.R.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Found by a second review pass over the outlier changes.

- mad() is 0 as soon as over half the values tie, e.g. the same sample
  uploaded twice. All three z-scores then evaluated 0/0, so z.outlier
  was NaN and pgx.preprocess died on "missing value where TRUE/FALSE
  needed". The three methods shared one abs(x-median)/mad expression,
  so the guard lives in one local zscore(): fall back to the mean
  absolute deviation, and to zeros only when every value is identical.
  Returning zeros directly would instead mask a real outlier whenever
  over half the samples tie. Bit-identical whenever mad() > 0.
- make is.outlier NA-safe so no non-finite score can reach that if()
  again, whatever produces it.
- plotOutlierScores: compute ylim from finite values only (non-finite
  Z aborted with "need finite 'ylim' values", reachable since par
  gained a working default), and restore the caller's par() on exit.
- test the above, plus the tied-sample regression and plot=TRUE on
  degenerate input. Drop the isoforest which.max assertion: it was
  seed-luck, and the score's scale and sign are still under review.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ivokwee
ivokwee merged commit c8ebb4f into edgy Aug 10, 2026
1 check 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.

2 participants