Algorithm-Classifier-IsolationForest

 view release on metacpan or  search on metacpan

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

	if ( $self->_ensure_c_trees ) {
		my ( $n_pts, $x_packed ) = $self->_pack_input($data);
		my $sums_packed = "\0" x ( $n_pts * 8 );
		Algorithm::Classifier::IsolationForest::score_all_xs(
			$self->{_c_nodes},   $self->{_c_coef_idx},       $self->{_c_coef_val},
			$x_packed,           $sums_packed,               $n_pts,
			$self->{n_features}, scalar @{ $self->{trees} }, $self->{_use_openmp}
		);
		my $result = [];
		Algorithm::Classifier::IsolationForest::finalize_scores_xs( $sums_packed, $n_pts, $self->_score_inv, $result );
		return $result;
	} ## end if ( $self->_ensure_c_trees )

	my $sums = $self->_depth_sums($data);
	my $inv  = $self->_score_inv;
	return [ map { exp( -$_ * $inv ) } @$sums ];
} ## end sub score_samples

=head2 score_sample_tagged(\%row)

Scores a single sample supplied as a hashref of named feature values,
without learning it.  Returns a scalar anomaly score in (0, 1].

    my $score = $oif->score_sample_tagged({ cpu => 0.9, mem => 0.4 });

Croaks under the same conditions as L<tagged_row_to_array|/tagged_row_to_array(\%row, $caller)>.

=cut

sub score_sample_tagged {
	my ( $self, $row ) = @_;
	my $vec    = $self->tagged_row_to_array( $row, 'score_sample_tagged' );
	my $result = $self->score_samples( [$vec] );
	return $result->[0];
}

=head2 explain_samples(\@data, %opts)

Explains, per sample, which features drove its anomaly score, without
learning anything -- the streaming counterpart of the parent class's
method of the same name, returning the identical structure (see
C<explain_samples> in L<Algorithm::Classifier::IsolationForest> for the
full description of the output shape and the C<method> option):

    my $explanations = $oif->explain_samples(\@data);
    my $top          = $explanations->[0]{features}[0];

Differences from the batch class:

The default C<ablation> method substitutes per-feature medians of the
currently retained window (there is no fit() to store baselines at;
the window IS the model's view of normal, and it tracks drift for
free).  It therefore requires C<< window_size > 0 >> with learned
points and croaks otherwise -- use C<path> on a windowless model.

The C<path> method carries an extra caveat on top of the batch class's
(see the parent POD): online trees are shallow by construction (the
depth budget is C<log(n/max_leaf_samples)/log(4)>) and most of a
sample's anomalousness lives in the per-leaf count adjustment rather
than in which splits it crossed, so path attributions here are coarse.
Treat them as a rough second opinion; prefer C<ablation> whenever a
window exists.

A model that has not yet accumulated tree structure (fewer than
C<max_leaf_samples> points seen) scores everything 1.0 and has no
splits to attribute; every weight comes back 0.

=cut

sub explain_samples {
	my ( $self, $data, %opts ) = @_;
	$self->_check_learned;
	croak "explain_samples() expects a non-empty arrayref of samples"
		unless ref $data eq 'ARRAY' && @$data;

	my $method = delete $opts{method} // 'ablation';
	croak "explain_samples: method must be 'path' or 'ablation'"
		unless $method =~ /\A(?:path|ablation)\z/;
	croak "explain_samples: unknown option(s): " . join( ', ', sort keys %opts )
		if %opts;

	return $method eq 'ablation'
		? $self->_explain_ablation($data)
		: $self->_explain_path($data);
} ## end sub explain_samples

=head2 explain_sample_tagged(\%row, %opts)

Explains a single sample supplied as a hashref of named feature values,
without learning it.  Takes the same C<method> option as
L<explain_samples|/explain_samples(\@data, %opts)> and returns the single explanation hashref.

    my $e = $oif->explain_sample_tagged({ cpu => 0.9, mem => 0.4 });

Croaks under the same conditions as L<tagged_row_to_array|/tagged_row_to_array(\%row, $caller)>.

=cut

sub explain_sample_tagged {
	my ( $self, $row, %opts ) = @_;
	my $vec = $self->tagged_row_to_array( $row, 'explain_sample_tagged' );
	return $self->explain_samples( [$vec], %opts )->[0];
}

=head2 path_lengths(\@data)

Returns an arrayref of the mean isolation depth per sample across the
trees, for inspection -- the streaming counterpart of the parent class's
method of the same name.  Depths include the per-leaf count adjustment.

    my $depths = $oif->path_lengths(\@data);

=cut

sub path_lengths {
	my ( $self, $data ) = @_;
	$self->_check_learned;
	croak "path_lengths() expects an arrayref of samples"
		unless ref $data eq 'ARRAY';
	my $t = scalar @{ $self->{trees} };



( run in 0.782 second using v1.01-cache-2.11-cpan-d01c6094234 )