BookmarkSubscribeRSS Feed
stevo642
Obsidian | Level 7

Is there a technique that would allow ranking each row of a matrix without looping?

 

So the returned matrix would have within-row ranks.

 

For example if A held all non-missing numeric data, then imaginary function rowRank(A) would return values 1:ncol(A) in each row.

 



 

10 REPLIES 10
Reeza
Super User
Show an example with data please. Sounds like you want to rank each row of data.
stevo642
Obsidian | Level 7

 

a = {1 3 10, 6 9 5};  
print a ,,
(rank(a))[l="Ranks from rank(a)"] ,,
({1 2 3, 2 3 1})[l="Desired Ranks (row-wise ranks)"];

 

A

1 3 10
6 9 5

 

Ranks from rank(a)

1 2 6
4 5 3

 

Desired Ranks (row-wise ranks)

1 2 3
2 3 1

IanWakeling
Barite | Level 11

I think this can be done with a two pass solution.  The initial rank matrix needs to be adjusted so that each row has values that are larger than the row before, then the second ranking is operating within row.  For example:

proc iml;
  a = {1 3 10, 6 9 5};
  print a;

  b = row(a) - 1;
  r = rank( rank(a) + nrow(a) # ncol(a) # b ) - ncol(a) # b;
  print r;
quit;
KachiM
Rhodochrosite | Level 12

@stevo642 

 

The ranks given to second row seems to be wrong. I think it has to be 3 1 2. I have less expertise in IML. Do you accept a Data Step solution?

The steps include the sorting of each row by using SORTN() function and at the same time switching the index of the array. Then re-ordered index will be the rank. SMALLEST() takes care of the sorting in the background and returns

the i-th smallest value. Using WHICHN() with the i-th smallest value gives the desired Rank.

 

Note, no attempt is made to check for ties.

 


%let vn = 3;
data rank;
   set A;
   array x x1-x&vn;
   array R R1 - R&vn;
   do i = 1 to dim(x);
      R[i] = whichN(smallest(i, of x[*]), of x[*]);
      put R[i] =;
   end;
keep R:;
run;
stevo642
Obsidian | Level 7

Great ideas -- thank you. The "r2" array in DO loop below is what I have been using.

 

proc iml;

a = {2 4 6, 15 11 13, 29 28 27};

r1 = rank(a);

r2 = j(nrow(a), ncol(a), .);

do i = 1 to nrow(a);
  r2[i,] = rank(a[i,]);
end;

print a ,, r1[l="Rank(a) - whole matrix - don't want"] ,, r2[l="Desired 'Row Ranks' from looping rank(row-by-row)"];

quit;
Rick_SAS
SAS Super FREQ

Ian is a master at these manipulation tricks!

 

I think you can make a small improvement to his idea to make it more efficient. This computation requires only a translation, a rank, and another translation.

proc iml;
a = {2 4 6, 
     15 11 13, 
     29 28 27,
     2 4 6, 
     4 1 6, 
     2 6 4, 
     6 4 2 };

maxDiff = range(a) + 1;  /* largest difference between elements */
b = a + maxDiff*row(a);  /* uniquify each row */
r = rank(b) - ncol(a)* T(0:(nrow(a)-1));
print r;
IanWakeling
Barite | Level 11

I like your modification Rick, surely the way to go if the OP is looking for an IML solution.

hashman
Ammonite | Level 13

@stevo642 :

If your matrix is in the form of a SAS data set, it is a simple 3 step process and 7 statements (including 3 RUN statements):

 

1. Proc TRANSPOSE.

2. Proc RANK.

3. Proc TRANSPOSE.

 

Kind regards

Paul D.

hashman
Ammonite | Level 13

@stevo642:

Had no SAS access on the first response, but now that I do and can test, consider:

data have ;                                                                                                                                                                                                                                                     
  input v1-v3 ;                                                                                                                                                                                                                                                 
  cards ;                                                                                                                                                                                                                                                       
6 9  5                                                                                                                                                                                                                                                          
1 3 10                                                                                                                                                                                                                                                          
0 0  0                                                                                                                                                                                                                                                          
0 0  1                                                                                                                                                                                                                                                          
0 1  1                                                                                                                                                                                                                                                          
;                                                                                                                                                                                                                                                               
run ;                                                                                                                                                                                                                                                           
                                                                                                                                                                                                                                                                
proc transpose data = have out = t (drop = _:) ;                                                                                                                                                                                                                
run ;                                                                                                                                                                                                                                                           
proc rank data = t out = r ;                                                                                                                                                                                                                                    
  var col: ;                                                                                                                                                                                                                                                    
run ;                                                                                                                                                                                                                                                           
proc transpose data = r out = want (drop=_:) prefix=v ;                                                                                                                                                                                                         
run ;                                                 

Result:

 v1     v2     v3
-----------------
2.0    3.0    1.0
1.0    2.0    3.0
2.0    2.0    2.0
1.5    1.5    3.0
1.0    2.5    2.5

Plus, you have all the bells and whistles of proc RANK to handle the ties differently, should you wish.

 

Kind regards

Paul D. 

Rick_SAS
SAS Super FREQ

If the OP wants to control how to rank tied values, the RANKTIE function in SAS/IML supports exactly the same methods as PROC RANK.

sas-innovate-2024.png

Available on demand!

Missed SAS Innovate Las Vegas? Watch all the action for free! View the keynotes, general sessions and 22 breakouts on demand.

 

Register now!

Multiple Linear Regression in SAS

Learn how to run multiple linear regression models with and without interactions, presented by SAS user Alex Chaplin.

Find more tutorials on the SAS Users YouTube channel.

From The DO Loop
Want more? Visit our blog for more articles like these.
Discussion stats
  • 10 replies
  • 1473 views
  • 14 likes
  • 6 in conversation