Ranking methods

From Rosetta Code
Revision as of 18:49, 25 August 2014 by Steenslag (talk | contribs) (→‎{{header|Ruby}}: Added Ruby)
Ranking methods is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

The numerical rank of competitors in a competition shows if one is better than, equal to, or worse than another based on their results in a competition.

The numerical rank of a competitor can be assigned in several different ways.

Task

The following scores are accrued for all competitors of a competition (in best-first order):

44 Solomon
42 Jason
42 Errol
41 Garry
41 Bernard
41 Barry
39 Stephen

Create functions/methods/procedures/subroutines... that apply one, each, of the following ranking methods to an ordered list of scores with scorers:

  1. Standard. (Ties share what would have been their first ordinal number).
  2. Modified. (Ties share what would have been their last ordinal number).
  3. Dense. (Ties share the next available integer).
  4. Ordinal. ((Competitors take the next available integer. Ties are not treated otherwise).
  5. Fractional. (Ties share the mean of what would have been their ordinal numbers).

See the wikipedia article for a fuller description.

Show here, on this page, the ranking of the test scores under each of the numbered ranking methods.

AWK

Translation of: Python

This uses separate files for each method of ranking: <lang awk>##

    1. Dense ranking in file: ranking_d.awk

BEGIN{ lastresult = "!"; lastrank = 0 }

function d_rank(){

   if($1==lastresult){
       print lastrank, $0
   }else{
       lastresult = $1
       print ++lastrank, $0 }

} //{d_rank() }

    1. Fractional ranking in file: ranking_f.awk

BEGIN{

   last = "!"
   flen = 0 }

function f_rank(){

   item = $0
   if($1!=last){
       if(flen){
           sum = 0
           for(fl=0; fl < flen;){
               $0 = fifo[fl++]
               sum += $1 }
           mean = sum / flen
           for(fl=0; fl < flen;){
               $0 = fifo[fl++]
               $1 = ""
               printf("%3g %s\n", mean, $0) }
           flen = 0
   }}
   $0 = item
   last = $1
   fifo[flen++] = sprintf("%i %s", FNR, item)

} //{f_rank()}

END{ if(flen){

       sum = 0
       for(fl=0; fl < flen;){
           $0 = fifo[fl++]
           sum += $1 }
       mean = sum / flen
       for(fl=0; fl < flen;){
           $0 = fifo[fl++]
           $1 = ""
           printf("%3g %s\n", mean, $0) }}}
    1. Modified competition ranking in file: ranking_mc.awk

BEGIN{

   lastresult = "!"
   flen = 0 }

function mc_rank(){

   if($1==lastresult){
       fifo[flen++] = $0
   }else{
       for(fl=0; fl < flen;){
           print FNR-1, fifo[fl++]}
       flen = 0
       fifo[flen++] = $0
       lastresult = $1}

} //{mc_rank()}

END{ for(fl=0; fl < flen;){

       print FNR, fifo[fl++]} }
    1. Ordinal ranking in file: ranking_o.awk

function o_rank(){ print FNR, $0 } //{o_rank() }

    1. Standard competition ranking in file: ranking_sc.awk

BEGIN{ lastresult = lastrank = "!" }

function sc_rank(){

   if($1==lastresult){
       print lastrank, $0
   }else{
       print FNR, $0
       lastresult = $1
       lastrank = FNR}

} //{sc_rank()} </lang>

The input as a file ranking.txt:

44 Solomon
42 Jason
42 Errol
41 Garry
41 Bernard
41 Barry
39 Stephen
Output:
C:\Users\RC\Code>awk -f ranking_sc.awk ranking.txt
1 44 Solomon
2 42 Jason
2 42 Errol
4 41 Garry
4 41 Bernard
4 41 Barry
7 39 Stephen

C:\Users\RC\Code>awk -f ranking_mc.awk ranking.txt
1 44 Solomon
3 42 Jason
3 42 Errol
6 41 Garry
6 41 Bernard
6 41 Barry
7 39 Stephen

C:\Users\RC\Code>awk -f ranking_d.awk ranking.txt
1 44 Solomon
2 42 Jason
2 42 Errol
3 41 Garry
3 41 Bernard
3 41 Barry
4 39 Stephen

C:\Users\RC\Code>awk -f ranking_o.awk ranking.txt
1 44 Solomon
2 42 Jason
3 42 Errol
4 41 Garry
5 41 Bernard
6 41 Barry
7 39 Stephen

C:\Users\RC\Code>awk -f ranking_f.awk ranking.txt
  1  44 Solomon
2.5  42 Jason
2.5  42 Errol
  5  41 Garry
  5  41 Bernard
  5  41 Barry
  7  39 Stephen

C:\Users\RC\Code>

J

Implementation:

<lang J>competitors=:<;._1;._2]0 :0

