12
SeedRandom[417]
list1 = RandomChoice[Range[22, 28], 4]

{22, 24, 26, 23}

We can get the rank list

Position[Sort[list1], #] & /@ list1 // Flatten

{1, 3, 4, 2}

But when the list have a duplicated element,this method doesn't work,Such as

SeedRandom[416]
list2=RandomChoice[Range[22,28],4]

{28, 24, 25, 24}

A list {3,1,2,1} be expected,Any advice can be gived?

yode
  • 26,686
  • 4
  • 62
  • 167
  • 1
    Ordering@Ordering[list]? –  Apr 14 '16 at 16:16
  • @Xavier Wonderful solution for list1.But list2? – yode Apr 14 '16 at 16:18
  • Something like that: position[list_List] := Module[{n = 1, f}, f[x_] := f[x] = n++; f /@ Sort[list]; f /@ list]? –  Apr 14 '16 at 16:22
  • @Xavier As you prompt,We can do it like this.Position[Union[list2],#]&/@list2//Flatten – yode Apr 14 '16 at 16:26
  • Nice way too. In case performance matters, you might prefer position for large lists. –  Apr 14 '16 at 16:32
  • Sure, I'll post it. You could add a performance request in your question along with the performance-tuning tag if you are indeed interested in this aspect. If you do so, please do not accept my answer right away, others may come with faster solutions. –  Apr 14 '16 at 16:38
  • @TomD Wow,Thanks for your link.:) – yode Apr 16 '16 at 09:50
  • @yoda. Why do you not expect {4,1,3,1} for the rank list of list2? This may be obtained by # /. Thread[# -> Ordering@Ordering@#] &@list2, which also gives the expected answer for list1 (A variant on this answer). – user1066 Apr 16 '16 at 10:27
  • # /. Thread[(# -> Ordering@Ordering@#) & @ DeleteDuplicates@#] & /@ {list1, list2} gives {{1, 3, 4, 2}, {3, 1, 2, 1}} – user1066 Apr 16 '16 at 10:29
  • @TomD So concise.I think you'd better post that as an answer.:) – yode Apr 16 '16 at 11:07

4 Answers4

13

Assuming your lists are positive integers as in OP, this should be quite fast, particularly if duplication is high. Fiddle with using Union vs Sort@DeleteDuplicates - each will have an advantage depending on data.

Normal[SparseArray[
   With[{us = Sort@DeleteDuplicates@#}, us -> Range@Length@us]][[#]]]&@yourlist

For any kind of data, this s/b quick also:

Replace[#, PositionIndex[Sort@DeleteDuplicates@#][[All, 1]], 1] &@yourlist

or probably a bit quicker yet for general data:

Replace[#, 
   With[{s = Sort@DeleteDuplicates@#}, 
    AssociationThread[s, Range@Length@s]], 1] &@yourlist
ciao
  • 25,774
  • 2
  • 58
  • 139
10

A possible approach:

position[list_List] := Module[{n = 1, f}, 
   f[x_] := f[x] = n++; 
   f /@ Union[list]; 
   f /@ list
];

Examples:

SeedRandom[417];
list1 = RandomChoice[Range[22, 28], 4];

position[list1]
(* {1, 3, 4, 2} *)

SeedRandom[416];
list2 = RandomChoice[Range[22, 28], 4];

position[list2]
(* {3, 1, 2, 1} *)

A few timings:

SeedRandom[416];
list3 = RandomChoice[Range[22, 28], 10^6];

position[list3]; // AbsoluteTiming
(* {0.672818, Null} *)

SeedRandom[417];
list4 = RandomChoice[Range[10^5], 10^5];

position[list4]; // AbsoluteTiming
(* {0.379642, Null} *)
10

A replacement-Rule-based approach:

rankList[lst_List] := lst /. Dispatch[Thread[# -> Range@Length@#] &@ Union@lst]

It's a little bit of speed-up over Xavier's solution (position):

SeedRandom[416];
list3 = RandomChoice[Range[22, 28], 10^6];
rankList[list3]; // AbsoluteTiming // First
position[list3]; // AbsoluteTiming // First
rankList[list3] === position[list3]
(* 0.287793 *)
(* 0.584785 *)
(* True *)

SeedRandom[417];
list4 = RandomChoice[Range[10^5], 10^5];
rankList[list4]; // AbsoluteTiming // First
position[list4]; // AbsoluteTiming // First
rankList[list4] === position[list4]
(* 0.108716 *)
(* 0.281810 *)
(* True *)

Note: Dispatch is absolutely crucial here. Without it, the second example runs for a long time. Alternatively, use Associations (suggested by Xavier in a comment), which serve the same purpose:

rankList1[lst_List] := lst /. (AssociationThread[# -> Range@Length@#] &@Union@lst)
march
  • 23,399
  • 2
  • 44
  • 100
  • 1
    A slightly faster solution: rankList1[lst_List] := lst /. (AssociationThread[# -> Range@Length@#] &@Union@lst). –  Apr 14 '16 at 19:17
  • @Xavier. Of course! Somehow, this didn't occur to me. – march Apr 14 '16 at 19:56
3

Not sure if this simplification helps:

ordinalScale[list_] := AssociationThread[Sort@list, Range@Length@list] /@ list
Dunlop
  • 4,023
  • 5
  • 26
  • 41
  • Hello and welcome to Mathematica S.E. Please edit your answer, select the code you shared and using the options that you have on top set it in a code environment. Alternatively, you can select the code and then do Command and K – bmf Jan 17 '23 at 07:40