Discussion:
Macro do loop with discrete values
(too old to reply)
Susie Li
2006-01-19 16:21:15 UTC
Permalink
Hello,

I have a use to create a macro to read datasets repeatedly. Part of the
input dataset names are designated numbers.

I don't know how to set it up in macro to read such data. In the
following example, I tried to use %do loop to do the job, but it did not
work, because SAS would require me to supply %to.

Please help.


-----

%let days=%str(5 26 69 917 1720 2023 232);
%macro vodp1_daily (dayparts=,outvar=);

%do i=&dayparts; /*dayparts*/
data y.&outvar&i;
set x.events&i;
%end;
run;
%mend%

%vodp1_daily(dayparts=&days,outvar=vodq)

---

Susie Li
TV Guide
1211 Avenue of the Americas
New York, NY 10036
Tel 212.852.7453
Email ***@tvguide.com
Dorfman, Paul
2006-01-19 17:15:37 UTC
Permalink
Susie,

You have to break up the list into tokens before a token canbe used. One
way would be

%local i tkn ;
%do i = 1 %to 999999 ;
%let tkn = %scan (&dayparts, &i) ;
%if %length (&tkn) = 0 %then %goto iend ;
data y.&outvar&tkn;
set x.events&tkn;
%end ;
%iend:

Purists consider suppliying a "large enough number", such as 999999 above
a kludge, preferring the better distilled

%local i tkn ;
%let i = 1 ;
%let tkn = %scan (&dayparts, &i) ;
%do %while (%length (&tkn) ne 0) ;
data y.&outvar&tkn;
set x.events&tkn;
%let i = %eval (&i + 1) ;
%let tkn = %scan (&dayparts, &i) ;
%end ;

but I am not sure at all that it is cleaner. I would rather stick with the
first approach, all the more that in all such cases, even 999999 is an
overkill - no list of this sort will ever contain as many tokens, but if
you are not sure about that, add another 9 <g>.

Kind regards
------------
Paul Dorfman
Jax, FL
------------
Post by Susie Li
Hello,
I have a use to create a macro to read datasets repeatedly. Part of the
input dataset names are designated numbers.
I don't know how to set it up in macro to read such data. In the
following example, I tried to use %do loop to do the job, but it did not
work, because SAS would require me to supply %to.
Please help.
-----
%let days=%str(5 26 69 917 1720 2023 232);
%macro vodp1_daily (dayparts=,outvar=);
%do i=&dayparts; /*dayparts*/
data y.&outvar&i;
set x.events&i;
%end;
run;
%mend%
%vodp1_daily(dayparts=&days,outvar=vodq)
---
Susie Li
TV Guide
1211 Avenue of the Americas
New York, NY 10036
Tel 212.852.7453
Jiann-Shiun Huang
2006-01-19 20:11:11 UTC
Permalink
Susie:

The following code should help. The first macro %countday is used to
determine the number of days in (5 26 69 917 1720 2023 232). Then that
number is passed as a parameter to the second macro
vodp1_daily. Since the total count is available, you should be able to
use (%do %to) to iterate through like the second macro illustrates. The
result from log window is copied at the end.

Let me know if you have any question on this.

%macro countday(A=);
%global i;
%let i=1;
%do %while (%qscan(&A,&i) ne %str());
%let i=%eval(&i+1);
%end;
%let i=%eval(&i-1);
%mend countday;

%macro vodp1_daily(dayparts=,daycount=);

%do i=1 %to &daycount; /*dayparts*/
%put %qscan(&dayparts,&i);
%* data y.&outvar&i;
%* set x.events&i;
%end;
%*run;
%mend vodp1_daily;

%countday(A='5 26 69 917 1720 2023 232')

%vodp1_daily(dayparts=5 26 69 917 1720 2023 232, daycount=&i)


***** Log Output *****

