Array, do loop and Marco

Accepted Solution Solved
Reply
Occasional Contributor
Posts: 14
Accepted Solution

Array, do loop and Marco

Dear SAS experts,

I am stuck on this:

data _null_;

format i best32. ;

do i= 1 to 3 ;

%tamper(sim=i);

end;

run;

(partial code of tamper)

%macro tamper(sim=);

.....(a whole bunch of code having &sim  ...)

data  INdata;

set OUTdata;

array SurA{3} SurA1 - SurA3;

SurA{&sim} = (...some computation involving SurA{&sim} ... )

%mend;

My Log says:

Variable i is uninitialized

Array subscript out of range at line 19 column 14.

Million Thanks!!!

PS: How come I cannot do copy/cut and paste within this page (end up typing every line....) ?

- Matt


Accepted Solutions
Solution
‎08-13-2013 12:33 PM
Super User
Posts: 5,516

Re: Array, do loop and Marco

Matt,

It looks like you are trying to accomplish something like this:

%macro loopy (n_loops);

   %local i;

   %do i=1 %to &n_loops;

      %tamper (sim=&i)

   %end;

%mend loopy;

%loopy (3)

Good luck.

View solution in original post


All Replies
Solution
‎08-13-2013 12:33 PM
Super User
Posts: 5,516

Re: Array, do loop and Marco

Matt,

It looks like you are trying to accomplish something like this:

%macro loopy (n_loops);

   %local i;

   %do i=1 %to &n_loops;

      %tamper (sim=&i)

   %end;

%mend loopy;

%loopy (3)

Good luck.

Trusted Advisor
Posts: 1,931

Re: Array, do loop and Marco

In addition to the above answer, you cannot have a data step _null_, in which a macro is embedded, and in the macro are other data steps.

Super Contributor
Posts: 339

Re: Array, do loop and Marco

Astounding's response should do your trick unless you need some input or output for %tamper within a data step more complicated than the loop you've examplified.

Here's a general explanation as to why you are getting this error:

Sas runs in 2 phases, compilation and execution. Macros (and thus the %tamper(sim=i) in your data step) are resolved at compilation phase and thus before your data step even attempts to do the loop. As such, at the moment that %tamper(sim=i) is resolved i doesn't have any value it is just text i. So inside your macro, each time you have &sim it resolves to i instead of 1-3 leading to the array substcript error.

In order to run a macro in a datastep during execution phase, would've had to use something like

call execute("%tamper(sim="||i||")");

Just like Astounding however, I tend to favor macro loops when I don't need a data step based variable to control my loop or the values passed so I would prefer a macro solution like the one he proposed which is all resolved at compilation phase.

Hope this helps.

Vince

Occasional Contributor
Posts: 14

Re: Array, do loop and Marco

Posted in reply to Vince28_Statcan

It is working ! Thanks.

But now in my array SurA{sim}, where sim=1 to 3, only SurA{3} has values, the other two are missing?

Did I not output??

Also, what is the concept of Astounding's code that there is % in front of the lines in Loopy?

I got rid of it and tried, but it does not work. (Just want to learn more)

Thanks!

- Matt

Super Contributor
Posts: 339

Re: Array, do loop and Marco

I'm not entirely sure to understand your problem.

Inside your %tamper macro, you've defined array{3} as static dimension so all variables are read from the dataset. However, only one of the columns in the resulting INdata dataset are altered each time you call the macro %tamper(sim=i) and it is the column identified by i. The reason your final dataset INdata only has the 3rd column filled, is because when you get to the last step of the loop, you use the statement set OUTdata which has not been modified/updated to include sim1 and sim2 so the array creates 2 variables that are missing at each data step iteration and thus empty in the final dataset. You have 2 ways around this

data  OUTdata;

set OUTdata;

array SurA{3} SurA1 - SurA3;

SurA{&sim} = (...some computation involving SurA{&sim} ... )

Reuse the same dataset and thus only overwrite the necessary column from each call of your macro - OR move the macro iterations that Astounding has provided inside your original macro

%macro tamper(sim=);

.....(a whole bunch of code having &sim  ...)

data  INdata;

set OUTdata;

array SurA{3} SurA1 - SurA3;

%do i=1 %to ∼

SurA{&sim} = (...some computation involving SurA{&sim} ... )

%end;

%mend;

%tamper(sim=3);

By doing this, you are effectively using the macro facility for it's most basic purpose: "Text parsing". What this change does is basically write the expanded data step syntax for you at compilation and then executes the code that would've taken you much longer to hard type by replacing each occurence appropriately. However, last but not least, it occurs to me that your entire code could probably have been done in a data step without the use of macros since you already use arrays in your data step syntax. If you want further help to improve and understand, pleace provide your full code. You no longer need %loopy if you move the macro loop construct inside your own %tamper macro.

additionnal notes:

Since the macro facility elements are resolved at compilation phase, there needs to be a way for the software to tell what parts of the code is macro code syntax and what part is regular sas statements. The % symbol prefixing a macro name, or function indicates that this is a line or segment that should be resolved at compilation phase.

You cannot remove the % from %loopy because loopy is a macro. If you wanted to have a data-step function doing something similar, you would've needed to use proc fcmp to define a function named loopy but since proc fcmp is more recent and macros are typically more efficient, most people use macros.

Vince

Occasional Contributor
Posts: 14

Re: Array, do loop and Marco

Posted in reply to Vince28_Statcan

Thanks Vince.

Let me phrase my question this way since I probably have written a code over-complicated.

1) I wrote a series of code, call it  'Tamper', that produce what I want. It involves generating some random number,say seeded at 1, and then (after some long calculation) I generate a column, called CDF1 that depends on that random number.

