Algorithm-Classifier-IsolationForest

 view release on metacpan or  search on metacpan

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

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

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

sub opt_spec {
	return (
		[
			'from-model=s',
			'Extract a prototype from this saved model JSON (batch or online).',
			{ 'completion' => 'files' }
		],
		[
			'check=s',
			'Validate this prototype file and print a summary of it; exits non-zero when invalid.',
			{ 'completion' => 'files' }
		],
		[
			'o=s',
			'Output the prototype to this file instead of printing (--from-model only).',
			{ 'completion' => 'files' }
		],
		[ 'w', 'If the file specified via -o exists, over write it.' ],
	);
} ## end sub opt_spec

sub abstract { 'Extract a prototype from a saved model, or validate a prototype file' }

sub description {
	'Works with model prototypes: small JSON documents holding the variable
schema (feature names, per-feature descriptions, munger specs, missing
policy), a user-owned schema_version and schema_description, and
optionally the tuning knobs.  `iforest fit --prototype` and
`iforest stream --prototype` create models from one; see PROTOTYPES in
the Algorithm::Classifier::IsolationForest POD for the file format.

--from-model extracts a prototype from a saved model, closing the loop:
pull the schema and knobs out of a good model, edit the metadata, and
create fresh models from it.  A model with no recorded schema_version /
schema_description gets placeholder values to edit in.

--check validates a prototype file and prints a summary of what it
describes, exiting non-zero when the file is not a valid prototype.

Exactly one of --from-model or --check must be given.
';
} ## end sub description

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

	my $from  = defined $opt->{'from_model'} ? 1 : 0;
	my $check = defined $opt->{'check'}      ? 1 : 0;
	if ( $from + $check != 1 ) {
		$self->usage_error('exactly one of --from-model or --check must be specified');
	}

	my ( $switch, $file ) = $from ? ( '--from-model', $opt->{'from_model'} ) : ( '--check', $opt->{'check'} );
	if ( !-f $file ) {
		$self->usage_error( $switch . ', "' . $file . '", is not a file or does not exist' );
	} elsif ( !-r $file ) {
		$self->usage_error( $switch . ', "' . $file . '", is not readable' );
	}

	if ( defined $opt->{'o'} ) {
		if ($check) {
			$self->usage_error('-o may only be used with --from-model');
		}
		if ( -e $opt->{'o'} && !$opt->{'w'} ) {
			$self->usage_error( '-o, "' . $opt->{'o'} . '", already exists and -w is not specified' );
		}
	}

	return 1;
} ## end sub validate

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

	if ( defined $opt->{'from_model'} ) {
		my $model      = Algorithm::Classifier::IsolationForest->load( $opt->{'from_model'} );
		my $proto_json = $model->to_prototype;
		if ( defined $opt->{'o'} ) {
			write_file( $opt->{'o'}, { 'atomic' => 1 }, $proto_json . "\n" );
		} else {
			print $proto_json. "\n";
		}
		return 1;
	} ## end if ( defined $opt->{'from_model'} )

	# --check: structural validation plus a human summary of the file.
	my $raw   = read_file( $opt->{'check'} );
	my $proto = eval { Algorithm::Classifier::IsolationForest->validate_prototype($raw) };
	die( '--check, "' . $opt->{'check'} . '", is not a valid prototype: ' . $@ ) if $@;

	my $schema = $proto->{schema};
	my $tags   = $schema->{feature_names};
	printf "  %-20s  %s\n", 'file',               $opt->{'check'};
	printf "  %-20s  %s\n", 'class',              $proto->{class};
	printf "  %-20s  %s\n", 'schema_version',     $proto->{schema_version};



( run in 1.338 second using v1.01-cache-2.11-cpan-995e09ba956 )