DATA Step, Macro, Functions and more

Assigning a macro variable inside a loop.

Reply
N/A
Posts: 0

Assigning a macro variable inside a loop.

Part of my program:
.
SOMETHING
.
do i=&m to 10;
%let j=%eval(&m+i);
.
SOMETHING
.
end;

Error message: A character operand was found in the %EVAL function ...
The condition was: 1+i

(&m happens to be 1 the first time)

I would like to get j=2,3,...,11 as a macro variable which I use later in the loop.

How can this be solved?
Contributor
Posts: 48

Re: Assigning a macro variable inside a loop.

%let j=%eval(&m+&i);
N/A
Posts: 0

Re: Assigning a macro variable inside a loop.

That doesn't work.

i isn't a macro variable.
Super Contributor
Posts: 359

Re: Assigning a macro variable inside a loop.

You would need to give us more of your code, but it appears you are mixing compile time macro resolution with datastep values. This will not work in the context you are presenting.
Give us a better idea what you are trying to do and we may be able to help.

You could for instance calculate a new value such as
x = &m + i;
call symput('j' , put(x best.))
N/A
Posts: 0

Re: Assigning a macro variable inside a loop.

Flip

With your:
x = &m + i;
call symput('j' , put(x, best.));

I tried with the expression: p=%eval(&j - 5);

But I get the same type of error message: A character operand was found ...
The condition was: &j -5

The code I have been referring to above was inside a macro, looking something like this:

%macro mymacro;
%do i=1 %to 10;
data newset&i;
set oldset&i;

code referred to

run;
%end;
%mend;
%mymacro;

Why is p=%eval(&j - 5); invalid?
Super Contributor
Super Contributor
Posts: 3,174

Re: Assigning a macro variable inside a loop.

You should be able to diagnose the &J value with the following SAS OPTIONS set:

OPTIONS SOURCE SOURCE2 MACROGEN SYMBOLGEN MLOGIC;

SAS macro variable &J is going to have a value *AFTER* the DATA step execution, not during it.

Moreover, I would encourage you to reveal the SAS code you represent as "code referred to" -- just to ensure that it is all DATA step code and not inconsistent macro language.

Hopefully you have read up on the suggested SCOPING documentation for important relevance?


Scott Barry
SBBWorks, Inc.
Super Contributor
Posts: 359

Re: Assigning a macro variable inside a loop.

The statement should work within a datastep providing that &j resolves to an integer. Put an statement %put &j ; right before the data step. That should tell you if it is resolving correctly. Do not expect to be able to assign and resolve a macro variable within a single datastep.
You may have better luck using dataset variables for these counters and assigning the macro variables conditionally from those.
Is there a reason for updating the macro variables at each itteration of your loop? Only the last loop will be assigned at the end of the datastep.

I see from your first post that this is exactly what you are doing. Macros don't work that way. Message was edited by: Flip
SAS Super FREQ
Posts: 8,744

Re: Assigning a macro variable inside a loop.

Hi:
When you use a %DO loop, your loop index becomes a MACRO variable. So in this case, any references to i should be &i;

However, as it says here (and in the documenation):
http://support.sas.com/kb/22/987.html

-You cannot use a MACRO variable reference to retrieve the value of a MACRO
variable in the same program (or step) in which SYMPUT creates that MACRO
variable and assigns it a value.
-You must explicitly use a step boundary statement to force the DATA Step to
execute before referencing the MACRO variable that is created with SYMPUT. The boundary
could be a RUN statement or another DATA or PROC statement.


This means that you CANNOT have the creation of &J and the use of &J in the same DATA step program.

This documentation is quite useful:
http://support.sas.com/documentation/cdl/en/mcrolref/61885/HTML/default/tw3514-symput.htm
http://support.sas.com/documentation/cdl/en/mcrolref/61885/HTML/default/a000210266.htm