44 Solomon
42 Jason
42 Errol
41 Garry
41 Bernard
41 Barry
39 Stephen

)

scores=: {."1

standard=: 1+i.~ modified=: 1+i:~ dense=: #/.~ # #\@~. ordinal=: #\ fractional=: #/.~ # ] (+/%#)/. #\

rank=:1 :'<"0@u@:scores,.]'</lang>

Note that we assume that the competitors are already in the right order. Also, of course (as is common when using J) we use the J command line, because that is portable across operating systems (for example: the OS command line is difficult to use on phones).

Task examples:

<lang J> standard rank competitors ┌─┬──┬───────┐ │1│44│Solomon│ ├─┼──┼───────┤ │2│42│Jason │ ├─┼──┼───────┤ │2│42│Errol │ ├─┼──┼───────┤ │4│41│Garry │ ├─┼──┼───────┤ │4│41│Bernard│ ├─┼──┼───────┤ │4│41│Barry │ ├─┼──┼───────┤ │7│39│Stephen│ └─┴──┴───────┘

  modified rank competitors

┌─┬──┬───────┐ │1│44│Solomon│ ├─┼──┼───────┤ │3│42│Jason │ ├─┼──┼───────┤ │3│42│Errol │ ├─┼──┼───────┤ │6│41│Garry │ ├─┼──┼───────┤ │6│41│Bernard│ ├─┼──┼───────┤ │6│41│Barry │ ├─┼──┼───────┤ │7│39│Stephen│ └─┴──┴───────┘

  dense rank competitors

┌─┬──┬───────┐ │1│44│Solomon│ ├─┼──┼───────┤ │2│42│Jason │ ├─┼──┼───────┤ │2│42│Errol │ ├─┼──┼───────┤ │3│41│Garry │ ├─┼──┼───────┤ │3│41│Bernard│ ├─┼──┼───────┤ │3│41│Barry │ ├─┼──┼───────┤ │4│39│Stephen│ └─┴──┴───────┘

  ordinal rank competitors

┌─┬──┬───────┐ │1│44│Solomon│ ├─┼──┼───────┤ │2│42│Jason │ ├─┼──┼───────┤ │3│42│Errol │ ├─┼──┼───────┤ │4│41│Garry │ ├─┼──┼───────┤ │5│41│Bernard│ ├─┼──┼───────┤ │6│41│Barry │ ├─┼──┼───────┤ │7│39│Stephen│ └─┴──┴───────┘

  fractional rank competitors

┌───┬──┬───────┐ │1 │44│Solomon│ ├───┼──┼───────┤ │2.5│42│Jason │ ├───┼──┼───────┤ │2.5│42│Errol │ ├───┼──┼───────┤ │5 │41│Garry │ ├───┼──┼───────┤ │5 │41│Bernard│ ├───┼──┼───────┤ │5 │41│Barry │ ├───┼──┼───────┤ │7 │39│Stephen│ └───┴──┴───────┘</lang>

Java

<lang java>import java.util.*;

public class RankingMethods {

   private enum Tie {
       NO, YES, FIRST, LAST
   }
   private static Tie[] ties;
   final static String[] input = {"44 Solomon", "42 Jason", "42 Errol",
       "41 Garry", "41 Bernard", "41 Barry", "39 Stephen"};
   public static void main(String[] args) {
       int n = input.length;
       int[] scores = new int[n];
       for (int i = 0; i < n; i++)
           scores[i] = new Scanner(input[i]).nextInt();
       ties = new Tie[n];
       Arrays.fill(ties, Tie.NO);
       for (int i = 0; i < n; i++) {
           boolean eqPrev = i > 0 && scores[i] == scores[i - 1];
           boolean eqNext = i < n - 1 && scores[i] == scores[i + 1];
           if (!eqPrev && eqNext)
               ties[i] = Tie.FIRST;
           else if (eqPrev && eqNext)
               ties[i] = Tie.YES;
           else if (eqPrev && !eqNext)
               ties[i] = Tie.LAST;
       }
       standardRanking(n);
       modifiedRanking(n);
       denseRanking(n);
       ordinalRanking(n);
       fractionalRanking(n);
   }
   private static void standardRanking(int len) {
       System.out.println("\nStandard ranking");
       for (int i = 0, rank = 0, ord = 1; i < len; i++, ord++) {
           if (ties[i] == Tie.NO || ties[i] == Tie.FIRST)
               rank = ord;
           System.out.printf("%d %s%n", rank, input[i]);
       }
   }
   private static void modifiedRanking(int len) {
       System.out.println("\nModified ranking");
       for (int i = 0, rank = 0, ord = 0; i < len; i++) {
           if (ties[i] == Tie.NO)
               rank = ++ord;
           else if (ties[i] == Tie.FIRST) {
               while (ties[ord] != Tie.LAST)
                   ord++;
               rank = ++ord;
           }
           System.out.printf("%d %s%n", rank, input[i]);
       }
   }
   private static void denseRanking(int len) {
       System.out.println("\nDense ranking");
       for (int i = 0, rank = 1; i < len; i++) {
           System.out.printf("%d %s%n", rank, input[i]);
           if (ties[i] == Tie.NO || ties[i] == Tie.LAST)
               rank++;
       }
   }
   private static void ordinalRanking(int len) {
       System.out.println("\nOrdinal ranking");
       for (int i = 0; i < len; i++)
           System.out.printf("%d %s%n", i + 1, input[i]);
   }
   private static void fractionalRanking(int len) {
       System.out.println("\nFractional ranking");
       float rank = 0;
       for (int i = 0, ord = 0; i < len; i++) {
           if (ties[i] == Tie.NO)
               rank = ++ord;
           else if (ties[i] == Tie.FIRST) {
               int orig = ord;
               rank = 0;
               do {
                   rank += (++ord);
               } while (ties[ord - 1] != Tie.LAST);
               rank /= (ord - orig);
           }
           System.out.printf("%2.1f %s%n", rank, input[i]);
       }
   }

}</lang>

Standard ranking
1 44 Solomon
2 42 Jason
2 42 Errol
4 41 Garry
4 41 Bernard
4 41 Barry
7 39 Stephen

Modified ranking
1 44 Solomon
3 42 Jason
3 42 Errol
6 41 Garry
6 41 Bernard
6 41 Barry
7 39 Stephen

Dense ranking
1 44 Solomon
2 42 Jason
2 42 Errol
3 41 Garry
3 41 Bernard
3 41 Barry
4 39 Stephen

Ordinal ranking
1 44 Solomon
2 42 Jason
3 42 Errol
4 41 Garry
5 41 Bernard
6 41 Barry
7 39 Stephen

Fractional ranking
1,0 44 Solomon
2,5 42 Jason
2,5 42 Errol
5,0 41 Garry
5,0 41 Bernard
5,0 41 Barry
7,0 39 Stephen

Python

<lang python>def mc_rank(iterable, start=1):

   """Modified competition ranking"""
   lastresult, fifo = None, []
   for n, item in enumerate(iterable, start-1):
       if item[0] == lastresult:
           fifo += [item]
       else:
           while fifo:
               yield n, fifo.pop(0)
           lastresult, fifo = item[0], fifo + [item]
   while fifo:
       yield n+1, fifo.pop(0)


def sc_rank(iterable, start=1):

   """Standard competition ranking"""
   lastresult, lastrank = None, None
   for n, item in enumerate(iterable, start):
       if item[0] == lastresult:
           yield lastrank, item
       else:
           yield n, item
           lastresult, lastrank = item[0], n


def d_rank(iterable, start=1):

   """Dense ranking"""
   lastresult, lastrank = None, start - 1,
   for item in iterable:
       if item[0] == lastresult:
           yield lastrank, item
       else:
           lastresult, lastrank = item[0], lastrank + 1
           yield lastrank, item


def o_rank(iterable, start=1):

   """Ordinal  ranking"""
   yield from enumerate(iterable, start)


def f_rank(iterable, start=1):

   """Fractional ranking"""
   last, fifo = None, []
   for n, item in enumerate(iterable, start):
       if item[0] != last:
           if fifo:
               mean = sum(f[0] for f in fifo) / len(fifo)
               while fifo:
                   yield mean, fifo.pop(0)[1]
       last = item[0]
       fifo.append((n, item))
   if fifo:
       mean = sum(f[0] for f in fifo) / len(fifo)
       while fifo:
           yield mean, fifo.pop(0)[1]


if __name__ == '__main__':

   scores = [(44, 'Solomon'),
             (42, 'Jason'),
             (42, 'Errol'),
             (41, 'Garry'),
             (41, 'Bernard'),
             (41, 'Barry'),
             (39, 'Stephen')]
   print('\nScores to be ranked (best first):')
   for s in scores:
       print('        %2i %s' % (s ))
   for ranker in [sc_rank, mc_rank, d_rank, o_rank, f_rank]:
       print('\n%s:' % ranker.__doc__)
       for rank, score in ranker(scores):
           print('  %3g, %r' % (rank, score))</lang>
Output:
Scores to be ranked (best first):
        44 Solomon
        42 Jason
        42 Errol
        41 Garry
        41 Bernard
        41 Barry
        39 Stephen

Standard competition ranking:
    1, (44, 'Solomon')
    2, (42, 'Jason')
    2, (42, 'Errol')
    4, (41, 'Garry')
    4, (41, 'Bernard')
    4, (41, 'Barry')
    7, (39, 'Stephen')

Modified competition ranking:
    1, (44, 'Solomon')
    3, (42, 'Jason')
    3, (42, 'Errol')
    6, (41, 'Garry')
    6, (41, 'Bernard')
    6, (41, 'Barry')
    7, (39, 'Stephen')

Dense ranking:
    1, (44, 'Solomon')
    2, (42, 'Jason')
    2, (42, 'Errol')
    3, (41, 'Garry')
    3, (41, 'Bernard')
    3, (41, 'Barry')
    4, (39, 'Stephen')

Ordinal  ranking:
    1, (44, 'Solomon')
    2, (42, 'Jason')
    3, (42, 'Errol')
    4, (41, 'Garry')
    5, (41, 'Bernard')
    6, (41, 'Barry')
    7, (39, 'Stephen')

Fractional ranking:
    1, (44, 'Solomon')
  2.5, (42, 'Jason')
  2.5, (42, 'Errol')
    5, (41, 'Garry')
    5, (41, 'Bernard')
    5, (41, 'Barry')
    7, (39, 'Stephen')

Ruby

<lang ruby>ar = "44 Solomon 42 Jason 42 Errol 41 Garry 41 Bernard 41 Barry 39 Stephen".lines.map{|line| line.split}

grouped = ar.group_by{|pair| pair.delete_at(0).to_i} s_rnk = 1 m_rnk = o_rnk = f_rnk = 0 puts "stand.\tmod.\tdense\tord.\tfract."

grouped.each.with_index do |(score, names), i|

 d_rnk = i + 1 
 m_rnk += names.flatten!.size
 f_rnk = (s_rnk + m_rnk)/2.0
 names.each do |name|
   o_rnk += 1
   puts "#{s_rnk}\t#{m_rnk}\t#{d_rnk}\t#{o_rnk}\t#{f_rnk.to_s.gsub(".0","")}\t#{score} #{name}"
 end
 s_rnk += names.size

end</lang>

Output:
stand.	mod.	dense	ord.	fract.
1	1	1	1	1	44 Solomon
2	3	2	2	2.5	42 Jason
2	3	2	3	2.5	42 Errol
4	6	3	4	5	41 Garry
4	6	3	5	5	41 Bernard
4	6	3	6	5	41 Barry
7	7	4	7	7	39 Stephen