# Ranking a vector containing ties

How can I rank a vector such that the ties are replaced by their middle ranks. For example, {1, 2, 2, 3}. I want to rank this vector but the ties must be replaced by their mid-rank; i.e., given {1, 2, 2, 3}, I want to get the vector {1, 2.5, 2.5, 4}. Does there exist any fast command or function to do this?

-

list = {1, 2, 2, 3};

(Ordering@Ordering@# + Reverse@Ordering@Ordering@Reverse@#)/2 &@list


{1, 5/2, 5/2, 4}

As requested, here as a function:

rank[list_] := (Ordering@Ordering@list + Reverse@Ordering@Ordering@Reverse@list)/2

-
Nice, ~4x more faster than mine! – István Zachar Sep 28 '13 at 11:57
@IstvánZachar Thanks! Indeed, Ordering is a much underestimated function with lots of hidden power. It is very useful for fast, partial sorts, for instance. – Sjoerd C. de Vries Sep 28 '13 at 12:04
@ S.C. de Vries: Can you increase its speed by putting in the form of proper function named Rank[x_]:= ? – Abdul Haq Sep 28 '13 at 12:07
@AbdulHaq I've added a function definition. As far as I could see there is so speed increase (not that I expected it). – Sjoerd C. de Vries Sep 28 '13 at 12:16
That's pretty darn clever. +1 – Mr.Wizard Sep 28 '13 at 14:04

Both methods assume continuous sublists of identical elements, i.e., an already sorted vector. Gather is ~10x faster than Union for larger lists.

x = {1, 2, 2, 3};
f1[x_] := x /. (# -> Mean @ Flatten@Position[x, #] & /@ Union@x);
f2[x_] :=
Module[{i = 1}, x /. ((First @ # -> (i + (i = i + Length @ #) - 1)/2) & /@ Gather@x)];

{f1@x, f2@x}

 {{1, 5/2, 5/2, 4}, {1, 5/2, 5/2, 4}}

y = Sort@RandomInteger[{0, 100}, {100000}];
AbsoluteTiming[r1 = f1@y;]
AbsoluteTiming[r2 = f2@y;]
r1 === r2

{2.090404, Null}
{0.327601, Null}
True

-
@ István Zachar: Indeed both functions are acceptable depending on time constraints. I really appreciate the effort. – Abdul Haq Sep 28 '13 at 12:10