DATA Step, Macro, Functions and more

Creating New Variables with %Do Loop

Accepted Solution Solved
Reply
Occasional Contributor
Posts: 14
Accepted Solution

Creating New Variables with %Do Loop

Say I want to create 100 new variables (x1, x2,,, x100) with %do %to loop, and x1 might be char and x2 might be numeric and so on, the following sample code strangely does not allow me to create x1 and x2 with different format:

%macro format();

data test;

%do i=1 %to 2;

if &i.=1 then x&i.=1;

if &i.=2 then x&i.="two";

%end;

run;

%mend;

%format();

I have to change the code a little bit to get what I want:

%macro format1();

data test1;

%do i=1 %to 2;

%if &i.=1 %then %do; x&i.=1; %end;

%if &i.=2 %then %do; x&i.="two"; %end;

%end;

run;

%mend;

%format1();

so the questions are, 1) what is the reason behind the wierd behavior, and 2) is there is a way to do what I want, using only "if then" instead of "%if %then", as "%if %then" is so clumsy and I cannot really use it in more compleecated coding.

Thanks.


Accepted Solutions
Solution
‎09-15-2014 04:46 AM
Super User
Super User
Posts: 7,955

Re: Creating New Variables with %Do Loop

Hi,

Could I suggest that you use the following options:

options mlogic mprint sybolgen;

You can then inspect the log to see why this is occurring.  It is then quite simple to see what the problem is:

MLOGIC(FORMAT):  Beginning execution.

MPRINT(FORMAT):   data test;

MLOGIC(FORMAT):  %DO loop beginning; index variable I; start value is 1; stop value is 2; by value is 1.

SYMBOLGEN:  Macro variable I resolves to 1

SYMBOLGEN:  Macro variable I resolves to 1   

/* At this point x1 needs to be created, as the results for the if are numeric, that is the attribute assigned to X1 */

MPRINT(FORMAT):   if 1=1 then x1=1;

SYMBOLGEN:  Macro variable I resolves to 1

SYMBOLGEN:  Macro variable I resolves to 1

MPRINT(FORMAT):   if 1=2 then x1="two";

MLOGIC(FORMAT):  %DO loop index variable I is now 2; loop will iterate again.

SYMBOLGEN:  Macro variable I resolves to 2

SYMBOLGEN:  Macro variable I resolves to 2

/* At this point, although the if condition does not resolve to true, the line is still used to assign a numeric to X2 */

MPRINT(FORMAT):   if 2=1 then x2=1;

SYMBOLGEN:  Macro variable I resolves to 2

SYMBOLGEN:  Macro variable I resolves to 2

/* And when we get to this point where the if is true the variable already exists as numeric, hence char to number notes */

MPRINT(FORMAT):   if 2=2 then x2="two";

MLOGIC(FORMAT):  %DO loop index variable I is now 3; loop will not iterate again.

MPRINT(FORMAT):   run;

Now you could change your macro to look like this:

options mlogic mprint symbolgen;
%macro format();
  data test;
    %do i=1 %to 2;
      %if &i.=1 %then %do;
        x&i.=1;
      %end;
      %if &i.=2 %then %do;
        x&i.="two";
      %end;
    %end;
  run;
%mend;

%format();

However I would be more interested to see what you are actually trying to achieve, i.e. why do you need to create lots of possibly numeric or character variables?  Maybe assigning a value to them in a datastep then transposing would be better, or arrays, or generating code etc. 

View solution in original post


All Replies
Super User
Super User
Posts: 7,046

Re: Creating New Variables with %Do Loop

Macro is a pre-processor that generates SAS code.  That is why you need to use %IF so that you can conditionally generate the code you want.

I am not sure why %IF/%THEN is any more "clumsy" than IF/THEN, they basically are the same type of construct, but they are operating in two different languages.

If you want to generate code from data then it is probably easier to use a regular program instead of macro code.  You could generate code to a macro variable and reference it where you want it used. Or you could use CALL EXECUTE().  Or write the code to a file and %INCLUDE the file.

data new_vars ;

   length name $32 type $4 length 8 ;

cards;

X1 num 8

X2 char 3

run;

proc sql noprint ;

select catx(' ',name,case when type='char' then '$' else ' ' end,length)

into :lengths separated by ' '

from new_vars;

run;

data want ;

length &lengths ;

run;

Occasional Contributor
Posts: 14

Re: Creating New Variables with %Do Loop

Tom. thanks.

My point here is that I have been using %do loop to create 100 new variables (x1 to x100) and everything worked perfectly fine for me as long as x1, x2, ...x100 are of the same format (either all char or all numeric).

but strange thing happened when x1, x2... x100 are of different formats, i.e. mixed of char and num. See the following 3 macros - the first 2 macros give you the corect outcomes, and the third macro gives you undesired results:

%macro format_num();

data test_num;

%do i=1 %to 2;

if &i.=1 then x&i.=1;

if &i.=2 then x&i.=2;

%end;

run;

%mend;

%format_num();

%macro format_char();

data test_char;

%do i=1 %to 2;

if &i.=1 then x&i.="one";

if &i.=2 then x&i.="two";

%end;