388 options nosymbolgen nomprint nomlogic;
389 %macro countday(A=);
390 %global i;
391 %let i=1;
392 %do %while (%qscan(&A,&i) ne %str());
393 %let i=%eval(&i+1);
394 %end;
395 %let i=%eval(&i-1);
396 %mend countday;
397
398 %macro vodp1_daily(dayparts=,daycount=);
399
400 %do i=1 %to &daycount; /*dayparts*/
401 %put %qscan(&dayparts,&i);
402 %* data y.&outvar&i;
403 %* set x.events&i;
404 %end;
405 %*run;
406 %mend vodp1_daily;
407
408 %countday(A='5 26 69 917 1720 2023 232')
409
410 %vodp1_daily(dayparts=5 26 69 917 1720 2023 232, daycount=&i)
5
26
69
917
1720
2023
232

J S Huang
1-515-557-3987
fax 1-515-557-2422
Hello,

I have a use to create a macro to read datasets repeatedly. Part of
the
input dataset names are designated numbers.

I don't know how to set it up in macro to read such data. In the
following example, I tried to use %do loop to do the job, but it did
not
work, because SAS would require me to supply %to.

Please help.


-----

%let days=%str(5 26 69 917 1720 2023 232);
%macro vodp1_daily (dayparts=,outvar=);

%do i=&dayparts; /*dayparts*/
data y.&outvar&i;
set x.events&i;
%end;
run;
%mend%

%vodp1_daily(dayparts=&days,outvar=vodq)

---

Susie Li
TV Guide
1211 Avenue of the Americas
New York, NY 10036
Tel 212.852.7453
Email ***@tvguide.com
David L Cassell
2006-01-19 20:09:50 UTC
Permalink
Post by Susie Li
I have a use to create a macro to read datasets repeatedly. Part of the
input dataset names are designated numbers.
I don't know how to set it up in macro to read such data. In the
following example, I tried to use %do loop to do the job, but it did not
work, because SAS would require me to supply %to.
I see that SAS-L gurus have already given you their advice. Follow it.

But first think about whether you *need* to do this. If you want to read in
a series of files, there's almost always a better approach than a big
looping macro.
In fact, this has come up about ten times in the past two weeks.

If you are reading in flat files, then seriously consider using the FILEVAR=
option in the data step to read in all your files in a single step. If you
want
to read in a series of Excel worksheets, then try Paul Choate's code using
the libname engine.

HTH,
David
--
David L. Cassell
mathematical statistician
Design Pathways
3115 NW Norwood Pl.
Corvallis OR 97330

_________________________________________________________________
Express yourself instantly with MSN Messenger! Download today - it's FREE!
http://messenger.msn.click-url.com/go/onm00200471ave/direct/01/
Ian Whitlock
2006-01-19 20:11:28 UTC
Permalink
Suzie,

Like Paul I am a bit of a pragmatist harboring a purist's conscience.

It is worth considering whether you have chosen to loop at the correct
level. The code

data y.&outvar&tkn;
set x.events&tkn;

is often a danger sign that your data structure design will require much
macro code to manage a poor design. The problem is that you are creating
data sets Y.&OUTVAR&TKN where information has been buried in the data set
names. Consequently, just as your data sets X.EVENTS&TKN have required you
to make a macro looping over these data sets, your solution will continue
to demand that you write such macros. Of course, the good news is that you
will get much better at writing such macros. The bad news is that might
find it continually necessary to write them, and never realize that the
source of this requirement is a data structure problem.

If the data sets, X.EVENTS&TKN, have the same structure then it probably
would be better to create one data set with all the information stored in
variables. The purist's view of the problem might produce the following
code.

%macro source_names ( pref=x.events, dayparts= , nsourcev=nds) ;
%* return list of data sets with in= options
and set &NSOURCEEV= number of elements
*;
%local i tkn list ;
%do i = 1 %to 999999 ;
%let tkn = %scan (&dayparts, &i) ;
%if %length (&tkn) = 0 %then %goto iend ;
%let list = &list &pref&tkn(in = in&tkn) ;
%end ;
%iend:
%let &NSOURCEV = %eval(&i-1) ;
&list
%mend source_names ;

%macro source ( nv=1 , pref=in) ;
%* return formula for source variable *;
%local list i ;
%do i = 1 %to &nv ;
%let list = &list + &i*in&i ;
%end ;
0 /* + */ &list
%mend source ;

%macro vodp1_daily (dayparts=,outvar=);
%local nds ;
data y.&outvar ( drop = __i ) ;
length day $ 6 var $ 32 ;
retain var "&outvar" ;
set %source_names ( dayparts = &dayparts, nsourcev=nds ) ;
__i = %source ( nv=&nds ) ;
day = scan("&dayparts", __i) ;
run ;
%mend vodp1_daily ;

