App-rank

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN


0.006   2018-05-01 (PERLANCAR)

	- No functional changes.

	- [doc] Fix results in Synopsis.


0.005   2018-04-25 (PERLANCAR)

	- [doc] Add --calc-percentile example in Synopsis and more
	  documentation for options.

	- [Fix] Allow extrapolating to get requested low percentiles.

	- Forgot the standard Getopt::Long configuration (bundling +
	  no_ignore_case).


0.004   2018-04-25 (PERLANCAR)

	- Add options: --action (new action calc-percentile),
	  --calc-percentile to calculate specific percentiles.


0.003   2018-04-22 (PERLANCAR)

	- Add option: --rank.

	- [doc] Mention List::Rank & Sort::Rank.


0.002   2018-04-22 (PERLANCAR)

script/rank  view on Meta::CPAN


use 5.010001;
use strict;
use warnings;

use Getopt::Long qw(:config bundling no_ignore_case);

my %Opts = (
    action => 'rank',
    ignore_leading_blanks => 0,
    calc_percentile => [],
    reverse => 0,
    sort => 'ascii',
    field_separator => "\t",
    sort_field => 0,
    show_rank => 1,
    show_percentile => 0,
    rank => 'default',
    # TODO: --dictionary-order, -d
    # TODO: --ignore-nonprinting, -i
    # TODO: --human-numeric-sort, -h
    # TODO: --version-sort, -V
    # TODO: --percentile-format=s
);

sub parse_cmdline {
    my $res = GetOptions(
        'action=s' => \$Opts{action},
        'calc-percentile=s' => sub {
            $Opts{action} = 'calc-percentile';
            $Opts{calc_percentile} = [split /\s*,\s*/, $_[1]];
        },
        'ignore-leading-blanks|b' => \$Opts{ignore_leading_blanks},
        'ignore-case|f' => \$Opts{ignore_case},
        'reverse|r' => \$Opts{reverse},
        'field-separator|t=s' => \$Opts{field_separator},
        'numeric-sort|n' => sub { $Opts{sort} = 'numeric' },
        'sort=s' => \$Opts{sort},
        'sort-field=i' => \$Opts{sort_field},
        'rank=s' => \$Opts{rank},
        'show-rank!' => \$Opts{show_rank},
        'show-percentile!' => \$Opts{show_percentile},
        '-p' => sub { $Opts{show_percentile} = 1 },
        'help|h' => sub {
            print <<USAGE;
Usage:
  rank [OPTIONS]... [INPUT]...
  rank --help

For more details, see the manpage/documentation.
USAGE
            exit 0;
        },

script/rank  view on Meta::CPAN

    } else {
        if ($Opts{reverse}) {
            $sortsub = sub { $_[1] cmp $_[0] };
        } else {
            $sortsub = sub { $_[0] cmp $_[1] };
        }
    }

    @data = sort { $sortsub->($a->[0], $b->[0]) } @data;

    # exact percentiles to calculate
    my @percentiles;
    for (@{ $Opts{calc_percentile} }) {
        my $p = $_+0;
        die "rank: Invalid percentile '$_', must be (0,100]\n"
            unless $p > 0 && $p <= 100;
        push @percentiles, $p;
    }
    @percentiles = sort {$b <=> $a} @percentiles;

    # if we're using the 'no-skip' ranking system, we'll need to calculate
    # the lowest ranking (highest number)first
    my $lowest_rank;
    if ($Opts{rank} eq 'no-skip') {
        my $prev_sortkey;
        for my $row_num (0..$#data) {
            my $item = $data[$row_num];
            my $sortkey = $item->[0];
            if (!defined($prev_sortkey) ||
                    $sortsub->($sortkey, $prev_sortkey) > 0) {
                $lowest_rank //= 0;
                $lowest_rank++;
            }
            $prev_sortkey = $sortkey;
        }
    }

    my $rank;
    my $prev_sortkey;
    my $prev_percentile;
    for my $row_num (0..$#data) {
        my $item = $data[$row_num];
        my $sortkey = $item->[0];

        my $percentile;
        if ($Opts{rank} eq 'default') {
            if (!defined($prev_sortkey) ||
                    $sortsub->($sortkey, $prev_sortkey) > 0) {
                $rank = $row_num+1;
            }
            $percentile = (@data - $rank + 1)/@data * 100;
        } elsif ($Opts{rank} eq 'no-skip') {
            if (!defined($prev_sortkey) ||
                    $sortsub->($sortkey, $prev_sortkey) > 0) {
                $rank //= 0;
                $rank++;
            }
            $percentile = ($lowest_rank - $rank+1) / $lowest_rank * 100;
        } else { # no-same
            $rank = $row_num+1;
            $percentile = (@data - $rank + 1)/@data * 100;
        }

        if ($Opts{action} eq 'rank') {
            print $rank, $fs if $Opts{show_rank};
            printf "%.3f%s", $percentile, $fs
                if $Opts{show_percentile};
            print $item->[1];
        } elsif ($Opts{action} eq 'calc-percentile') {
            while (1) {
                last unless @percentiles;
                if ($percentile == $percentiles[0]) {
                    printf "%.3f\t%.8f\n", $percentile, $sortkey;
                    shift @percentiles;
                } elsif ($percentile < $percentiles[0] || $row_num == $#data) {
                    if (defined $prev_percentile) {
                        # linear interpolation
                        my $val = $prev_sortkey +
                            ($percentiles[0]-$prev_percentile) / ($percentile-$prev_percentile) * ($sortkey - $prev_sortkey);
                        printf "%.3f\t%.8f\n", $percentiles[0], $val;
                    } else {
                        printf "%.3f\t%s\n", $percentile, "N/A";
                    }
                    shift @percentiles;
                } else {
                    last;
                }
            }
            $prev_percentile = $percentile;
        }

        $prev_sortkey = $sortkey;
    }
}