It still is not clear to me what your exact process needs to be. However, consider the program below, which shows 2 new (dataset) variables being created from &I and &J (which are created OUTSIDE the Data step program...&I is created in the %DO loop and &J is created with a %LET). Note how the global macro variables &NEW_I and &NEW_J are available AFTER the Data step program and AFTER the macro program. They would NOT have been available INSIDE the Data step program. Note that you will see the DATASET variables in the PROC PRINT output and you will see the MACRO variables in the TITLE statement and in the %PUT results in the SAS log.

cynthia
[pre]

%macro mymacro;
%global new_i new_j;

%do i=1 %to 3;

%let j=%eval(&i + 10);

** because macro vars I and J are set -outside- the DATA step;
** they can be referenced -inside- the program;

data newset&i;
set sashelp.class;
if _n_ = &i then do;
newvar_i = &i + 10;
newvar_j = &j -10;
call symput('new_i',put(newvar_i,2.0));
call symput('new_j',put(newvar_j,2.0));
output;
end;
run;

** because there is a step boundary after the creation of;
** new_i and new_j macro variables, you can use them;
** after the program is over. Also, because they are GLOBAL;
** macro variables, they are available after the macro stops;
** executing;

ods listing;
proc print data=newset&i;
title "Value of I=&i Value of J=&j at program START";
title2 "NEWVAR_I is &i + 10 = %eval(&i+10)";
title3 "NEWVAR_J is &j - 10 = %eval(&j-10)";
title4 "Inside Macro: new_i = &new_i and new_j = &new_j";
run;

%end;
%mend mymacro;

%mymacro;

%put ************* Look in Log ****************;
%put Outside Macro: new_i = &new_i and new_j = &new_j;
[/pre]
N/A
Posts: 0

Re: Assigning a macro variable inside a loop.

My macro looked something like this:

%macro mymacro;
%do i=1 %to 10;
data newset&i;
set oldset&i;

code referred to

run;
%end;
%mend;
%mymacro;

After "code referred to" I wanted to name (and assign values to) some variables:

For the naming I tried with Var%eval(&k+1), where k depended upon &i and another variable j (not macro variabel), which in turn depended upon &i in certain and differing ways.

The important thing to me is the naming of the variables. I have checked every step in my pseudo code algorithm, and it is correct. It's the translation into SAS code that's difficult. In an ordinary programming language it had been easy to accomplish this.

SYMGET can't do me any good I think. It seems as if it get the values from the macro variable. But I want to put a number STRING after Var.

I have a lot to read now.
SAS Super FREQ
Posts: 8,744

Re: Assigning a macro variable inside a loop.

Hi:
Here's my .02 on your statement that:
The important thing to me is the naming of the variables. I have checked every step in my pseudo code algorithm, and it is correct. It's the translation into SAS code that's difficult. In an ordinary programming language it had been easy to accomplish this.


When you are creating a SAS macro program to accomplish something, you CANNOT work from pseudo code unless you have TONS and TONS of SAS macro experience.

The recommended steps are:
1) have a WORKING SAS program -- without ANY macro variable references AT all. If your WORKING SAS program needs to have hardcoded values, then write and test a WORKING program to make sure that your program generates at least 1 of your desired results. (for example, if you need to create 100 datasets, create a working SAS program with hard-coded logic to create the 1st dataset; then generate a second SAS program with hard-coded logic to create the 2nd dataset.) Somewhere along the way between writing program 1 and changing program 1 to become program 2, you will identify all the "mechanical" changes (need to increment a counter here) or all the opportunities to use a macro variable value.
[pre]
** step 1 -- have a working program or programs;
data make_s1;
set sashelp.class;
var1 = age + 1;
var2 = weight /(height * height);
run;

proc print data=make_s1;
title 'Step 1: Have a Working SAS program';
title2 'First time create VAR1 and VAR2';
run;

data make_s2;
set sashelp.class;
var2 = age + 2;
var4 = weight /(height * height);
run;

proc print data=make_s2;
title 'Step 1: Have a Working SAS program';
title2 'Second time create VAR2 and VAR4';
run;

data make_s3;
set sashelp.class;
var3 = age + 3;
var5 = weight /(height * height);
run;

