9
$\begingroup$

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?

$\endgroup$
12
$\begingroup$
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
$\endgroup$
  • $\begingroup$ Nice, ~4x more faster than mine! $\endgroup$ – István Zachar Sep 28 '13 at 11:57
  • $\begingroup$ @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. $\endgroup$ – Sjoerd C. de Vries Sep 28 '13 at 12:04
  • $\begingroup$ @ S.C. de Vries: Can you increase its speed by putting in the form of proper function named Rank[x_]:= ? $\endgroup$ – Abdul Haq Sep 28 '13 at 12:07
  • 1
    $\begingroup$ @AbdulHaq I've added a function definition. As far as I could see there is so speed increase (not that I expected it). $\endgroup$ – Sjoerd C. de Vries Sep 28 '13 at 12:16
  • $\begingroup$ That's pretty darn clever. +1 $\endgroup$ – Mr.Wizard Sep 28 '13 at 14:04
6
$\begingroup$

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
$\endgroup$
  • $\begingroup$ @ István Zachar: Indeed both functions are acceptable depending on time constraints. I really appreciate the effort. $\endgroup$ – Abdul Haq Sep 28 '13 at 12:10
1
$\begingroup$
Statistics`Library`GetDataRankings[{1, 2, 2, 3}]

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

This is slower than Istvan's f2 but faster than his f1. @Sjoerd's rank is the fastest of the four. With

gdr = Statistics`Library`GetDataRankings

and using Istvan's setup for timings:

y = Sort@RandomInteger[{0, 100}, {1000000}];
t1 = First@AbsoluteTiming[r1 = f1 @ y;]; (* istvan's answer *)
t2 = First@AbsoluteTiming[r2 = f2 @ y;]; (* istvan's answer *)
t3 = First@AbsoluteTiming[r3 = gdr @ y;];
t4 = First@AbsoluteTiming[r4 = rank@y;]; (* sjoerd's answer *)

r1 === r2 === r3 === r4

True

Grid[Transpose@SortBy[Transpose[{{"f1", "f2", "gdr", "rank"}, {t1, t2, t3, t4}}], Last], 
   Dividers -> All]

$\begin{array}{|c|c|c|c|} \hline \text{rank} & \text{f2} & \text{gdr} & \text{f1} \\ \hline 0.687670 & 1.440178 & 3.297210 & 8.324320 \\ \hline \end{array}$

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.