Algorithm-Classifier-IsolationForest
view release on metacpan or search on metacpan
benchmarking/BenchAccel.pm view on Meta::CPAN
# consumes ~N x the CPU time of its serial counterpart even when
# wall-clock time drops. That makes the c_openmp variant look
# *slower* than c_serial in cmpthese output -- the opposite of what
# a user actually experiences.
#
# This module replaces it with three Time::HiRes-based helpers, used
# across every bench script in this directory so they share a single
# timing path:
#
# wall_cmpthese($target_secs, \%vars)
# cmpthese-style comparison table, sorted slowest -> fastest with
# a pairwise percent-difference matrix. Prints only; returns
# nothing. Used when comparing several alternatives at once.
#
# wall_rate($code, $secs)
# Warm up briefly, then time $code for $secs wall-clock seconds.
# Returns ops/second as a scalar. Used when the script formats
# its own table (e.g. bench-sklearn-scoring's side-by-side
# Perl-vs-sklearn rows).
#
# wall_time_median($code, $reps)
lib/Algorithm/Classifier/IsolationForest.pm view on Meta::CPAN
} ## end sub _subsample
#-------------------------------------------------------------------------------
# Recursively build one isolation tree.
#
# A node is one of:
# leaf { leaf => 1, size => N }
# axis { attr => A, split => S, left => ..., right => ... }
# oblique { idx => [..], coef => [..], b => B, left => ..., right => ... }
#
# In both split styles the choice is restricted to features that actually vary
# across the points reaching the node: this avoids wasted levels on constant
# columns and lets a node leaf out exactly when its points are indistinguishable.
#
# Args:
# $X :: the points reaching this node, an arrayref of feature-value
# arrayrefs. Read-only; undef cells only appear under
# missing => 'nan'.
# $depth :: this node's depth, 0 at the root.
# $limit :: the height limit from _resolve_geometry. At or past it the
# node leafs out and c(size) covers the rest.
lib/Algorithm/Classifier/IsolationForest/App/Command/info.pm view on Meta::CPAN
#
# Example:
# _munger_summary($model); # { method => 'http_method_enum', ... }
sub _munger_summary {
my ($model) = @_;
my $mungers = $model->{mungers};
return undef unless ref $mungers eq 'HASH' && %$mungers;
return { map { $_ => ( ref $mungers->{$_} eq 'HASH' ? $mungers->{$_}{munger} : undef ) } keys %$mungers };
}
# Text-table rendering of the summary, matching the feature_names style.
#
# Args:
# $summary :: the hashref from _munger_summary, or undef.
#
# Returns: nothing. Prints a count line and one indented line per
# feature, sorted by name, to STDOUT. An undef summary prints nothing, so
# callers need not test first.
#
# Example:
# _print_mungers( _munger_summary($model) );
lib/Algorithm/Classifier/IsolationForest/App/Command/streamc.pm view on Meta::CPAN
],
[
'jsonl',
'Input lines are JSON rows instead of CSV: an array is positional, an object is a tagged row '
. '(full munger plan, raw values may contain anything JSON can). Output is the daemon\'s '
. 'reply JSON lines verbatim, one per request (--batch 1 for one per row).'
],
[
'batch=i',
'Rows per request message. Bigger amortises round trips; 1 gives per-row latency for '
. 'tail -F style pipelines.',
{ 'default' => 256 }
],
# command mode
[ 'ping', 'Check the daemon is alive; exits 0 on pong.' ],
[ 'stats', 'Print the daemon stats (seen, window, threshold, connections, set, ...).' ],
[ 'save', 'Ask the daemon to save the model now; prints the file name.' ],
[ 'relearn-threshold', 'Ask the daemon to relearn the contamination decision threshold.' ],
[ 'json', 'Command mode: print the raw JSON reply instead of the text rendering.' ],
);
t/80-sklearn-comparison-online.t view on Meta::CPAN
my $db = sqrt( $sbb / $n - $mb**2 );
return ( $da > 0 && $db > 0 ) ? $cov / ( $da * $db ) : 0;
} ## end sub spearman_rho
sub gaussian {
my ( $mu, $sigma ) = @_;
return $mu + $sigma * sqrt( -2 * log( rand() || 1e-12 ) ) * cos( 2 * PI * rand() );
}
# -----------------------------------------------------------------------
# Datasets: N-D Gaussian inliers + corner-style outliers, the same shape
# t/80-sklearn-comparison.t uses (and the same srand convention), with the
# inlier count scaled up in higher dimensions so the online model's depth
# budget log4(N/eta) gives its trees enough resolution to rank inliers.
#
# Gaussian inliers (rather than the batch test's regular grid) in every
# dimension: the tier-2 rank correlation needs real density structure both
# models can rank, and ranking among identical-density grid points is noise.
#
# Per-dataset knobs:
# eta -- max_leaf_samples for the online model (see header)
t/80-sklearn-comparison.t view on Meta::CPAN
#
# Cross-language validation: trains both this module and Python scikit-learn's
# IsolationForest on the same dataset and verifies that the two implementations
# agree on anomaly ordering. The whole file is skipped when Python or
# scikit-learn is not installed.
#
# The same battery of checks is run against multiple datasets so we exercise
# more than the 2-feature case:
#
# * "2d_grid" -- 225 inliers on a regular grid in [-1,1]^2 + 8 outliers
# * "5d_gaussian" -- 200 Gaussian inliers + 8 corner-style outliers (5 dims)
# * "10d_gaussian" -- 200 Gaussian inliers + 8 corner-style outliers (10 dims)
#
# Agreement is verified by three complementary checks per dataset:
# 1. Both models clearly separate the obvious outliers from the inliers
# (score direction test -- Perl: higher = anomalous; sklearn: lower).
# 2. Both models rank the obvious outliers as the top-N anomalies.
# 3. The Spearman rank correlation between the two score vectors is >= 0.85.
#
# Because the models use different RNG implementations they cannot produce
# identical floating-point scores, but any faithful Isolation Forest
# implementation produces highly correlated anomaly rankings on
t/80-sklearn-comparison.t view on Meta::CPAN
label => '2d_grid',
n_feat => 2,
inliers => \@inliers,
outliers => \@outliers,
data => [ @inliers, @outliers ],
n_in => scalar @inliers,
n_out => scalar @outliers,
};
} ## end sub make_2d_grid_dataset
# N-D Gaussian inliers + corner-style outliers far from origin in every axis.
# Deterministic via a fixed srand seed derived from the dimension.
sub make_nd_gaussian_dataset {
my ($nf) = @_;
srand( 20260629 + $nf );
my @inliers;
push @inliers, [ map { gaussian( 0, 0.3 ) } 1 .. $nf ] for 1 .. 200;
# Outliers: each coordinate at magnitude 5..8 with random sign so the
# point sits at a "corner" of the bounding box, well outside the inlier
t/pod-coverage.t view on Meta::CPAN
plan( skip_all => "Author tests not required for installation" );
}
# Ensure a recent version of Test::Pod::Coverage
my $min_tpc = 1.08;
eval "use Test::Pod::Coverage $min_tpc";
plan skip_all => "Test::Pod::Coverage $min_tpc required for testing POD coverage"
if $@;
# Test::Pod::Coverage doesn't require a minimum Pod::Coverage version,
# but older versions don't recognize some common documentation styles
my $min_pc = 0.18;
eval "use Pod::Coverage $min_pc";
plan skip_all => "Pod::Coverage $min_pc required for testing POD coverage"
if $@;
# Symbols that live in a package's symbol table but are not part of any
# public interface, so there is nothing for a user to read about them:
#
# - ALL_CAPS :: `use constant` values -- compile-time implementation
# detail (MAGIC, HEADER_LEN, MAX_INBUF, EULER, ...)
( run in 0.674 second using v1.01-cache-2.11-cpan-b16cb0d3907 )