proc print data=make_s3;
title 'Step 1: Have a Working SAS program';
title2 'Third time create VAR3 and VAR5';
run;
[/pre]


2) Now, take program #1 and create a macro variables with %LET and then use the macro variables in your program. You are NOT writing a macro program at this point -- you are just verifying that you identified the places in your program where macro variables could be used in the code. Now, test this somewhat generic program with the %LET statements that you need to produce the 1st data set -- if everything works as expected, now change the %LET statements in order to produce the 2nd data set. If everything works as expected, now make a macro program.

So in the program sample below, the first program creates a dataset called makevar_s1 where &X=1 and &Y=2; then the second program creates a dataset called makevar_s2 where &X=2 and &Y=4, and then the third program creates a dataset called makevar_s3 where &X=3 and &Y=5 ...and this pattern will repeat for all my other programs/datasets: the value of &Y will be equal to &X + 2 -- except for the first time. So this is the working code that I have now, with these data step programs and hard-coded %LET statements:
[pre]

** step 2 -- use %LET to create var1 and var2;
options mprint symbolgen;
%let x = 1;
%let y = 2;

data makevar_s&x;
set sashelp.class;
var&x = age + &x;
var&y = weight /(height * height);
run;

proc print data=makevar_s&x;
title 'Step 2: use macro variables set with %LET';
title2 "Dataset is: makevar_s&x X=&X Y=&Y";
run;

** step 2a -- use %LET to create var2 and var4;
options mprint symbolgen;
%let x = 2;
%let y = 4;

data makevar_s&x;
set sashelp.class;
var&x = age + &x;
var&y = weight /(height * height);
run;

proc print data=makevar_s&x;
title 'Step 2a: use macro variables set with %LET and diff values';
title2 "Dataset is: makevar_s&x X=&X Y=&Y";
run;

** step 2a -- use %LET with different values and see if;
** program still works. This time create the;
** variables var3 and var5.;
options mprint symbolgen;
%let x = 3;
%let y = 5;

data makevar_s&x;
set sashelp.class;
var&x = age + &x;
var&y = weight /(height * height);
run;

proc print data=makevar_s&x;
title 'Step 2a: use macro variables set with %LET and diff values';
title2 "Dataset is: makevar_s&x X=&X Y=&Y";
run;
[/pre]

3) Turn your program with macro variable references into a macro -program- (between %MACRO/%MEND). Now and -only- now do you put in the %IF and %DO logic. If you have tested your program (not pseudo code) at every step along the way, your debugging during this step should not be too burdensome. If you go straight to step 3, without understanding how to make the transition between step 1 and step 2 and then between step 2 and step 3 -- your macro program will be prone to errors, partly because you did not start with a WORKING program and did not understand which pieces of your process belonged to DATA step and procedure code and which pieces of your process belonged to the macro program. For example, in the macro program below, on the first time through the %DO loop, if &X = 1, then &Y is set to 2...for all other iterations, &Y is set to be equal to &X + 2.

[pre]
** step 3: NOW make and test a macro program;
** when x = 1, create variables var1 and var2 (use %IF);
** when x = 2, create variables var2 and var4 (&y = &x + 2);
** when x = 3, create variables var3 and var5 (&y = &x + 2);
** The macro logic is that inside the %DO loop, &X will be;
** incremented by 2 to create &Y -- for every iteration except
** when &X = 1.;

%macro calcvars;
options mprint symbolgen;
%do x = 1 %to 3;
%let y = %eval(&x + 2);

*** The first time thru the loop, want &Y to be 2 (not 3);
%if &x = 1 %then %let y = 2;

data makevar_s&x;
set sashelp.class;
var&x = age + &x;
var&y = weight /(height * height);
run;

ods listing;
proc print data=makevar_s&x;
title "Step 3: Value for X = &X and Y = &Y";
title2 "Dataset is: makevar_s&x";
run;
%end;

%mend calcvars;

%calcvars;

[/pre]

If you examine the output from the above examples, the macro program has the %DO loop and the %IF logic added. This is my never-fail method for writing a complex macro program that doesn't have endless debugging issues. If I start with a WORKING program and not pseudo code, I always have better success than if I jump straight to step 3. That's just my experience.

