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
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 ;