BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
robby_beum
Quartz | Level 8

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.

1 ACCEPTED SOLUTION

Accepted Solutions
data_null__
Jade | Level 19

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

View solution in original post

10 REPLIES 10
data_null__
Jade | Level 19

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

robby_beum
Quartz | Level 8

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;

FriedEgg
SAS Employee

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;

data_null__
Jade | Level 19

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.

robby_beum
Quartz | Level 8

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)

ScottBass
Rhodochrosite | Level 12

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).


Please post your question as a self-contained data step in the form of "have" (source) and "want" (desired results).
I won't contribute to your post if I can't cut-and-paste your syntactically correct code into SAS.
Haikuo
Onyx | Level 15

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

ScottBass
Rhodochrosite | Level 12

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


Please post your question as a self-contained data step in the form of "have" (source) and "want" (desired results).
I won't contribute to your post if I can't cut-and-paste your syntactically correct code into SAS.
ballardw
Super User

If you need a variable to persist between records look at RETAIN. That is likely to do what you need within a single datastep.

Ksharp
Super User

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

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
  • 10 replies
  • 11375 views
  • 9 likes
  • 7 in conversation