# MAIN

parse_cmdline();
run();

script/rank  view on Meta::CPAN


 1    89    parjiyem
 2    77    nono
 3    75    robi
 3    75    tedi
 5    50    atang
 6    30    budi
 7    21    ujang

Sample output using C<rank -np> (a.k.a. C<rank --numeric-sort
--show-percentile>):

 1    100.000   89    parjiyem
 2    85.714    77    nono
 3    71.429    75    robi
 3    71.429    75    tedi
 5    42.857    50    atang
 6    28.571    30    budi
 7    14.286    21    ujang

Sample output using C<rank -n --calc-percentile 5,25,50,75,95>):

 95.000  84.80000000
 75.000  75.50000000
 50.000  56.25000000
 25.000  27.75000000
 5.000   15.15000000

=head1 EXIT CODES

0 on success.

script/rank  view on Meta::CPAN

255 on I/O error.

99 on command-line options error.

=head1 OPTIONS

=over

=item * --action=s (default: rank)

Valid values: C<rank>, C<calc-percentile>

The default action is to show ranking (C<rank>).

Action C<calc-percentile> will calculate specific percentiles. Usually you just
specify C<--calc-percentile a,b,c,...> to set action to C<calc-percentile>.

=item * --calc-percentile=f1,f2,...

Imply C<--action=calc-percentile>. Calculate specific percentile(s). Will use
linear interpolation to get the percentile values.

=item * --reverse, -r

=item * --ignore-leading-blanks, -b

=item * --ignore-case, -i

=item * --field-separator, -f (default: Tab)

=item * --sort=s

script/rank  view on Meta::CPAN

 1    89    parjiyem
 2    77    nono
 3    75    robi
 4    75    tedi
 5    50    atang
 6    30    budi
 7    21    ujang

=item * --no-show-rank

=item * --show-percentile, -p

=item * --help, -h

=item * --version, -v

=back

=head1 FAQ

=head1 HOMEPAGE



( run in 0.432 second using v1.01-cache-2.11-cpan-05162d3a2b1 )