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:
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.
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!
@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.
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.
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.
@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.
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!
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;
Registration is now open for SAS Innovate 2025 , our biggest and most exciting global event of the year! Join us in Orlando, FL, May 6-9.
Sign up by Dec. 31 to get the 2024 rate of just $495.
Register now!
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.
Ready to level-up your skills? Choose your own adventure.