Help language development. Donate to The Perl Foundation
Ranker - a module to rank a list of elements.
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.
Either:
from CPAN:
zef install Ranker
from local directory:
git clone [email protected]:uzluisf/ranker.git
or download it
zef install path/to/ranker/
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 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
);
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'
.
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',
);
For more info, see the Wikipedia entry on Modified Competition Ranking.
my $rankings = Ranker::rank(
@players,
by => { $^p.score },
strategy => 'modified',
);
For more info, see the Wikipedia entry on [Dense Ranking](https://en.wikipedia.org/wiki/Ranking#Dense_ranking_(%221223%22_ranking) )
my $rankings = Ranker::rank(
@players,
by => { $^p.score },
strategy => 'dense',
);
For more info, see the Wikipedia entry on [Ordinal Ranking](https://en.wikipedia.org/wiki/Ranking#Ordinal_ranking_(%221234%22_ranking) ).
my $rankings = Ranker::rank(
@players,
by => { $^p.score },
strategy => 'ordinal',
);
For more info, see the Wikipedia entry on [Fractional Ranking](https://en.wikipedia.org/wiki/Ranking#Fractional_ranking_(%221_2.5_2.5_4%22_ranking) ).
my $rankings = Ranker::rank(
@players,
by => { $^p.score },
strategy => 'fractional',
);
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 };
Ranker
Ranker::rank(@rankables, *%options)
@rankables
- list of elements to be ranked
%options
- key-value pairs to specify a custom ranking with by
(e.g., by => { $^value }
), a sorting order with asc
(e.g., asc => True
) and strategy to be used with strategy
(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'));
Run p6doc
on Ranker::Ranking
, Ranker::Rankings
and Ranker::Strategies
to get the methods made available by Ranking
, Rankings
and Strategy
respectively.
Luis F. Uceta
Artistic License 2.0