LISTSERV at the University of Georgia
Menubar Imagemap
Home Browse Manage Request Manuals Register
Previous (more recent) messageNext (less recent) messagePrevious (more recent) in topicNext (less recent) in topicPrevious (more recent) by same authorNext (less recent) by same authorPrevious page (October 2003, week 4)Back to main SAS-L pageJoin or leave SAS-L (or change settings)ReplyPost a new messageSearchProportional fontNon-proportional font
Date:         Mon, 27 Oct 2003 12:18:24 -0500
Reply-To:     Ian Whitlock <WHITLOI1@WESTAT.COM>
Sender:       "SAS(r) Discussion" <SAS-L@LISTSERV.UGA.EDU>
From:         Ian Whitlock <WHITLOI1@WESTAT.COM>
Subject:      Re: Yet another "rename" query
Comments: To: AxN <axn00@HOTMAIL.COM>
Content-Type: text/plain

Subject: RE: Yet another "rename" query Summary: A lesson in macro writing, particularly in identifying the service to be performed Respondent: IanWhitlock@westat.com

AxN,

First, what is the problem? I ran your code with MPRINT and see in the log in part:

NOTE: Line generated by the invoked macro "MYMACRO". 3 do jj = 1 to dim(old) ; rename old(jj) = new(jj) ; end ; - 22 76 this makes it pretty clear that the statement,

rename old(jj) = new(jj) ;

is illegal. That is why your code does not execute correctly.

Since you indicate that you want to look at the task and see how to do it with macro. Let's take a look. First, make a clear statement of the task:

Given a list of variables, rename them by prefixing the old names with X_.

One important thing about the macro is getting the task correct. Note that my version of the task does not mention any datasets, and none are needed under the assumption that the macro will be used in an appropriate context. By bringing in the datasets, you have turned the problem from making a useful tool that may be widely applicable to the management of datasets which is rather clumsy. In general, any extraneous material added to the macro will restrict its use.

Now let's look an example task. I have

old1 anotherold moreold

I want to change the names to

X_old1 X_anotherold X_moreold

In SAS this requires the following as a part of a RENAME statement or option.

old1=X_old1 anotherold=X_anotherold moreold=X_moreold

So at a minimum we have

%macro rename ( vars= , prefix= ) ;

Not the name helps the reader understand the consuming code. Keyword parameters are used so that future extensions will be easy. In fact, some people might what a suffix instead of a prefix. So let's go with

%macro rename ( vars= , prefix= , suffix= ) ;

Now I added something, not in the original task, but that is to make the macro more usable, the extension is not restrict how the macro will be used, rather it is enhancing the usage or improving the task definition. You had a large part of the code correct so let's adopt what you have.

%macro rename ( vars= , prefix= , suffix= ) ; %local word /* to grab one variable at a time */ count /* looping index */ ; %let count = 1 ; %let word = %scan(&vars, &count) ; %do %while (%length(&word) > 0 ) ; &word=&prefix&word&suffix %let count = %eval(&count + 1) ; %let word = %scan(&vars, &count) ; %end ;

%mend rename ;

Note that I changed the while test in the loop. In this form there is no need for quoting so I also replaced %QSCAN with %SCAN.

Test the macro

%put %rename ( vars = old1 old2 old3 , prefix = X_ ) ;

We get

old1=X_old1 old2=X_old2 old3=X_old3

on the log. So the macro is doing its assigned task. So now let's apply it to a problem.

data first ; old1 = 1 ; old2 = 2*old1 + rannor(20031023) ; old3 = 3*old2 + rannor(20031024) ; old4 = 0 ; run ;

data second ; set first ; rename %rename ( vars = old1 old2 old3 , prefix = X_ ) ; run ;

A quick check of the variables shows that everything is working as expected. Note that I required the consumer to type a couple of extra lines of code. However, these lines make the code clearer. Could we use the new macro to solve the task you worked on? Sure.

%macro mymacro(indata,outdata,myvar) ; data &outdata ; set &indata ; rename %rename ( vars = &myvar , prefix = X1_ ) ; %mend mymacro ;

%mymacro(first, second, old1 old2 old3)

Here I used your positional parameters, because we are just playing around now. What does this latest macro save? Well it saves the consumer from typing the words DATA SET RENAME RUN and the semicolons for the corresponding statements. In short it does so little that it hard to see it as performing a useful task, and the call obscures what that task is.

