NAME
Ranker - a module to rank a list of elements.
DESCRIPTION
This is a module for ranking a list/array of scorable elements using various ranking strategies written in Raku.
It's mostly based on Ilya Scharrenbroich's Ruby library by the same name.
INSTALLATION
Either:
from CPAN:
zef install Ranker
from local directory:
git clone git@gitlab.com:uzluisf/ranker.git
or download itzef install path/to/ranker/
SYNOPSIS
Default Ranking
Default ranking will assume values are numeric and rank them in their descending order. For example, a score of 100 is higher than a score of 50 and thus appears before as in [100, 50]
.
use Ranker;
my @scores = 1, 1, 2, 3, 3, 1, 4, 4, 5, 6, 8, 1, 0, 8;
my $rankings = Ranker::rank(@scores);
put $rankings.elems; #=> 8
my $ranking = $rankings[0];
given $ranking {
.rank .put; #=> 1
.score .put; #=> 8
.rankables .put; #=> [8, 8]
.percentile .put; #=> 100
.z-score .put; #=> 1.83921346366645
}
Custom Ranking by Block
Custom ranking allows for ranking of objects by using an anonymous subroutine which can created with sub
, with a pointy block or with a block.
class Player {
has Int $.score;
}
my @players = (0, 100, 1000, 25).map: Player.new(score => *);
my (&score, $rankings);
# Explicit:
&score = -> $player { $player.score };
$rankings = Ranker::rank(@players, by => &score);
# Still more explicit:
&score = sub( Player:D $player ) { $player.score };
$rankings = Ranker::rank(@players, by => &score);
# Same thing but more succint:
$rankings = Ranker::rank(@players, by => { $^player.score });
In some cases, objects need to be ranked by score in ascending order. For instance, if you were ranking golf players. In this case, 75 is higher than a score of 100 and thus appears before as in [75, 100]
.
class GolfPlayer is Player { }
my @golfplayers = (72, 100, 138, 54).map: GolfPlayer.new(score => *);
my $rankings = Ranker::rank(
@golfplayers, # Rankable values
by => -> gp { $gp.score }, # Block to rank by
:asc # Use ascending order
);
Ranking Strategies
Ranker provides several ranking strategies, which are mostly based on the Wikipedia entry for ranking. Strategies can be passed in as an option to the Ranker::rank
subroutine.
my $rankings = Ranker::rank(
@players,
by => { $^p.score },
strategy => 'ordinal',
);
The possible string values for the default strategies are: 'standard'
, 'modified'
, 'ordinal'
, 'dense'
, and 'fractional'
.
Standard Competition Ranking ("1224" ranking)
This is the default ranking strategy used by Ranker
. For more info, see the Wikipedia entry on Standard Competition Ranking.
my $rankings = Ranker::rank(
@players,
by => { $^p.score },
strategy => 'standard',
);
Modified Competition Ranking ("1334" ranking)
For more info, see the Wikipedia entry on Modified Competition Ranking.
my $rankings = Ranker::rank(
@players,
by => { $^p.score },
strategy => 'modified',
);
Dense Ranking ("1223" ranking)
For more info, see the Wikipedia entry on Dense Ranking
my $rankings = Ranker::rank(
@players,
by => { $^p.score },
strategy => 'dense',
);
Ordinal Ranking ("1234" ranking)
For more info, see the Wikipedia entry on Ordinal Ranking.
my $rankings = Ranker::rank(
@players,
by => { $^p.score },
strategy => 'ordinal',
);
Fractional Ranking ("1 2.5 2.5 4" ranking)
For more info, see the Wikipedia entry on Fractional Ranking.
my $rankings = Ranker::rank(
@players,
by => { $^p.score },
strategy => 'fractional',
);
Custom Strategies
Ranker
allows you to write your own strategies and supply them to the Ranker::rank
subroutine. To do this, you must compose the Strategy
role into a class and use ::()
to interpolate the class name into a package or variable name. Strategy
is the role from which the default «Strategy» classes are composed.
use Ranker;
use Ranker::Strategies;
# Composing Strategy into a class
class MyCustomStrategy does Strategy {
method execute {
# You must implement this method ;-)
}
}
my $rankings = Ranker::rank(
@players,
by => -> $p { $p.score },
strategy => ::('MyCustomStrategy') # Passing the package-interpolated class name
);
By default, only the Strategy
role is exported with use Ranker::Strategies
, which must be composed before it's used. To export a default strategy, you must specify it with a use
statement. For instance, use Ranker::Strategies :dense
exports Strategies::Dense
which can then be instantiated:
use Ranker::Strategies :dense;
my @scores = 44, 42, 42, 43;
my $ds = Dense.new: rankables => @scores, options => { :asc };
ROUTINES
Ranker
Ranker::rank(@rankables, *%options)
@rankables
- list of elements to be ranked%options
- key-value pairs to specify a custom ranking withby
(e.g.,by => { $^value }
), a sorting order withasc
(e.g.,asc => True
) and strategy to be used withstrategy
(e.g.,strategy => 'dense'
).
By default, values are assumed to be numeric and ranked in descending order using the standard-competition ranking strategy. Thus, the following two calls achieve the same thing:
Ranker::rank(@values);
Ranker::rank(@values, :by({ $^value }), :asc(False), :strategy('standard'));
Others
Run p6doc
on Ranker::Ranking
, Ranker::Rankings
and Ranker::Strategies
to get the methods made available by Ranking
, Rankings
and Strategy
respectively.
AUTHOR
Luis F. Uceta
LICENSE
Artistic License 2.0