BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
IanWakeling
Barite | Level 11

This should be simple, but I just can't see it.

What is the best way to replicate the functionality of the data-step RANK function from within IML?

Using syntax such as rank('G') results in the error, "Character operation not implemented yet", which I presume is referring to the IML RANK function not being able to handle a character matrix.

Thanks,

Ian.

1 ACCEPTED SOLUTION

Accepted Solutions
ArinC
SAS Employee

Another way to do it would be to use the pib1. format. 

 n = inputn("A","pib1.");

 

 

View solution in original post

11 REPLIES 11
Scott_Mitchell
Quartz | Level 8

Hi Ian,

You  might find the following link interesting.

SAS/IML(R) 9.22 User's Guide

Specifically the section referring to the following code.

/* Create RANK-like functionality for character matrices */

start rankc(x);

  s = unique(x); /* the unique function returns a sorted list */

  idx = j(nrow(x), ncol(x));

  ctr = 1; /* there can be duplicate values in x */

  do i = 1 to ncol(s); /* for each unique value */

  t = loc(x = s);

  nDups = ncol(t);

  idx = ctr : ctr+nDups-1;

  ctr = ctr + nDups;

  end;

  return (idx);

finish;

/* call the RANKC module */

x = {every good boy does fine and good and well every day};

rc = rankc(x);

print rc[colnam=x];

/* Notice that ranking is in ASCII order, in which capital

  letters precede lower case letters. To get case-insensitive

  behavior, transform the matrix before comparison */

x = {"a" "b" "X" "Y" };

asciiOrder = rankc(x);

alphaOrder = rankc(upcase(x));

print x, asciiOrder, alphaOrder;

IanWakeling
Barite | Level 11

Hi Scott,

Thanks, but I am not looking to get the rank order of all the elements in a character matrix.  In a data-step rank('G') returns  71, the character code for the letter G, I am looking for the equivalent of that in IML.

Rick_SAS
SAS Super FREQ

You title suggests that you want to find the ascii code. Try this:

ascii = byte(65:122);

label = "A65":"A122";

print ascii[colname=label];

IanWakeling
Barite | Level 11

Thanks Rick,

I have gone with

start char_rank(c);

  ascii = byte(65:122);

  return( loc( ascii = c) + 64);

finish;

it will work for what I have in mind as I know c will always be a single letter.  I was kind of hoping that since BYTE was available, then there should be a way to get the inverse function.

Ian.

Rick_SAS
SAS Super FREQ

Sounds good. Here's a cute idea that falls into the "isn't that interesting" category. It maps each alphabetical value (no punctuation) to the ascii code for the uppercase version of the letter:

asciiVals = 65:90;

letters = byte(asciiVals);

mattrib asciiVals colname=letters;

str = {I A N W A K E L I N G};

b = asciiVals[,str];

print b;

Probably not useful for your application, but fun. For more on the MATTRIB function, see Access rows or columns of a matrix by names - The DO Loop

IanWakeling
Barite | Level 11

That's a very nice and a unintended use of mattrib.  Unfortunately my application has lower case letters as well otherwise I would use it.

The character data that I want to convert comes in blocks of letters without delimiters and I find that I need to chop the blocks into individual characters as you have done with my name above.   There is no need to loop over each character in the string, as I have found that I can do this as follows:

start chop(s);
  call execute( cat( 'chopstr = {', prxchange( "s/(.)/'$1' /", -1, s), '};') );
  return(chopstr);
finish;

str=chop('IanWakeling');
print str;

By the way, it is not possible to use a call execute to return from a function module - I tried!

Rick_SAS
SAS Super FREQ

If you have SAS/IML 12.1, you can use a new feature of the SUBSTR function:

start chop(s);

  return ( substr(s, 1:nleng(s), 1) );

finish;

IanWakeling
Barite | Level 11

That's a lot nicer, I shall have to look into upgrading.

Rick_SAS
SAS Super FREQ

Here's one you can use without upgrading:

start chop(s);

   return( cshape(s,1,nleng(s),1) );

finish;

IanWakeling
Barite | Level 11

Thanks Rick, that's cool.   I never looked at cshape before.  The assumption would be that the functionality is similar to the regular shape function, but the size parameter makes it more powerful as you have shown.

ArinC
SAS Employee

Another way to do it would be to use the pib1. format. 

 n = inputn("A","pib1.");

 

 

sas-innovate-2024.png

Join us for SAS Innovate April 16-19 at the Aria in Las Vegas. Bring the team and save big with our group pricing for a limited time only.

Pre-conference courses and tutorials are filling up fast and are always a sellout. Register today to reserve your seat.

 

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
  • 11 replies
  • 2404 views
  • 2 likes
  • 4 in conversation