run;

%mend;

%format_char();

%macro format();

data test;

%do i=1 %to 2;

if &i.=1 then x&i.=1;

if &i.=2 then x&i.="two";

%end;

run;

%mend;

%format();

Solution
‎09-15-2014 04:46 AM
Super User
Super User
Posts: 7,955

Re: Creating New Variables with %Do Loop

Hi,

Could I suggest that you use the following options:

options mlogic mprint sybolgen;

You can then inspect the log to see why this is occurring.  It is then quite simple to see what the problem is:

MLOGIC(FORMAT):  Beginning execution.

MPRINT(FORMAT):   data test;

MLOGIC(FORMAT):  %DO loop beginning; index variable I; start value is 1; stop value is 2; by value is 1.

SYMBOLGEN:  Macro variable I resolves to 1

SYMBOLGEN:  Macro variable I resolves to 1   

/* At this point x1 needs to be created, as the results for the if are numeric, that is the attribute assigned to X1 */

MPRINT(FORMAT):   if 1=1 then x1=1;

SYMBOLGEN:  Macro variable I resolves to 1

SYMBOLGEN:  Macro variable I resolves to 1

MPRINT(FORMAT):   if 1=2 then x1="two";

MLOGIC(FORMAT):  %DO loop index variable I is now 2; loop will iterate again.

SYMBOLGEN:  Macro variable I resolves to 2

SYMBOLGEN:  Macro variable I resolves to 2

/* At this point, although the if condition does not resolve to true, the line is still used to assign a numeric to X2 */

MPRINT(FORMAT):   if 2=1 then x2=1;

SYMBOLGEN:  Macro variable I resolves to 2

SYMBOLGEN:  Macro variable I resolves to 2

/* And when we get to this point where the if is true the variable already exists as numeric, hence char to number notes */

MPRINT(FORMAT):   if 2=2 then x2="two";

MLOGIC(FORMAT):  %DO loop index variable I is now 3; loop will not iterate again.

MPRINT(FORMAT):   run;

Now you could change your macro to look like this:

options mlogic mprint symbolgen;
%macro format();
  data test;
    %do i=1 %to 2;
      %if &i.=1 %then %do;
        x&i.=1;
      %end;
      %if &i.=2 %then %do;
        x&i.="two";
      %end;
    %end;
  run;
%mend;

%format();

However I would be more interested to see what you are actually trying to achieve, i.e. why do you need to create lots of possibly numeric or character variables?  Maybe assigning a value to them in a datastep then transposing would be better, or arrays, or generating code etc. 

Super User
Super User
Posts: 7,046

Re: Creating New Variables with %Do Loop

Did you look at the code that your macro is generating?  Turn on the MPRINT option.

None of those programs make any sense, but the last one generates code that causes error messages.

First you are telling SAS that sometimes you want X2 to have the value 1 so it defines X2 as a number since you tried to assign a number to it the first time it sees any usage of X2.

But then when the step runs the IF 2=2 clause is true so it tries to assign the string "two" to the numeric variable.

MPRINT(FORMAT):   data test;

MPRINT(FORMAT):   if 1=1 then x1=1;

MPRINT(FORMAT):   if 1=2 then x1="two";

MPRINT(FORMAT):   if 2=1 then x2=1;

MPRINT(FORMAT):   if 2=2 then x2="two";

MPRINT(FORMAT):   run;

NOTE: Character values have been converted to numeric

      values at the places given by: (Line)Smiley SadColumn).

      3:43   5:43

NOTE: Invalid numeric data, 'two' , at line 5 column 43.

x1=1 x2=. _ERROR_=1 _N_=1

NOTE: The data set WORK.TEST has 1 observations and 2 variables.

Occasional Contributor
Posts: 14

Re: Creating New Variables with %Do Loop

thanks everyone. the sas code by the mprint function explains the observation.

*==========================;

MPRINT(FORMAT):   data test;

MPRINT(FORMAT):   if 1=1 then x1=1;

MPRINT(FORMAT):   if 1=2 then x1="two";

MPRINT(FORMAT):   if 2=1 then x2=1;

MPRINT(FORMAT):   if 2=2 then x2="two";

MPRINT(FORMAT):   run;

Trusted Advisor
Posts: 3,213

Re: Creating New Variables with %Do Loop

The macro processor is processing only text, does not anything about numeric or char of the sas-datastep.

Within the sas datastep you can work with arrays and variable lists. The requirement there is commonly they should be of the same type.

The approach of macro usage here could be an strange way to do something.

I am missing the real issue the real problem.   

---->-- ja karman --<-----
Frequent Contributor
Posts: 81

Re: Creating New Variables with %Do Loop


Try using this code:

%macro format1();

data test1;

%do i=1 %to 100;
%if %sysfunc(mod(&i.,2))= 0 %then %do;
x&i.="two";
%end;
%else %do;
x&i.="1";%end;
%end;
run;

proc print data=test1;
run;

%mend;

%format1();

Thanks,

Daman

🔒 This topic is solved and locked.

Need further help from the community? Please ask a new question.

Discussion stats
  • 7 replies
  • 396 views
  • 3 likes
  • 5 in conversation