Can you create a macro variable, increment it (it's numeric) and use it in the same DATA step? If so, how?
A call SYMPUTwill not work, Slaughter and Delwiche (2004) elaborates, "Be careful. You cannot create a macro variable with CALL SYMPUT and use it in the same DATA step. Here’s why. When you submit macro code, it is resolved by the macro processor, and then compiled and executed. Not until the final stage—execution—does SAS see your data. CALL SYMPUT takes a data value from the execution phase, and passes it back to the macro processor for use in a later step. That’s why you must put CALL SYMPUT in one DATA step, but not use it until a later
step" (p. 11)
Slaughter, S.J., Delwiche, L. D. (2004). SAS Macro Programming for Beginners. Retrieved from http://www2.sas.com/proceedings/sugi29/243-29.pdf.
You can do it. It has no practical use that I can think of.
81 data _null_;
82 a = 100;
83 call symputX('a',a);
84 call execute('%put NOTE: A=&a;');
85 do i = 1 to 10;
86 call execute('%put NOTE: Before: A=&a;');
87 a = symgetN('a') + 1;
88 call symputX('a',a);
89 call execute('%put NOTE- After : A=&a;');
90 end;
91 run;
NOTE: A=100
NOTE: Before: A=100
After : A=101
NOTE: Before: A=101
After : A=102
NOTE: Before: A=102
After : A=103
NOTE: Before: A=103
After : A=104
NOTE: Before: A=104
After : A=105
NOTE: Before: A=105
After : A=106
NOTE: Before: A=106
After : A=107
NOTE: Before: A=107
After : A=108
NOTE: Before: A=108
After : A=109
NOTE: Before: A=109
After : A=110
NOTE: DATA statement used (Total process time):
real time 0.03 seconds
cpu time 0.03 seconds
NOTE: CALL EXECUTE routine executed successfully, but no SAS statements were generated
You can do it. It has no practical use that I can think of.
81 data _null_;
82 a = 100;
83 call symputX('a',a);
84 call execute('%put NOTE: A=&a;');
85 do i = 1 to 10;
86 call execute('%put NOTE: Before: A=&a;');
87 a = symgetN('a') + 1;
88 call symputX('a',a);
89 call execute('%put NOTE- After : A=&a;');
90 end;
91 run;
NOTE: A=100
NOTE: Before: A=100
After : A=101
NOTE: Before: A=101
After : A=102
NOTE: Before: A=102
After : A=103
NOTE: Before: A=103
After : A=104
NOTE: Before: A=104
After : A=105
NOTE: Before: A=105
After : A=106
NOTE: Before: A=106
After : A=107
NOTE: Before: A=107
After : A=108
NOTE: Before: A=108
After : A=109
NOTE: Before: A=109
After : A=110
NOTE: DATA statement used (Total process time):
real time 0.03 seconds
cpu time 0.03 seconds
NOTE: CALL EXECUTE routine executed successfully, but no SAS statements were generated
Your code works like a charm...and for the practical part - my last and final step of what I'm trying to accomplish based on your excellent code:
Your code works, unfortunately mine chokes on me adding the final part...
81 data output_ds;
set input_ds
82 a = 100;
83 call symputX('a',a);
84 call execute('%put NOTE: A=&a;');
85 do i = 1 to 10;
86 call execute('%put NOTE: Before: A=&a;');
87 a = symgetN('a') + 1;
88 call symputX('a',a);
89 call execute('%put NOTE- After : A=&a;');
/* I want to clear fields in a column that end in '&a' */
column1_&a = .;
column2_&a = .;
column3_&a = .;
90 end;
91 run;
The reason DN's example works is because symgetn gets resolves the variable during run time. The single quoted string inside call execute also resolves at run time. You example: &a in plain text will resolve at compile time.
data _null_;
a = 100;
call symputX('a',a);
call execute('%put NOTE: A=&a;');
do i = 1 to 10;
call execute('%put NOTE: Before: A=&a;');
a = symgetN('a') + 1;
call symputX('a',a);
call execute('%put NOTE- After : A=&a;');
call execute("%put NOTE- Nope : A=&a;");
call execute('%nrstr(%put NOTE: After After... : A=&a;)');
end;
run;
You would probably be better off approaching the problem with arrays, hopefully you can find the following directional:
data have;
length a 3;
array column_[100] 3 (100*1);
do _n_=1 to 100;
a=ceil(100*ranuni(1));
output;
end;
run;
data want;
length a 3;
array column_[100];
set have;
column_=.;
run;
I thought your question was rhetorical. (or whatever the right word is).
I'm pretty sure can do what you want without involving macro variables. If you could show a bit of your data. The have/want scenario that would be good.
Thank you everyone - I can see that I'm in a room full of macro experts and your help is appreciated.
Ok, per your suggestion - here is the scenario:
byear eyear f1_2009 f2_2009 f1_2010 f2_2010 f2_2011 f1_2011 f1_2012 f2_2012
2010 2012 1 . . 1 1 . 1 .
. . . 1 1 1 1 . . 1
2009 2011 1 1 1 . 1 . . 1
. . . . 1 . 1 . 1 1
. . . 1 1 . 1 1 1 .
2009 2009 1 1 1 1 . 1 . .
Basically, I want to zero out the fields that fall between the begin year (byear) and the end year (eyear). For example, on line three, I would like to set fn_2009 = ., fn_2010 = ., fn_2011 = . since they all fall within years 2009-2011.
Here’s my basic premise…
data out_ds;
set in_ds;
if byear~=. then
do;
do while (byear <= eyear);
call symputx('year', byear);
call execute('%put &year;');
f1_&year=.;
f2_&year=.;
...
byear=byear+1;
end;
end;
run;
Ever had the feeling that you're over thinking something - I'm there currently ;o)
Does this give you what you want?
HTH,
Scott
data have;
input byear eyear f1_2009 f2_2009 f1_2010 f2_2010 f2_2011 f1_2011 f1_2012 f2_2012;
datalines;
2010 2012 1 . . 1 1 . 1 .
. . . 1 1 1 1 . . 1
2009 2011 1 1 1 . 1 . . 1
. . . . 1 . 1 . 1 1
. . . 1 1 . 1 1 1 .
2009 2009 1 1 1 1 . 1 . .
;
run;
data want;
set have;
array vars{*} f1_2009 -- f2_2012;
do i=1 to dim(vars);
vname=vname(vars{i});
vyear=input(scan(vname,2,"_"),best.);
if byear <= vyear <= eyear then call missing(vars{i});
end;
run;
If any of this is unclear, hit the doc on the array statement (and examples), plus the vname, input, and scan functions, plus the call missing routine (you could also just assign vars{i}=. if you prefer).
Maybe I have oversimplied your problem, but it does look like a simple array() problem:
data have;
input byear eyear f1_2009 f2_2009 f1_2010 f2_2010 f2_2011 f1_2011 f1_2012 f2_2012;
cards;
2010 2012 1 . . 1 1 . 1 .
. . . 1 1 1 1 . . 1
2009 2011 1 1 1 . 1 . . 1
. . . . 1 . 1 . 1 1
. . . 1 1 . 1 1 1 .
2009 2009 1 1 1 1 . 1 . .
;
data want;
set have;
array f f1_2009--f2_2012;
do over f;
f=ifn(byear <= scan(vname(f),2,'_') <= eyear, ., f);
end;
run;
proc print;run;
Haikuo
Can you describe exactly what you're trying to do? I don't think you need macro in your example.
Quote:
/* I want to clear fields in a column that end in '&a' */
column1_&a = .;
column2_&a = .;
column3_&a = .;
Say &a=100 when the data step is compiled. This would resolve to:
/* I want to clear fields in a column that end in '&a' */
column1_100 = .;
column2_100 = .;
column3_100 = .;
That code isn't going to change, regardless of how much you fiddle with &a. It's already been compiled.
What about something like:
call missing(of column:);
or for something more exotic (untested)
array nums column:;
do i=1 to dim(nums);
if prxmatch("/column(\d+)_(\d+)/",vname(nums{i}) then nums{i}=.
end;
which would define an array "nums" containing all variables whose names begin with "column". Assume they're all numeric. Loop over the array. If the name matches "column", a number, an underscore, and a number, then set that variable to missing.
Again, without knowing what you want to do, it's hard to supply example code. But I suspect you don't need macro here, and you'll just tie yourself in knots if you try.
Hope this helps,
Scott
If you need a variable to persist between records look at RETAIN. That is likely to do what you need within a single datastep.
A call SYMPUTwill not work, Slaughter and Delwiche (2004) elaborates, "Be careful. You cannot create a macro variable with CALL SYMPUT and use it in the same DATA step.
That is true.
But there is an accident .
You can use the macro variable created by CALL SYMPUT() in the same data step when you are also using SYMGET(). Documentation has already clarified it.
15 data _null_; 16 do _n_=1 to 10 ; 17 call symputx('obs',_n_); 18 x= symget('obs'); 19 put x=; 20 end; 21 run; x=1 x=2 x=3 x=4 x=5 x=6 x=7 x=8 x=9 x=10 NOTE: DATA statement used (Total process time): real time 0.22 seconds cpu time 0.00 seconds
Ksharp
Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!
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.