File Coverage

blib/lib/Statistics/RankOrder.pm
Criterion Covered Total %
statement 84 84 100.0
branch 6 6 100.0
condition 3 3 100.0
subroutine 13 13 100.0
pod 7 7 100.0
total 113 113 100.0


line stmt bran cond sub pod time code
1 5     5   193482 use 5.008001;
  5         21  
  5         207  
2 5     5   28 use strict;
  5         10  
  5         340  
3 5     5   25 use warnings;
  5         11  
  5         380  
4              
5             package Statistics::RankOrder;
6             # ABSTRACT: Algorithms for determining overall rankings from a panel of judges
7             our $VERSION = '0.13'; # VERSION
8              
9 5     5   25 use Carp;
  5         11  
  5         870  
10             use Class::Tiny {
11 14         444 _data => sub { [] }
12 5     5   6275 };
  5         35781  
  5         57  
13              
14              
15              
16             sub add_judge {
17 70     70 1 15303 my ( $self, $obs ) = @_;
18 70         89 push @{ $self->_data }, $obs;
  70         1815  
19 70         479 return scalar @{ $self->_data };
  70         1674  
20             }
21              
22              
23             sub best_majority_rank {
24 5     5 1 46 my ($self) = shift;
25 5         10 my %candidates = $self->candidates;
26 5         9 my %best_maj;
27 5         21 while ( my ( $cand, $scores ) = ( each %candidates ) ) {
28 25         58 my @sorted = sort { $a <=> $b } @$scores;
  189         207  
29 25         41 my $index = int( @sorted / 2 );
30 25         30 my $bom = $sorted[$index];
31 25         37 my ( $som, $tom, $to ) = (0) x 3;
32 25         34 for (@sorted) {
33 125         115 $to += $_;
34 125 100       230 $tom += $_, $som++ if $_ <= $bom;
35             }
36 25         146 $best_maj{$cand} = {
37             bom => $bom,
38             som => $som,
39             tom => $tom,
40             to => $to
41             };
42             }
43 5         7 my %compare;
44 5         13 for my $k ( keys %best_maj ) {
45 25         34 $compare{$k} = 0;
46             $compare{$k} += (
47             $best_maj{$k}{bom} <=> $best_maj{$_}{bom} # low is good
48             || $best_maj{$_}{som} <=> $best_maj{$k}{som} # high is good
49             || $best_maj{$k}{tom} <=> $best_maj{$_}{tom} # low is good
50             || $best_maj{$k}{to} <=> $best_maj{$_}{to} # low is good
51 25   100     530 ) for keys %best_maj;
52             }
53 5         18 return _scores_to_ranks(%compare);
54             }
55              
56              
57             sub candidates {
58 12     12 1 160 my ($self) = @_;
59 12         17 my %c;
60 12         29 for my $j ( $self->judges ) {
61 60         409 push @{ $c{ $j->[$_] } }, $_ for 0 .. $#{$j};
  60         120  
  300         614  
62             }
63 12         76 return %c;
64             }
65              
66              
67             sub judges {
68 19     19 1 39 my ($self) = @_;
69 19         23 return @{ $self->_data };
  19         396  
70             }
71              
72              
73             sub mean_rank {
74 2     2 1 25 my ($self) = shift;
75 2         10 return $self->trimmed_mean_rank(0);
76             }
77              
78              
79             sub median_rank {
80 2     2 1 22 my ($self) = shift;
81 2         6 my %candidates = $self->candidates;
82 2         4 my %medians;
83 2         9 while ( my ( $cand, $scores ) = ( each %candidates ) ) {
84 10         24 my @sorted = sort { $a <=> $b } @$scores;
  78         87  
85 10         17 my $index = int( @sorted / 2 );
86 10         35 $medians{$cand} = $sorted[$index];
87             }
88 2         8 return _scores_to_ranks(%medians);
89             }
90              
91              
92             sub trimmed_mean_rank {
93 6     6 1 49 my ( $self, $trim ) = @_;
94 6 100       24 die "Can't trim away all scores" if 2 * $trim >= $self->judges;
95 5         51 my %candidates = $self->candidates;
96 5         11 my %means;
97 5         22 while ( my ( $cand, $scores ) = ( each %candidates ) ) {
98 25         102 my @sorted = sort { $a <=> $b } @$scores;
  200         243  
99 25         75 @sorted = @sorted[ $trim .. $#sorted - $trim ];
100 25         35 my $avg = 0;
101 25         86 $avg += $_ for @sorted;
102 25         122 $means{$cand} = $avg / @sorted;
103             }
104 5         22 return _scores_to_ranks(%means);
105             }
106              
107             #--------------------------------------------------------------------------#
108             # Private functions
109             #--------------------------------------------------------------------------#
110              
111             sub _scores_to_ranks {
112 12     12   32 my (%scores) = @_;
113 12         17 my %ranks;
114 12         16 my $cur_rank = 0;
115 12         16 my $index = 0;
116 12         18 my $last_score = -keys %scores;
117 12         170 for my $cand ( sort { $scores{$a} <=> $scores{$b} } keys %scores ) {
  87         123  
118 60         77 $index++;
119 60 100       128 $cur_rank = $index if $scores{$cand} > $last_score;
120 60         76 $ranks{$cand} = $cur_rank;
121 60         110 $last_score = $scores{$cand};
122             }
123 12         161 return %ranks;
124             }
125              
126             1;
127              
128              
129             # vim: ts=4 sts=4 sw=4 et:
130              
131             __END__