%vodp1_daily(dayparts=5 26 69 917 1720 2023 232, outvar=vodq)

Clearly the above code is more complex than either you or Paul suggested,
since it contains 3 macros instead of two and with more looping. On the
other hand, the first two macros can be considered the price paid for
conversion to a better structure; they can be easily tested with

%global nds ;
%put %source_names ( dayparts = 5 20 ) ;
%put &nds ;
%put %source ( nv=&nds) ;

and they reveal the true structure of the problem, list management. It is
left to macro programmer to build his own tools for list management
problems, since SAS macro does not provide these tools in a ready to use
form.

However, the pragmatist might argue that there is no need to violently
change the nature of the Paul's code, the above was designed to avoid
concatenation. What does an extra data pass to perform the concatenation
hurt?

%macro vodp1_daily (dayparts=,out=y.vodq);
%local i tkn ;
%do i = 1 %to 999999 ;
%let tkn = %scan (&dayparts, &i) ;
%if %length (&tkn) = 0 %then %goto iend ;
data temp;
length day $ 6 var $ 32 ;
retain var "%scan(&out,.)" ;
set x.events&tkn;
day = "&tkn" ;
run ;

proc append base = &out data = temp ;
run ;
%end ;
%iend:
%mend vodp1_daily ;

%vodp1_daily(dayparts=5 26 69 917 1720 2023 232, out=y.vodq)

In any case, I note that missing RUN statement in your code, later adopted
by Paul, is an error that may or may not bite as you continue to develop
your program, since it depends on the precise nature of the remaining code
and whether it is run interactively or in batch mode.

Well at least you have answers to your problem and things to think about
when confronting them. It probably will not matter much whether you adopt
the purist's solution, the pragmatist's solution or go with the many data
sets solution for this problem. The pragmatist can argue, get the job
done; and the purist can argue that it is worth preparing for the job's
where efficiency techniques will really matter; but it is the tension
between these two points of view that will make you a better programmer.

Ian Whitlock
================
Date: Thu, 19 Jan 2006 12:15:37 -0500
Reply-To: "Dorfman, Paul" <***@FCSO.COM>
Sender: "SAS(r) Discussion"
From: "Dorfman, Paul" <***@FCSO.COM>
Subject: Re: Macro do loop with discrete values
Susie,

You have to break up the list into tokens before a token canbe used. One
way would be

%local i tkn ;
%do i = 1 %to 999999 ;
%let tkn = %scan (&dayparts, &i) ;
%if %length (&tkn) = 0 %then %goto iend ;
data y.&outvar&tkn;
set x.events&tkn;
%end ;
%iend:

Purists consider suppliying a "large enough number", such as 999999 above a
kludge, preferring the better distilled

%local i tkn ;
%let i = 1 ;
%let tkn = %scan (&dayparts, &i) ;
%do %while (%length (&tkn) ne 0) ;
data y.&outvar&tkn;
set x.events&tkn;
%let i = %eval (&i + 1) ;
%let tkn = %scan (&dayparts, &i) ;
%end ;

but I am not sure at all that it is cleaner. I would rather stick with the
first approach, all the more that in all such cases, even 999999 is an
overkill - no list of this sort will ever contain as many tokens, but if
you are not sure about that, add another 9 <g>.

Kind regards
------------
Paul Dorfman
Jax, FL
------------
Post by Susie Li
Hello,
I have a use to create a macro to read datasets repeatedly. Part of the
input dataset names are designated numbers.
I don't know how to set it up in macro to read such data. In the
following example, I tried to use %do loop to do the job, but it did not
work, because SAS would require me to supply %to.
Please help.
-----
%let days=%str(5 26 69 917 1720 2023 232);
%macro vodp1_daily (dayparts=,outvar=);
%do i=&dayparts; /*dayparts*/
data y.&outvar&i;
set x.events&i;
%end;
run;
%mend%
%vodp1_daily(dayparts=&days,outvar=vodq)
---
Susie Li
TV Guide
1211 Avenue of the Americas
New York, NY 10036
Tel 212.852.7453
Loading...