Algorithm-Classifier-IsolationForest

 view release on metacpan or  search on metacpan

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

#
# Example:
#   my $acc = { nodes => 0, leaves => 0, max_depth => 0, depth_sum => 0 };
#   _walk_tree( $model->{trees}[0], 0, $acc );
sub _walk_tree {
	my ( $node, $depth, $acc ) = @_;
	$acc->{nodes}++;
	if ( $node->[0] == 0 ) {    # leaf
		$acc->{leaves}++;
		$acc->{max_depth} = $depth if $depth > $acc->{max_depth};
		$acc->{depth_sum} += $depth;
		return;
	}
	# Axis interior nodes have children at slots 3,4; oblique at 4,5.
	my ( $li, $ri ) = $node->[0] == 1 ? ( 3, 4 ) : ( 4, 5 );
	_walk_tree( $node->[$li], $depth + 1, $acc );
	_walk_tree( $node->[$ri], $depth + 1, $acc );
} ## end sub _walk_tree

# Whole-forest shape summary for the batch-model report: walks every tree
# into one accumulator, so the averages `info` prints are over the forest
# rather than per tree.
#
# Args:
#   $trees :: the model's trees, an arrayref of root nodes.
#
# Returns: a hashref of nodes, leaves, max_depth and depth_sum totalled
# across the forest.  Divide depth_sum by leaves for the mean leaf depth.
#
# Example:
#   my $stats = _tree_stats( $model->{trees} );
#   $stats->{depth_sum} / $stats->{leaves};   # mean leaf depth
sub _tree_stats {
	my ($trees) = @_;
	my $acc = { nodes => 0, leaves => 0, max_depth => 0, depth_sum => 0 };
	_walk_tree( $_, 0, $acc ) for @$trees;
	return $acc;
}

# Summary of a model's Algorithm::ToNumberMunger spec as a flat
# 'key => munger name' map (the full spec can be arbitrarily large --
# frozen count tables and the like -- so info only names the mungers).
#
# Args:
#   $model :: the loaded model, of either class.  Only its mungers slot is
#             read.
#
# Returns: a hashref of feature name => munger name, or undef when the
# model carries no mungers.  A name comes back undef when the spec entry
# is not a hashref, which _print_mungers shows as '(?)'.
#
# Example:
#   _munger_summary($model);   # { method => 'http_method_enum', ... }
sub _munger_summary {
	my ($model) = @_;
	my $mungers = $model->{mungers};
	return undef unless ref $mungers eq 'HASH' && %$mungers;
	return { map { $_ => ( ref $mungers->{$_} eq 'HASH' ? $mungers->{$_}{munger} : undef ) } keys %$mungers };
}

# Text-table rendering of the summary, matching the feature_names style.
#
# Args:
#   $summary :: the hashref from _munger_summary, or undef.
#
# Returns: nothing.  Prints a count line and one indented line per
# feature, sorted by name, to STDOUT.  An undef summary prints nothing, so
# callers need not test first.
#
# Example:
#   _print_mungers( _munger_summary($model) );
#   #   mungers               2 configured
#   #     method              http_method_enum
sub _print_mungers {
	my ($summary) = @_;
	return unless $summary;
	printf "  %-20s  %s\n", 'mungers', scalar( keys %$summary ) . ' configured';
	for my $k ( sort keys %$summary ) {
		printf "    %-18s  %s\n", $k, ( defined $summary->{$k} ? $summary->{$k} : '(?)' );
	}
	return;
}

# Online-model counterpart of _walk_tree: nodes are
# [0, count, lo, hi] / [1, count, lo, hi, attr, split, left, right],
# a tree record is { root, count, depth_limit }, and root may be undef
# on a tree that has not learned anything yet.
#
# Args:
#   $node :: the node to descend from.  Must be defined -- the caller
#            skips trees whose root is not.
#   $depth :: the depth to credit $node with, 0 for a root.
#   $acc :: the accumulator hashref, same keys as _walk_tree's.  Updated in
#           place.
#
# Returns: nothing; everything lands in $acc.
#
# Example:
#   _walk_tree_online( $tree->{root}, 0, $acc ) if defined $tree->{root};
sub _walk_tree_online {
	my ( $node, $depth, $acc ) = @_;
	$acc->{nodes}++;
	if ( $node->[0] == 0 ) {    # leaf
		$acc->{leaves}++;
		$acc->{max_depth} = $depth if $depth > $acc->{max_depth};
		$acc->{depth_sum} += $depth;
		return;
	}
	_walk_tree_online( $node->[6], $depth + 1, $acc );
	_walk_tree_online( $node->[7], $depth + 1, $acc );
} ## end sub _walk_tree_online

# Whole-forest shape summary for the online-model report.  The online
# counterpart of _tree_stats, and tolerant of the empty trees a
# barely-started model has.
#
# Args:
#   $trees :: the model's trees, an arrayref of { root, count, depth_limit }
#             records.  A record whose root is undef is skipped.
#
# Returns: a hashref of nodes, leaves, max_depth and depth_sum totalled



( run in 0.872 second using v1.01-cache-2.11-cpan-b16cb0d3907 )