2) I want to repeat the previous step 1000 tims, with random number = 1 to 1000 and CDF1 to CDF1000.

3) At the end, I want

CDF1 CDF2 ....CDF1000  avg=mean(of CDF1 to CDF1000)  (how to do this by the way?)

I don't mind copy and paste the 150+ lines of code here, but somehow copy + paste does not work on this website?

Thank you so much !!!

- Matt

Respected Advisor
Posts: 3,156

Re: Array, do loop and Marco

Posted in reply to Vince28_Statcan

Vince,

Quote from Vince's post:

In order to run a macro in a datastep during execution phase, would've had to use something like

call execute("%tamper(sim="||i||")");

This actually reminds me of the new function that is available until after 9.3 M2:

DOSUBL()

For call execute, the macro will be sent to the macro facility and then dump to input stack, but it will only run AFTER the data step finished. BTW, since you don't want your macro resolved during the compiling phase, it might be a good idea to replace double quotes with single quotes.

Just my 2 cents,

Haikuo

Super Contributor
Posts: 339

Re: Array, do loop and Marco

Haikuo,

Sadly I'm still on 9.2 at work and have delayed requiring update to 9.3 waiting for statscan to obtain 9.4 licenses but I will definitely keep an eye on this thread as a reminder to check dosubl(). Also, thanks for pointing the "" versus '' mistake. I absolutely always do this when I write call execute statements the first pass its like I'm still haunted by my rookie mistakes of doing the opposite inside macros and having to debug for hours because I did not know that '' blocked ampersands resolving when I first dipped into macros.

MattLin,

The main concern to program this is the vertical vertical complexity of your "long calculation". For example, I can generate n records, each with 1000 random numbers with a syntax like the following for so long as the "long calculation" for each given cell, does not depend on any previous rows (it could depend on previous columns of the same row as I will throw in my example). If previous rows become involved in the calculation, then it becomes much more complicated in a datastep and using macros to calculate columns one at a time but always reusing the same dataset would probably be the path I take.

data want;

     array temparray{*} CDF1-CDF1000;

     do j=1 to 55; /*arbitrary number of records to generate to examplify*/

          do i=1 to 1000;

               if i=1 then temparray{i} = ranuni(i);

               else temparray{i} = temparray{i-1}*(0.001*i*rannor(i)); /* normal mu=0 sigma=0.001 successively multiplying each previous term in the same row */

          end;

     output;

     end;

     drop i j;

run;

Occasional Contributor
Posts: 14

Re: Array, do loop and Marco

Posted in reply to Vince28_Statcan

Thanks Vince. If you can help more, here is what I want, conceptually:

data want;

array temparray{*} CDF1 -CDF1000;

do i = 1 to 1000;

GO through 5 or 6 data steps that I already finish in 'Tamper', each run will produce one CDFX.

Hope that makes sense?

Still cannot copy and paste. Can you?

Thanks!!

- Matt

Super Contributor
Posts: 339

Re: Array, do loop and Marco

No but it's easy to join a .sas file if you can provide your code.

I'm definitely unclear with what you mean by "Go through 5 or 6 data steps that I already finish in 'Tamper'". I would really see the actual output or at least intended output of tamper is in terms of a data structure and how this is further used to build your final desired result.

Vince

Respected Advisor
Posts: 3,156

Re: Array, do loop and Marco

Well, if you have SAS 9.3 M2 or later, then it is quite simple:

data want;

array temparray{*} CDF1 -CDF1000;

  do i = 1 to 1000;

    rc=dosubl('%tamper'); /*assume you put your target value into macro variable 'xxx',*/

    temparray(i)=symget('xxx');

   end;

run;

Haikuo

Super User
Posts: 5,516

Re: Array, do loop and Marco

A variety of bits and pieces ...

To cut and paste, for me it works better if I copy my program into Notepad first.  Then cut from Notepad and paste here.

If you are using my %LOOPY macro, you must be very careful in defining %TAMPER.  If %TAMPER contains a macro variable named &I, it must be defined inside %TAMPER with a %LOCAL statement.

We don't see what happens inside the loops, but it is conceivable that you don't need to create separate columns.  You might be able to create the same column name each time, but use a separate name for each output data set.  Then append all the output data sets together, and use PROC SUMMARY on the final data set to get the average.

Using single quotes will not prevent the software from immediately executing macro language statements within CALL EXECUTE.  You will also need to apply %NRSTR to mask the percent signs.

Respected Advisor
Posts: 3,156

Re: Array, do loop and Marco

Posted in reply to Astounding

"You will also need to apply %NRSTR to mask the percent signs."

Exactly! Single quotes only works on Macro variables, which I often use to host SAS codes.  There is always something.

Super Contributor
Posts: 339

Re: Array, do loop and Marco

Posted in reply to Astounding

I may misunderstand something here but I would think that %nrstr in the above call execute example is not needed if single quotes are used. It is fine that the %tamper macro code gets resolved during the data step. The call execute becomes merely a way to pass the data step variable i. What we did not want is for the macro to be resolved before the execution phase of this data step because then i had no value or meaning other than a blatant text i.

At least that is how I understand it. I would need to use %nrstr if I wanted to pass an existing macro variable as text such that it only gets resolved later or the macro called by execute used other same our outer scope (like global) variables created with call symput within the same data step.

So unless (sim=) macro parameter uses call symput behind the scenes somehow, I don't see how not using %nrstr would cause any issue.

🔒 This topic is solved and locked.

Need further help from the community? Please ask a new question.

Discussion stats
  • 16 replies
  • 698 views
  • 9 likes
  • 5 in conversation