BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
wetman
Fluorite | Level 6

Hi all, I'm trying to use macro to process some data for each observation in a data step but is stuck in a situation that I don't understand.

To simplify: to add a "+" after the sex variable ("M" or "F") in sashelp.class, and assign the results to a new variable "trial". Here's the code I use:

%global gvar;
%macro test(invar);
	%let gvar = %sysfunc(cats(&invar,+));
	%put in macro: &gvar;
%mend;
Data test;
	set sashelp.class;
	call execute('%test('||Sex||')');
	put "In data step: &gvar";
	trial = "&gvar";
run;

Here is the result I got from the log:

 

macrovariable.png

As you can see, SAS did go through each observation and got the correct "M+" or "F+" for the global macro variable &gvar. But once it comes out of the macro and get back to the data step, the value of &gvar changed to "M+" regardless whether this observation is male or female.

macrovariable2.png

And with no surprise, in the dataset, the "trial" variable has got the undesired values.

Could you please explain why this is happening and what am I supposed to do to achieve the correct results?

Thanks!

1 ACCEPTED SOLUTION

Accepted Solutions
ballardw
Super User

@wetman wrote:

Thank you very much for your explanation!

Now I understand that the value of the global macro variable is taken when compiling the data step rather than running this line. But what is the alternative way to achieve what I'm trying to do?

Of course doing it without using macro is the simpliest way in this example, but adding a "+" is only a simplified version. In real life I'm trying to run quite a few if...then... commands and would prefer to keep them in something like a macro to keep the code tidy.


 

It may help to describe what your actual problem/ need is some detail.

 

Many times if you are only considering a single variable and want the value displayed differently then a FORMAT is the choice. If the requirement is more complicated then perhaps creating your own function with Proc FCMP is going to be more appropriate. Or possibly a Format that calls a function. If the behavior can be done with a format then there is usually no need to actually add or modify variables as groups/values created by formats will be honored by reporting and analysis procedures and generally will work with graphing procedures as well (most exceptions relate to custome date/time/datetime formats).

 

There are additional "timing" aspects related to using Call execute as the code doesn't actually execute in the middle of the data step but at the step boundary.

View solution in original post

5 REPLIES 5
ballardw
Super User

The clue will come from running the code in a fresh session. When I run your shown code the first time I get:

276  Data test;
277     set sashelp.class;
278     call execute('%test('||Sex||')');
279     put "In data step: &gvar";
WARNING: Apparent symbolic reference GVAR not resolved.
280     trial = "&gvar";
WARNING: Apparent symbolic reference GVAR not resolved.
281  run;

And the output for "in data step" is always &gvar.

 

This is because the value for &gvar used by Trial =  is what the macro variable has when the data step compiles before anything executes so the statement is always "trial= "&gvar" the first time or "trial= "M " . So all your results are showing the result of the last time the &gvar was set before the data step executes.

 

 

 

 

 

wetman
Fluorite | Level 6

Thank you very much for your explanation!

Now I understand that the value of the global macro variable is taken when compiling the data step rather than running this line. But what is the alternative way to achieve what I'm trying to do?

Of course doing it without using macro is the simpliest way in this example, but adding a "+" is only a simplified version. In real life I'm trying to run quite a few if...then... commands and would prefer to keep them in something like a macro to keep the code tidy.

ballardw
Super User

@wetman wrote:

Thank you very much for your explanation!

Now I understand that the value of the global macro variable is taken when compiling the data step rather than running this line. But what is the alternative way to achieve what I'm trying to do?

Of course doing it without using macro is the simpliest way in this example, but adding a "+" is only a simplified version. In real life I'm trying to run quite a few if...then... commands and would prefer to keep them in something like a macro to keep the code tidy.


 

It may help to describe what your actual problem/ need is some detail.

 

Many times if you are only considering a single variable and want the value displayed differently then a FORMAT is the choice. If the requirement is more complicated then perhaps creating your own function with Proc FCMP is going to be more appropriate. Or possibly a Format that calls a function. If the behavior can be done with a format then there is usually no need to actually add or modify variables as groups/values created by formats will be honored by reporting and analysis procedures and generally will work with graphing procedures as well (most exceptions relate to custome date/time/datetime formats).

 

There are additional "timing" aspects related to using Call execute as the code doesn't actually execute in the middle of the data step but at the step boundary.

wetman
Fluorite | Level 6

Thank you so much to both of you!

What I'm trying to do this time is actually simple enough to be handled with format. But I am also eager to know what can be used if more complex processing is required. Both of your answers have pointed me to the direction I'm looking for. 

Both of your answers are good enough to be accepted as the solution so I chose Ballardw's since he replied first and twice. Really appreciate your help!

Tom
Super User Tom
Super User

So you want to append a + to a string?  Just type the + after the string.

%let gvar = &invar.+;

If you have some complex logic you want to apply in the middle of a data step consider coding it as a subroutine in the data step instead of messing around with trying to manipulate data in macro code.

data test;
  set sashelp.class;
  length trial $2 ;
  trial=sex;
  link add_plus;
return;
add_plus:
  trial=cats(trial,'+');
return;
run;

If you have complex macro that returns a string you could write the macro so that the only text it generates is the string your want returned.  So no need to a GLOBAL macro variable.

%macro addplus(invar);
&invar.+
%mend;

You can then use the RESOLVE() function to dynamically call the macro over and over in the data step.

data test;
  set sashelp.class;
  length trial $2 ;
  trial=resolve(cats('%addplus(',sex,')'));
run;

 

 

sas-innovate-2024.png

Available on demand!

Missed SAS Innovate Las Vegas? Watch all the action for free! View the keynotes, general sessions and 22 breakouts on demand.

 

Register now!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 5 replies
  • 425 views
  • 3 likes
  • 3 in conversation