Algorithm-ToNumberMunger

 view release on metacpan or  search on metacpan

lib/Algorithm/ToNumberMunger.pm  view on Meta::CPAN

# is a single line and they can never drift apart. mgcp_enum is deliberately
# NOT a row here: its strict range has a hole (8xx exists, 6xx/7xx do not),
# which a single [lo, hi] cannot express, so it has its own builder below.
my %STATUS_PROTO = (
	http   => [ 100, 599 ],       # 1xx-5xx
	smtp   => [ 200, 599 ],       # 2xx-5xx; SMTP never issues 1yz in practice
	sip    => [ 100, 699 ],       # 1xx-6xx; SIP adds a 6xx global-failure class
	ftp    => [ 100, 599 ],       # 1xx-5xx FTP reply codes
	rtsp   => [ 100, 599 ],       # RTSP (RFC 2326) reuses HTTP's status scheme
	nntp   => [ 100, 599 ],       # 1xx-5xx NNTP (RFC 3977), SMTP-convention codes
	dict   => [ 100, 599 ],       # DICT (RFC 2229) uses SMTP-style codes
	gemini => [ 10,  69, 10 ],    # two-digit codes, 1x-6x; class = int(code/10)
);
for my $proto ( keys %STATUS_PROTO ) {
	my ( $lo, $hi, $div ) = @{ $STATUS_PROTO{$proto} };
	$div = 100 unless defined $div;
	$BUILDERS{"${proto}_enum"}
		= sub { _status_class_munger( $proto, $lo, $hi, $div, @_ ) };
}

# ratio and combine consume several source fields at once, so they are only

lib/Algorithm/ToNumberMunger.pm  view on Meta::CPAN

what an Isolation Forest splits on most naturally.

=item * C<freq> - the probability itself, C<(count + smoothing) / denom>.

=item * C<log_count> - C<ln(1 + count)>, the count with its heavy tail tamed.

=item * C<count> - the raw count.

=back

Probabilities use add-one style C<smoothing> (default C<1>), treating "unseen" as
one aggregate bucket: C<prob(v) = (count + smoothing) / (total + smoothing*(V+1))>
where C<V> is the number of listed values. C<unseen> controls what a value absent
from the table maps to -- C<'rare'> (default) emits that value under the current
mode as if it had been seen zero times (for C<neg_log_prob>/C<freq> this is the
smoothed unseen bucket, for C<count>/C<log_count> it is C<0>), or a number to
force a fixed default. Because an unseen value is usually the very thing you are
hunting, mapping it to "maximally rare" rather than erroring is the point.

C<frozen_freq_map> only suits B<bounded, moderate-cardinality> columns (extensions,
vendor classes, named pipes, keyboard layouts, link addresses): the table lives

lib/Algorithm/ToNumberMunger.pm  view on Meta::CPAN

SMTP, NNTP does issue C<1xx> replies (help text, capability lists), so the
strict floor is C<100> rather than C<smtp_enum>'s C<200>. With a true
C<strict>, inputs outside C<100>-C<599> croak.

=head2 dict_enum

    { munger => 'dict_enum' }
    { munger => 'dict_enum', strict => 1 }

The DICT counterpart of L</http_enum>, for DICT protocol (RFC 2229) status
codes, which use the SMTP-style code classes. With a true C<strict>, inputs
outside C<100>-C<599> croak.

=head2 gemini_enum

    { munger => 'gemini_enum' }
    { munger => 'gemini_enum', strict => 1 }

Like L</http_enum> but for the Gemini protocol, whose status codes are B<two>
digits -- C<1x> input expected, C<2x> success, C<3x> redirect, C<4x> temporary
failure, C<5x> permanent failure, C<6x> client certificate required -- so the

lib/Algorithm/ToNumberMunger.pm  view on Meta::CPAN

		my ($v) = @_;
		croak "zscore munger$where: '" . ( defined $v ? $v : 'undef' ) . "' is not numeric"
			unless looks_like_number($v);
		return ( $v - $mean ) / $std;
	};
} ## end sub _build_zscore

=head2 log

    { munger => 'log' }                 # natural log
    { munger => 'log', offset => 1 }    # log1p-style, so 0 is allowed
    { munger => 'log', base => 10, offset => 1 }

Logarithm of C<v + offset>. Heavy-tailed counts (bytes, durations) compress well
under a log, which keeps a few huge values from dominating the forest. C<offset>
(default C<0>) shifts the input so zeros/small values are representable; the
shifted value must be strictly positive or the input croaks. C<base> defaults to
natural log.

=cut

t/mungers-datetime-fast.t  view on Meta::CPAN

	};
	my $r1 = $code->apply_named( { ts => '2026-07-05T00:00:00' } );
	my $r2 = $code->apply_named( { ts => '2026-07-05T00:00:00' } );
	is_deeply( $r2, $r1, 'multi memo repeat returns the identical pair' );
	my $r3 = $code->apply_named( { ts => '2026-07-06T12:00:00' } );
	ok( abs( $r3->[1] - $r1->[1] ) > 0.1, 'multi memo does not serve stale pairs' );
}

# ---- non-fast formats still work via strptime -------------------------------
{
	# %b (alphabetic month) is not fast-eligible; apache-style log stamp.
	my $c = $M->build(
		{
			munger => 'datetime',
			format => '%d/%b/%Y:%H:%M:%S',
			part   => 'hour'
		}
	);
	is( $c->('05/Jul/2026:13:37:42'), 13, 'strptime path for %b formats' );

	# a fast format missing some codes (date-only) also stays on strptime

t/pod-coverage.t  view on Meta::CPAN

	plan( skip_all => "Author tests not required for installation" );
}

# Ensure a recent version of Test::Pod::Coverage
my $min_tpc = 1.08;
eval "use Test::Pod::Coverage $min_tpc";
plan skip_all => "Test::Pod::Coverage $min_tpc required for testing POD coverage"
	if $@;

# Test::Pod::Coverage doesn't require a minimum Pod::Coverage version,
# but older versions don't recognize some common documentation styles
my $min_pc = 0.18;
eval "use Pod::Coverage $min_pc";
plan skip_all => "Pod::Coverage $min_pc required for testing POD coverage"
	if $@;

all_pod_coverage_ok();



( run in 2.528 seconds using v1.01-cache-2.11-cpan-b16cb0d3907 )