The most important thing to remember is that the SAS Macro facility is acting like a big, dumb typewriter. In the above programs, every time the Macro word scanner sees "&X", it will type whatever the current value of &X is into the program code that is being generated.

The Macro facility does not "compile" or "execute" any code. It only "resolves" code and then the resolved code is sent to the compiler. That's why it's important to know what your WORKING program needs to look like -- because you want your macro program to generate that same WORKING program or programs for you. When a Macro PROGRAM executes, it is ONLY generating code based on the %DO loop(s) or %IF statement(s) in your MACRO program. the generated code is then sent to the compiler and, if free of compile errors, will be executed. By the time your code gets to the SAS compiler, there are no macro variable references or %DO loops or %IF logic. The links previously posted will enlighten you on the issues of macro variable scope and whether macro variables are LOCAL or GLOBAL in scope, however, even these issues only apply to the way that resolved code is generated.

cynthia
N/A
Posts: 0

Re: Assigning a macro variable inside a loop.

Cynthia

Thank you for your detailed answer.

I could move assignments of a macro variable outside the data steps:

%if &i le 5 %then &p=1;
%else &p=&i-1;

BUT inside the data steps I must have the counter j in a do loop:

do j=&p to 9;

some code

naming Var (and assigning values)

end;

And it is the j that should influence the the names on my variables Var_(
depending on j).

I am not sure if the following is allowed inside the datasteps and without risk of spoiling something:

%do j=&p %to 9;

some code

naming Var (and assigning values)

%end;

If allowed, then my problem would be solved.
Super Contributor
Super Contributor
Posts: 3,174

Re: Assigning a macro variable inside a loop.

Honestly, you need to read and digest the SCOPING discussion for macro variables -- it appears that you still do not get it -- the line of code below will not change with a DATA step execution:

call symput('j' , put(x, best.));

You will see the SAS compilation activity if you turn on the diagnostics as suggested earlier in this thread.

Scott Barry
SBBWorks, Inc.
Super Contributor
Posts: 359

Re: Assigning a macro variable inside a loop.

You are trying to create a variable in the middle of a datastep. Have you looked at using your macro to set up your data structure prior to reading the data? In this simple example I am using an array to create the variables prior to reading the data.

%let p = 5;
data one;
array avar(*) var_&p - var_9 ;
do j = &p to 9;
avar(j-&p+1) = J + 7;
end;
run;

Since I have the limits of the array, I am able to set up my data structure then use the datastep loop to assign them values.
N/A
Posts: 0

Re: Assigning a macro variable inside a loop.

Scott Barry

Your wording: "the line of code below will not change with a DATA step execution".

Can you explain what you really want to say. Do you mean that the line of code will have no effect or what?

And I have been reading some documentation ...

For instance: "You can create global macro variables any time during a SAS session or job. Except for some automatic macro variables, you can change the values of global macro variables any time during a SAS session or job."

If I declared a macro variabel n outside the data steps with %GLOBAL n;,
wouldn't it be possibel to assign the counter j inside the data steps to n, and then name my variables Var_&n?
Super Contributor
Super Contributor
Posts: 3,174

Re: Assigning a macro variable inside a loop.

Share *ALL* of your SAS application code and then we can discuss it in detail.

You have been provided opportunities for DOC reference and also suggested SAS log diagnostic techniques, which either you have not attempted or possibly the output does not make sense.

Once again, SAS macro language is normally resolved during SAS code compilation, not during DATA step execution / iternation. A CALL SYMPUT within a DATA step will not change or set a macro variable value until the DATA step is completed -- by that I mean when the next RUN; occurs or when a new DATA step or PROC statement is encountered by SAS. I expect that it would help you with understanding the code by ending each SAS DATA or PROC "step" with a RUN; statement, so you will see SAS compile up to that point.

Scott Barry
SBBWorks, Inc.
Ask a Question
Discussion stats
  • 22 replies
  • 281 views
  • 0 likes
  • 7 in conversation