Algorithm-Classifier-IsolationForest

 view release on metacpan or  search on metacpan

lib/Algorithm/Classifier/IsolationForest.pm  view on Meta::CPAN

	croak "sample_size must be >= 1" unless $self->{sample_size} >= 1;
	croak "extension_level must be >= 0"
		if defined $self->{extension_level} && $self->{extension_level} < 0;
	croak "contamination must be a number in (0, 0.5]"
		if defined $self->{contamination}
		&& !( $self->{contamination} > 0 && $self->{contamination} <= 0.5 );
	croak "parallel_fit must be a positive integer"
		if defined $self->{parallel_fit}
		&& ( $self->{parallel_fit} !~ /^\d+$/ || $self->{parallel_fit} < 1 );

	return bless $self, $class;
} ## end sub new

=head2 decision_threshold

The score cutoff C<predict> uses by default; undef unless C<contamination> was
set.

=cut

sub decision_threshold { return $_[0]->{threshold} }

lib/Algorithm/Classifier/IsolationForest.pm  view on Meta::CPAN

		unless ref $data eq 'ARRAY' && @$data;
	my @rows;
	for my $i ( 0 .. $#$data ) {
		push @rows, $self->tagged_row_to_array( $data->[$i], "fit_tagged (row $i)" );
	}
	return $self->fit( \@rows );
} ## end sub fit_tagged

=head2 pack_data(\@data)

Returns an opaque, blessed wrapper around the input dataset that the
scoring methods can use directly, skipping the per-call work of walking
the arrayref-of-arrayrefs and converting each cell into a double.  At
high feature counts this is a meaningful win when the same dataset is
scored repeatedly (e.g. interactive threshold tuning, dashboards,
plotting that updates as parameters change).

Requires the Inline::C backend; croaks if C<use_c> is false.

    my $packed = $forest->pack_data(\@data);

lib/Algorithm/Classifier/IsolationForest.pm  view on Meta::CPAN

		_use_openmp     => $HAS_OPENMP,
		_use_openmp_fit => 0,                        # opt-in only; loaded models never re-fit implicitly
	};
	croak "model contains no trees" unless @{ $self->{trees} };

	# Recompute the normalising constant from the (integer, exact) sub-sample
	# size rather than trusting the stored float, so a reloaded model's scores
	# are bit-for-bit identical to the original's.
	$self->{c_psi} = _c( $self->{psi_used} ) if defined $self->{psi_used};

	my $model = bless $self, $class;
	$model->_rebuild_c_trees() if $self->{_use_c};
	return $model;
} ## end sub from_json

=head2 save($path)

Saves the model to the specified path.

    $iforest->save($path);

lib/Algorithm/Classifier/IsolationForest.pm  view on Meta::CPAN

	}
	return \@trees;
} ## end sub _unpack_forest

#-------------------------------------------------------------------------------
# Packed input wrapper.  pack_data() returns one of these so callers can
# score the same dataset many times without re-walking the AV/AV refs on
# every call -- a meaningful win at high feature counts where
# pack_input_xs is a non-trivial slice of total scoring time.
#
# It's a minimal blessed hashref: { packed, n_pts, n_feats }.  The C
# scoring functions only need the packed bytes + dimensions.
#-------------------------------------------------------------------------------
sub pack_data {
	my ( $self, $data ) = @_;
	$self->_check_fitted;
	croak "pack_data requires the Inline::C backend; install Inline::C"
		unless $self->{_use_c};
	croak "pack_data() expects an arrayref of samples"
		unless ref $data eq 'ARRAY';
	my $n_pts    = scalar @$data;
	my $nf       = $self->{n_features};
	my $x_packed = "\0" x ( $n_pts * $nf * 8 );
	my ( $mode, $fill ) = $self->_pack_args;
	pack_input_xs( $data, $x_packed, $n_pts, $nf, $mode, $fill );
	return bless {
		packed  => $x_packed,
		n_pts   => $n_pts,
		n_feats => $nf,
		},
		'Algorithm::Classifier::IsolationForest::PackedData';
} ## end sub pack_data

# Internal helper: given $data that may be a raw arrayref OR a PackedData
# instance, return the (n_pts, n_feats, x_packed) triple ready for
# score_all_xs.  Called from every scoring fast path.

lib/Algorithm/Classifier/IsolationForest/App/Command/predict.pm  view on Meta::CPAN

	my $score_input;    # what we hand to score_predict_samples

	if ( Algorithm::Classifier::IsolationForest::App::Command::pack::is_packed_file( $opt->{'i'} ) ) {
		my ( $n_pts, $n_feats, $bytes )
			= Algorithm::Classifier::IsolationForest::App::Command::pack::read_packed_file( $opt->{'i'} );
		die "packed input has $n_feats features but model expects " . $iforest->{n_features} . "\n"
			if $n_feats != $iforest->{n_features};

		# Build a PackedData wrapper directly from the on-disk bytes --
		# no CSV parse, no pack_input_xs.
		$score_input = bless {
			packed  => $bytes,
			n_pts   => $n_pts,
			n_feats => $n_feats,
			},
			'Algorithm::Classifier::IsolationForest::PackedData';

		# Only unpack to per-row arrayrefs when -d asks for it, since
		# that work undoes the whole point of using a packed file.
		if ( $opt->{'d'} ) {
			my @doubles = unpack( 'd*', $bytes );

lib/Algorithm/Classifier/IsolationForest/Online.pm  view on Meta::CPAN

	croak "subsample must be in (0, 1]"
		unless $self->{subsample} > 0 && $self->{subsample} <= 1;
	croak "contamination must be a number in (0, 0.5]"
		if defined $self->{contamination}
		&& !( $self->{contamination} > 0 && $self->{contamination} <= 0.5 );

	$self->{trees} = [ map { { root => undef, count => 0, depth_limit => 0 } } 1 .. $self->{n_trees} ];

	srand( $self->{seed} ) if defined $self->{seed};

	return bless $self, $class;
} ## end sub new

=head2 learn(\@data)

Learns the passed samples, in order, as the next points of the stream.
Once the model has seen more than C<window_size> points, each learned
point also forgets the oldest retained point, so the model tracks the
most recent C<window_size> points.

The data format matches the parent class's C<fit>: an arrayref of

lib/Algorithm/Classifier/IsolationForest/Online.pm  view on Meta::CPAN

		seen                 => $p->{seen}         // 0,
		window               => $payload->{window} // [],
		trees                => [],
		_use_c               => $Algorithm::Classifier::IsolationForest::HAS_C,
		_use_openmp          => $Algorithm::Classifier::IsolationForest::HAS_OPENMP,
	};

	my $trees = $payload->{trees};
	croak "model contains no trees" unless ref $trees eq 'ARRAY' && @$trees;

	my $model = bless $self, $class;

	# depth_limit is a pure function of the tree's count, so recompute it
	# rather than trusting a stored float.
	$self->{trees}
		= [ map { { count => $_->{count}, root => $_->{root}, depth_limit => $model->_rpl( $_->{count} ) } } @$trees ];

	return $model;
} ## end sub from_json

=head2 save($path)



( run in 1.202 second using v1.01-cache-2.11-cpan-0b5f733616e )