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$
0

3 Answers 3

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$
5
  • $\begingroup$ Nice, ~4x more faster than mine! $\endgroup$ Commented Sep 28, 2013 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$ Commented Sep 28, 2013 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
    Commented Sep 28, 2013 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$ Commented Sep 28, 2013 at 12:16
  • $\begingroup$ That's pretty darn clever. +1 $\endgroup$
    – Mr.Wizard
    Commented Sep 28, 2013 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$
1
  • $\begingroup$ @ István Zachar: Indeed both functions are acceptable depending on time constraints. I really appreciate the effort. $\endgroup$
    – Abdul Haq
    Commented Sep 28, 2013 at 12:10
2
$\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 and acknowledge you have read our privacy policy.

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