Algorithm-Classifier-IsolationForest

 view release on metacpan or  search on metacpan

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

package Algorithm::Classifier::IsolationForest::App::Command::set_voting;

use strict;
use warnings;
use Algorithm::Classifier::IsolationForest ();
use Algorithm::Classifier::IsolationForest::App -command;
use File::Slurp  qw(read_file write_file);
use Scalar::Util qw(looks_like_number);

sub opt_spec {
	return (
		[
			'm=s',
			'Input model JSON file path/name.',
			{ 'default' => 'iforest_model.json', 'completion' => 'files' }
		],
		[
			'voting=s',
			"Target scoring-time aggregation: 'mean' (classic averaged score) or 'majority' (MVIForest per-tree vote)."
		],
		[
			'i=s',
			'CSV training data. Required only when the model was fit with contamination, so the decision threshold can be recalibrated for the new mode.',
			{ 'completion' => 'files' }
		],
		[ 'o=s', 'Write the updated model here instead of overwriting -m.', { 'completion' => 'files' } ],
		[ 'p',   'Print the updated model JSON instead of saving it.' ],
		[ 'w',   'Overwrite the -o file if it already exists.' ],
	);
} ## end sub opt_spec

sub abstract { 'Switch a saved model between mean and majority voting' }

sub description {
	'Switches the scoring-time aggregation of a saved model between "mean" and
"majority" and writes it back (in place over -m by default, or to -o / stdout).

The forest itself is voting-independent, so no tree is rebuilt. The one thing
that does not carry over is a contamination-learned decision threshold: it is a
quantile of whichever per-point quantity the mode thresholds against (the
averaged anomaly score under mean, the per-tree majority pivot under majority),
so switching relearns it for the target mode. That recalibration needs the
original training data, supplied as a CSV via -i. Models fit without
contamination carry no threshold and switch without -i.

Switches to new args are like below...

--voting -> voting
-i       -> training CSV (contamination models only)

';
} ## end sub description

sub validate {
	my ( $self, $opt, $args ) = @_;

	if ( !-f $opt->{'m'} ) {
		$self->usage_error( '-m, "' . $opt->{'m'} . '", is not a file or does not exist' );
	} elsif ( !-r $opt->{'m'} ) {
		$self->usage_error( '-m, "' . $opt->{'m'} . '", is not readable' );
	}

	if ( !defined( $opt->{'voting'} ) ) {
		$self->usage_error('--voting has not been specified');
	} elsif ( $opt->{'voting'} !~ /\A(?:mean|majority)\z/ ) {
		$self->usage_error( '--voting, "' . $opt->{'voting'} . '", must be either mean or majority' );
	}

	if ( defined( $opt->{'i'} ) ) {
		if ( !-f $opt->{'i'} ) {
			$self->usage_error( '-i, "' . $opt->{'i'} . '", is not a file or does not exist' );
		} elsif ( !-r $opt->{'i'} ) {
			$self->usage_error( '-i, "' . $opt->{'i'} . '", is not readable' );
		}
	}

	if ( defined( $opt->{'o'} ) && !$opt->{'w'} && -e $opt->{'o'} ) {
		$self->usage_error( '-o, "' . $opt->{'o'} . '", already exists and -w is not specified' );
	}

	return 1;
} ## end sub validate

sub execute {
	my ( $self, $opt, $args ) = @_;

	my $iforest = Algorithm::Classifier::IsolationForest->load( $opt->{'m'} );



( run in 1.659 second using v1.01-cache-2.11-cpan-c966e8aa7e8 )