Algorithm-Classifier-IsolationForest

 view release on metacpan or  search on metacpan

t/32-pack-data.t  view on Meta::CPAN

#!perl
# 32-pack-data.t
#
# Verifies pack_data + accept-packed semantics on the five scoring
# methods: passing the PackedData wrapper produces the same results
# as passing the original arrayref, on the same model.

use strict;
use warnings;
use Test::More;
use List::Util qw(sum);

use Algorithm::Classifier::IsolationForest;

my $CLASS = 'Algorithm::Classifier::IsolationForest';

plan skip_all => 'pack_data requires the Inline::C backend'
	unless $Algorithm::Classifier::IsolationForest::HAS_C;

# Small but non-trivial dataset: 200 Gaussian-ish inliers + 5 outliers
# in 4 dims.  Enough that scoring exercises the C path meaningfully but
# fast enough to keep the test snappy.
sub gaussian {
	my ( $mu, $sigma ) = @_;
	my $u1 = rand() || 1e-12;
	my $u2 = rand();
	return $mu + $sigma * sqrt( -2 * log($u1) ) * cos( 2 * 3.14159265358979 * $u2 );
}

srand(123);
my @train;
push @train, [ map { gaussian( 0, 1 ) } 1 .. 4 ] for 1 .. 200;
push @train, [ map { 6 + rand() } 1 .. 4 ]       for 1 .. 5;

my $f = $CLASS->new( n_trees => 100, sample_size => 256, seed => 7 );
$f->fit( \@train );

# Query data: mix of inlier-like + a couple of obvious outliers.
srand(456);
my @query;
push @query, [ map { gaussian( 0, 1 ) } 1 .. 4 ] for 1 .. 50;
push @query, [  7, -7,  6, -6 ];
push @query, [ -8,  8, -7,  7 ];

# ----- pack_data sanity -----
my $packed = $f->pack_data( \@query );
isa_ok( $packed, 'Algorithm::Classifier::IsolationForest::PackedData', 'pack_data returns a PackedData' );
is( $packed->n_pts,   scalar @query, 'PackedData->n_pts matches' );
is( $packed->n_feats, 4,             'PackedData->n_feats matches' );

# ----- score_samples -----
subtest 'score_samples: packed == arrayref' => sub {
	my $sa = $f->score_samples( \@query );
	my $sp = $f->score_samples($packed);
	is( scalar @$sa, scalar @$sp, 'same length' );
	my $diffs = grep { $sa->[$_] != $sp->[$_] } 0 .. $#$sa;
	is( $diffs, 0, 'every score matches bit-for-bit' );
};

# ----- predict -----
subtest 'predict: packed == arrayref' => sub {
	my $la = $f->predict( \@query, 0.55 );
	my $lp = $f->predict( $packed, 0.55 );
	is( scalar @$la, scalar @$lp, 'same length' );
	my $diffs = grep { $la->[$_] != $lp->[$_] } 0 .. $#$la;
	is( $diffs, 0, 'every label matches' );



( run in 0.596 second using v1.01-cache-2.11-cpan-9581c071862 )