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

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.

1 ACCEPTED SOLUTION

Accepted Solutions
RW9
Diamond | Level 26 RW9
Diamond | Level 26

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

7 REPLIES 7
Tom
Super User Tom
Super User

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;

armor
Calcite | Level 5

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();

RW9
Diamond | Level 26 RW9
Diamond | Level 26

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. 

Tom
Super User Tom
Super User

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):(Column).

      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.

armor
Calcite | Level 5

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;

jakarman
Barite | Level 11

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 --<-----
damanaulakh88
Obsidian | Level 7


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

sas-innovate-2024.png

Join us for SAS Innovate April 16-19 at the Aria in Las Vegas. Bring the team and save big with our group pricing for a limited time only.

Pre-conference courses and tutorials are filling up fast and are always a sellout. Register today to reserve your seat.

 

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
  • 7 replies
  • 2808 views
  • 3 likes
  • 5 in conversation