Suppose I wanted to do the renaming on input? No problem with %RENAME.

data second ; set first ( rename = (%rename (vars=old1 old2 old3, prefix=X1)) ) ; run ;

Suppose that FIRST is so big that it would be a shame to pass the data, so I want to use PROC DATASETS? No problem with %RENAME.

proc datasets lib = work ; modify first ; rename %rename (vars=old1 old2 old3, prefix=X1) ; run ; quit ;

All of this should be an indication that we have got the right level of work for the task and that the macro will prove handy in many places.

Now let's return to the quoting problem. I removed it from considration by changing the %WHILE test because the code wasn't working. Before that I had

%macro rename ( vars= , prefix= , suffix= ) ; %local word /* to grab one variable at a time */ count /* looping index */ ; %let count = 1 ; %let word = %qscan(&vars, &count) ; %do %while (&word ne %str() ) ; &word=&prefix&word&suffix %let count = %eval(&count + 1) ; %let word = %qscan(&vars, &count) ; %end ;

%mend rename ;

Here

data first ; old1 = 1 ; old2 = 2*old1 + rannor(20031023) ; old3 = 3*old2 + rannor(20031024) ; old4 = 0 ; run ;

data second ; set first ; rename %rename ( vars = old1 old2 old3 , prefix = X1_ ) ; run ;

produced a flood of error messages that I couldn't understand. However, the problem was easily fixed by changing the line

&word=&prefix&word&suffix

to

%unquote(&word=&prefix&word&suffix)

This shows that something, I don't know what, was causing the generated code to remain quoted in the SAS compiler. It is one of those bug problems where macro quoting should be removed on the way to the SAS compiler, but sometimes it isn't. It just shows that it wise to know why you are macro quoting something and wise to know ways to avoid doing so, unless you are prepared to sprinkle %UNQUOTE in the appropriate places.

I hope this has helped you and others to see better how to develop macros.

IanWhitlock@westat.com -----Original Message----- From: AxN [mailto:axn00@HOTMAIL.COM] Sent: Monday, October 27, 2003 1:11 AM To: SAS-L@LISTSERV.UGA.EDU Subject: Yet another "rename" query

Hello everyone,

I have been looking through the archives for useful tips, but I don't seem to be able to adapt all the great information I found in my own problem.

I am trying to rename some varaibles in a data set, by adding a prefix (x_ in the example below). Since I seemed to be doing this somewhat frequently, and since there was no underlying pattern, I though that this would be a good way to learn macros, and wrote the following macro and example program. SAS (Version 8.12 on UNIX, I run these in batch mode) complains about the do loop, I think - I can't make out exactly where the problem lies, and would appreciate any suggestions.

AxN ======================================= %macro mymacro(indata,outdata,myvar) ; %let newvar = ; %let count = 1 ; %let word = %qscan(&myvar, &count) ; %let newword = %str(x_&word) ; %let newvar = %str(&newword) ; %let count = %eval(&count + 1) ; %put word = "&word", newword = "&newword", newvar = "&newvar", count = "&count" ; %do %while (&word ne %str()) ; %let word = %qscan(&myvar, &count) ; %if &word ne %str() %then %do ; %let newword = %str(x_&word) ; %let newvar = %str(&newvar &newword) ; %let count = %eval(&count + 1) ; %put word = "&word", newword = "&newword", newvar = "&newvar", count = "&count" ; %end ; %end ; %global mycount ; %let mycount = &count ; %put myvar = "&myvar", newvar = "&newvar", count = "&count", mycount = "&mycount" ;

data &outdata(drop=jj) ; set &indata ; array old(*) &myvar ; array new(*) &newvar ; %put myvar = "&myvar", newvar = "&newvar" ; do jj = 1 to dim(old) ; rename old(jj) = new(jj) ; end ; run ;

%mend mymacro ;

data first ; old1 = 1 ; old2 = 2*old1 + rannor(20031023) ; old3 = 3*old2 + rannor(20031024) ; old4 = 0 ; run ;

%mymacro(first, second, old1 old2 old3)

proc contents data=first ; run ;

proc contents data=second ; run ;


Back to: Top of message | Previous page | Main SAS-L page