BookmarkSubscribeRSS Feed
n6
Quartz | Level 8 n6
Quartz | Level 8

The code below isn't my exact code but it accurately illustrates the problem and is much simpler.

 

I have a macro named ABC with the argument BP_VAR.    "BP" stands for "Body Part" and the values I can specify for BP_VAR are Arm, Leg and Head.

 

Inside the macro, there is a datastep and inside it I had code like this.

 

If &BP_VAR = "Arm" then do;

    ... some statements...

end;

And there is similar code for Leg and Head.  But it wouldn't work so I found a way around it, although if you have comments on why the above didn't work then I'd be interested to hear it. 

 

But here is the way I got around it, with a longer example to get to the meat of my question.  The formats abc, def and ghi referred to below have already been defined.  This code is from inside the macro.

 

%LET BP = "&BP_VAR";

 

if &BP = "Arm" then do;

     ...some assignment statements to create a new variable &BP.2;

    format &BP.2 abc.;

end;

 

if &BP = "Leg" then do;

     ...some assignment statements to create a new variable &BP.2;

    format &BP.2 def.;

end;

 

if &BP = "Head" then do;

     ...some assignment statements to create a new variable &BP.2;

    format &BP.2 ghi;

end;

  

So as you can see, when the body part of interest is Arm, I want it to go to the Arm section of the macro and make a new variable named Arm2, makes some assignments for it, then assign a format to it.  And then go on to do some other stuff that is not relevant to the current problem.

 

And I want to do the same when the body part of interest is Leg.  And again when the body part of interest is Head.

 

Okay, so here is the problem.  Say the body part is Arm.  The if-then condition is satisfied in the first section and it creates the new variable and assigns the format abc to it.  Neither of the other two if-then conditions are satisfied so it shouldn't even get into those it-then-do loops.

 

And yet when I run the code as listed, the format for the newly created variable Arm2 is ghi (the one in the 3rd if-then-do group).  If I comment out the format assignment in the 3rd group, then the format for the new variable is def (the one in the 2nd if-then-do group).

 

IOW, it uses the last non-commented out format assignment statement there is even if it's inside a if-then-do group that does not get executed.  I even put "put = 'gets here';" code inside the if-then-do group that I don't think is getting executed, and indeed it is not getting executed because the "gets here" message does not show up in the log.  And yet somehow the format assignment in that if-then-do group does get executed.

 

Any idea on what's happening?  Thanks in advance.

9 REPLIES 9
PaigeMiller
Diamond | Level 26

I have a macro named ABC with the argument BP_VAR. "BP" stands for "Body Part" and the values I can specify for BP_VAR are Arm, Leg and Head.

 

Inside the macro, there is a datastep and inside it I had code like this.

 

If &BP_VAR = "Arm" then do;

... some statements...

end;

And there is similar code for Leg and Head. But it wouldn't work so I found a way around it, although if you have comments on why the above didn't work then I'd be interested to hear it.

 

So &BP_VAR takes on values Arm or Leg or Head. Thus you are testing if the value of &BP_VAR (let's assume it is Arm) is equal to the text string "Arm". In other words, when SAS executes, it is trying to execute

 

if Arm = "Arm" then do;

 

and this probably produces an error because you don't have a variable in your data set named Arm. I'm not sure what you are trying to do, so I can't produce code that will work for you here. Could you explain what you are trying to do?

 

As a wild guess, are you trying to test to see if the macro variable &BP_VAR has the value "Arm"??? This won't work either because even if &BP_VAR contains Arm, it doesn't match "Arm" because &BP_VAR doesn't contain double-quotes. And you would need to use %IF and not IF. Like this

 

%if &bp_var=Arm %then %do;

 

but maybe that's not what you want, I was guessing. So really, please explain what you are trying to do in words, not in code. Please explain the big picture of what you are trying to do.

--
Paige Miller
ballardw
Super User

@n6 wrote:

The code below isn't my exact code but it accurately illustrates the problem and is much simpler.

 

I have a macro named ABC with the argument BP_VAR.    "BP" stands for "Body Part" and the values I can specify for BP_VAR are Arm, Leg and Head.

 

Inside the macro, there is a datastep and inside it I had code like this.

 

If &BP_VAR = "Arm" then do;

    ... some statements...

end;

And there is similar code for Leg and Head.  But it wouldn't work so I found a way around it, although if you have comments on why the above didn't work then I'd be interested to hear it. 

Would be glad to comment but that would require 1) the complete definition of the macro 2) the actual call to the macro and 3) likely some example data along with 4) a description of what "doesn't work" means.

Doesn't work is awful vague.

Are there errors in the log?: Post the code and log in a code box opened with the "</>" to maintain formatting of error messages.

No output? Post any log in a code box.

Unexpected output? Provide input data in the form of data step code pasted into a code box, the actual results and the expected results. Instructions here: https://communities.sas.com/t5/SAS-Communities-Library/How-to-create-a-data-step-version-of-your-dat... will show how to turn an existing SAS data set into data step code that can be pasted into a forum code box using the "</>" icon or attached as text to show exactly what you have and that we can test code against.

 

Format statements are not executable. They set a property of a variable. That means if you have multiple Format statements for the same variable in the code generally the LAST format statement in a step is used/ applied. Regardless of if a branch of code the format statement is in ever executes.

Brief example:

data junk;
   x=1;
   if x=1 then format x f5.;
   else format x date9.;
run;

 

You can conditionally USE a format with a PUT statement to assign a value to a variable

 

So maybe something like

if &BP = "Arm" then do;
     ...some assignment statements to create a new variable &BP.2;
    result = put(&BP.2, abc. );
end;

 

n6
Quartz | Level 8 n6
Quartz | Level 8

The macro code is below with the macro call below that.  The code is lined up and beautiful and easy to read in my program editor but it is hard to read below.  If there's a way to make it easier to read for you then please let me know.   

 

The first part of the macro is a proc freq, then a proc logistic that creates a dataset, but you can skip past that and go to the subsequent data step, where is uses that dataset and which is where the problem is.

 

In my output window, the proc print at the end shows that it's giving the wrong result.  In the real macro there is yet more code below that but that's not the point, so I just put %MEND; right after the proc print.  And there is no error in my log, although I can post it if you like, but I'm trying not to overwhelm you.  My code runs without error but it just gives a result that I don't understand.

 

The chunk from "TAKE CARE OF X_VAR - START" to "TAKE CARE OF X_VAR - END" is where the problem is so you don't have to look at the stuff after that.  The 2nd argument in the macro is x_var.   The argument in the macro call that corresponds to that is NatInj_Hand.  The if-then-do loop for NatInj_Hand comes first and creates a variable named NatInj_Hand_v and assigns a format to it. 

 

But in the proc print at the end, the variable NatInj_v has a different format assigned to it.  It has the format assigned in the if-then-do loop for Sector, which is the last of the three.  Or if I comment that format assignment out, then it has the one for MineAct_Hand, which is the second of the three.  Or if I comment both of those format assignments out, only then does it have the appropriate format assignment of the on in the NatInj_Hand if-then-do loop.

 

I hope this isn't too much.  If there's anything I can do to make it easier or any questions I can answer then let me know.

 

%MACRO IMP (bodypart, x_var, x_var_ref, x_levels, group_var, group_var_ref);
options mprint;
proc freq data=AI_Use;
tables &BODYPART.Inj * &X_VAR * &GROUP_VAR / nopct norow;
run;

ods select ModelInfo NObs ResponseProfile;
proc logistic data=AI_Use;
title "THIS HAS COVARIABLES &X_VAR AND &GROUP_VAR";
class &X_VAR (ref="&X_VAR_REF") &GROUP_VAR (ref="&GROUP_VAR_REF") / param=ref;
model &BODYPART.Inj = &X_VAR &GROUP_VAR
&X_VAR * &GROUP_VAR
/ clodds = wald;
oddsratio &X_VAR;
ods output OddsRatiosWald = &BODYPART._&X_VAR._&GROUP_VAR;
run;
proc print data=&BODYPART._&X_VAR._&GROUP_VAR; title "&BODYPART._&X_VAR._&GROUP_VAR"; run;

data &BODYPART._&X_VAR._&GROUP_VAR.2;
set &BODYPART._&X_VAR._&GROUP_VAR;

*********************************************************************************;
************************* TAKE CARE OF X_VAR -- START *************************;
*********************************************************************************;
%LET X = "&X_VAR";

if &X = "NatInj_Hand" then do;
if find (Effect, "&X_VAR_REF") ne 0 then do;
if find (Effect, 'Amputation/Enucleation' ) ne 0 then &X_VAR._v = 1;
if find (Effect, 'Burns' ) ne 0 then &X_VAR._v = 2;
if find (Effect, 'Crushing' ) ne 0 then &X_VAR._v = 3;
if find (Effect, 'Cut/Laceration/Puncture') ne 0 then &X_VAR._v = 4;
if find (Effect, 'Fracture/Chip' ) ne 0 then &X_VAR._v = 5;
if find (Effect, 'Other injury' ) ne 0 then &X_VAR._v = 6;
if find (Effect, 'Sprain/Strains' ) ne 0 then &X_VAR._v = 7;
* if find (Effect, 'Contusion/Bruise' ) ne 0 then &X_VAR._v = 8;
label &X_VAR._v = 'Nature of Injury';
format &X_VAR._v NatInj_Hand.;
end;
end;

if &X = "MineAct_Hand" then do;
if find (Effect, "&X_VAR_REF") ne 0 then do;
if find (Effect, 'Operating Equipment' ) ne 0 then &X_VAR._v = 1;
if find (Effect, 'Handling Material' ) ne 0 then &X_VAR._v = 2;
if find (Effect, 'Welding/Grinding' ) ne 0 then &X_VAR._v = 3;
if find (Effect, 'Machine Maint./Repair)' ) ne 0 then &X_VAR._v = 4;
if find (Effect, 'Hand tools (Not Powered)') ne 0 then &X_VAR._v = 5;
if find (Effect, 'Hand tools (Powered)' ) ne 0 then &X_VAR._v = 6;
if find (Effect, 'Walking/Running' ) ne 0 then &X_VAR._v = 7;
if find (Effect, 'Other' ) ne 0 then &X_VAR._v = 8;
* if find (Effect, 'Roofbolting' ) ne 0 then &X_VAR._v = 9;
label &X_VAR._v = 'Mine Worker Activity';
format &X_VAR._v MineAct_Hand.;
end;
end;

if &X = "Sector" then do;
if find (Effect, "&X_VAR_REF") ne 0 then do;
if find (Effect, '1 - Coal' ) ne 0 then &X_VAR._v = 1;
if find (Effect, '5 - Sand/Gravel') ne 0 then &X_VAR._v = 2;
if find (Effect, '6 - Stone' ) ne 0 then &X_VAR._v = 3;
if find (Effect, '8 - Metal' ) ne 0 then &X_VAR._v = 4;
* if find (Effect, '7 - Nonmetal' ) ne 0 then &X_VAR._v = 5;
label &X_VAR._v = 'Sector';
format &X_VAR._v Sector.;
end;
end;
*********************************************************************************;
************************* TAKE CARE OF X_VAR -- END *************************;
*********************************************************************************;

*********************************************************************************;
*********************** TAKE CARE OF GROUP_VAR -- START ************************;
*********************************************************************************;
%LET GROUP = "&GROUP_VAR";

if &GROUP = "SubUnit3" then do;
if find (Effect, 'A-Underground' ) ne 0 then &GROUP_VAR._v = 1;
if find (Effect, 'C-Mills/Prep Plants') ne 0 then &GROUP_VAR._v = 2;
if find (Effect, 'B-Surface' ) ne 0 then &GROUP_VAR._v = 3;
label &GROUP_VAR._v = 'SubUnit3';
format &GROUP_VAR._v SubUnit.;
end;

else if &GROUP = "Sector" then do;
if find (Effect, "&X_VAR_REF") ne 0 then do;
if find (Effect, '1 - Coal' ) ne 0 then &GROUP_VAR._v = 1;
if find (Effect, '5 - Sand/Gravel') ne 0 then &GROUP_VAR._v = 2;
if find (Effect, '6 - Stone' ) ne 0 then &GROUP_VAR._v = 3;
if find (Effect, '8 - Metal' ) ne 0 then &GROUP_VAR._v = 4;
if find (Effect, '7 - Nonmetal' ) ne 0 then &GROUP_VAR._v = 5;
label &GROUP_VAR._v = 'Sector';
format &GROUP_VAR._v Sector.;
end;
end;
*********************************************************************************;
*********************** TAKE CARE OF GROUP_VAR -- END ************************;
*********************************************************************************;

if &X_VAR._v ne . and &GROUP_VAR._v ne . then output;
run;

proc print data=&BODYPART._&X_VAR._&GROUP_VAR.2;
title "ODDS RATIO OF HAVING A &BODYPART INJURY AS OPPOSED TO A NON-&BODYPART INJURY";
var &X_VAR._v &GROUP_VAR._v OddsRatioEst LowerCL UpperCL;
run;

%MEND;

 

Here is the macro call.

 

%IMP (Hand, NatInj_Hand , Contusion/Bruise, 7, SubUnit3, B-Surface );

Tom
Super User Tom
Super User

Use the tools of the forum editor.  Either Insert Code or Insert SAS Code to get a pop-up window to enter/edit your text.

Tom_0-1718150387405.png

 

Tom
Super User Tom
Super User

Do not try to execute FORMAT statement conditionally.  There is nothing to execute.  The FORMAT statement just defines what format is attached to the variables.  The variables are all defined before the step actually executes anything.  Move them outside of the IF/THEN/DO block so you don't confuse yourself or other programmers.

 

If you want to conditional GENERATE a FORMAT statement you need to use MACRO CODE.

Quentin
Super User

Thanks for posting your full macro.  There are a few different issues going on, both common when learning the macro language.  One is that you're using a DATA step IF statement when you probably want a macro %IF statement.  The other (not actually related to macro language) is that you're trying to use a DATA step IF statement to conditionally execute a FORMAT statement, but as others have pointed out the FORMAT statement works during DATA step compile time, so it won't work.

 

I would back up (a lot) from your big macro, and make a toy macro to explore the %IF statement, passing parameters, etc.  Here's a toy macro:

 

%macro try(bodypart,letter) ;
  data want ;
    set sashelp.class ;
    %if &bodypart=arm %then %do ;
      if find(name,"&letter") ne 0 then age2=age**2 ;
      format height 8.3 ;
    %end ;
    %else %if &bodypart=leg %then %do ;
      if find(name,"&letter") ne 0 then age3=age**3 ;
      format height 8.5 ;
    %end ;
  run ;

  proc print data=want ;
  run ;

%mend try ;

You can test it running code like:

options mprint ;
%try(arm,a)
%try(leg,e)
%try(stomach,i)

Of note, it uses a macro %IF statement to check the value of the macro variable &BodyPart.  Based on the value of that macro variable, it will then either generate this block of SAS code:

      if find(name,"&letter") ne 0 then age2=age**2 ;
      format height 8.3 ;

or this block:

      if find(name,"&letter") ne 0 then age3=age**3 ;
      format height 8.5 ;

or will generate no SAS code.

 

Before running the macro, make sure that you turn on the MPRINT option.  After running the macro, read the log to review the SAS code that the macro generated.

 

If you get stuck, feel free to post follow-up questions.  But it will be easier to work through if you make a small example macro (and perhaps small example input data) to illustrate the central issues.

BASUG is hosting free webinars Next up: Mike Raithel presenting on validating data files on Wednesday July 17. Register now at the Boston Area SAS Users Group event page: https://www.basug.org/events.
n6
Quartz | Level 8 n6
Quartz | Level 8

That did it.  My root problem is that I didn't know that format assignments were just done in datastep without regard to whether they were in a if-then-do loop.

 

Also, I was trying to use code like

 

if &X_VAR = "NatInj_Hand" then do;

 

when instead I should have been using code like

 

%if &X_VAR = NatInj_Hand %then %do

 

So that's that.  Although I've done a lot of macro coding, I haven't used %if and %then much so that was my problem, as well as the aforementioned fact that I didn't know that formats were assigned even if they were in a do loop.

 

Thanks to all for the help.

Tom
Super User Tom
Super User

This block of code is just asking for the use of an INFORMAT.

 

if &X = "NatInj_Hand" then do;
if find (Effect, "&X_VAR_REF") ne 0 then do;
if find (Effect, 'Amputation/Enucleation' ) ne 0 then &X_VAR._v = 1;
if find (Effect, 'Burns' ) ne 0 then &X_VAR._v = 2;
if find (Effect, 'Crushing' ) ne 0 then &X_VAR._v = 3;
if find (Effect, 'Cut/Laceration/Puncture') ne 0 then &X_VAR._v = 4;
if find (Effect, 'Fracture/Chip' ) ne 0 then &X_VAR._v = 5;
if find (Effect, 'Other injury' ) ne 0 then &X_VAR._v = 6;
if find (Effect, 'Sprain/Strains' ) ne 0 then &X_VAR._v = 7;
* if find (Effect, 'Contusion/Bruise' ) ne 0 then &X_VAR._v = 8;
label &X_VAR._v = 'Nature of Injury';
format &X_VAR._v NatInj_Hand.;
end;

Perhaps something like:

 

 

proc format ;
  informat NatInj_Hand
    'Amputation/Enucleation' = 1
   'Burns' = 2
   ...
  ;
run;

So that you could use a statement like this to set the value of NatInj_Hand_V to the number that the NatInj_Hand. informat generates from the value of EFFECT.

 

 

 

&X_VAR._v = input(effect, &X_VAR..);

 

Alternatively it might just be easier to remove the format from the NatInj_Hand variable before running the logistic so that the raw numeric value that the NatInj_Hand. format that you later tries to attached to the newly generated NatInj_Hand_V variable make it into the EFFECT variable of the output.

 

 

n6
Quartz | Level 8 n6
Quartz | Level 8

I'm not sure what you're saying about informats but I have the following proc format earlier in the program that defines the formats that I refer to in the macro.  Since the macro argument I pass is NatInj_Hand, I should see the words below for that format, but instead I see words for the other formats (or numbers when it runs out of what it thinks should be the formats).

 

Also note that the NatInj_Hand variable exists before I call the macro and it is a binary variable that I use as the response variable in the proc logistic.  The newly created variable in the macro is NatInj_Hand_v and it contains values 1 thru 8 and it is supposed to have the formats for NatInj_Hand below, but the proc print at the end of my macro shows that it does not, which is the problem.  I'm pretty sure I could get around this problem by being inelegant and if I can't figure it out much longer I may do that, but for now, I'll keep trying.  The code I attached has three variables for now in the "X_VAR" section but if I can fix my current problem it's going to have a lot more.

 

proc format;
value NatInj_Hand
1 = 'Amputation'
2 = 'Burns'
3 = 'Crushing'
4 = 'Lac/Punc'
5 = 'Frac/Chip'
6 = 'Other inj'
7 = 'Sprain/Strain'
8 = 'Contusion/Bruise'
;
1 = 'Op Equip'
2 = 'Handling'
3 = 'Weld/Grind'
4 = 'Mach Maint'
5 = 'Hand (No Pow)'
6 = 'Hand (Power)'
7 = 'Walk/Run'
8 = 'Other'
9 = 'Roofbolt'
;
value SubUnit
1 = 'Underground'
2 = 'Mill / Prep Plant'
3 = 'Surface'
;
value Sector
1 = 'Coal'
2 = 'Sand/Gravel'
3 = 'Stone'
4 = 'Metal'
5 = 'NonMetal'
;
run;

Ready to join fellow brilliant minds for the SAS Hackathon?

Build your skills. Make connections. Enjoy creative freedom. Maybe change the world. Registration is now open through August 30th. Visit the SAS Hackathon homepage.

Register today!
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
  • 9 replies
  • 533 views
  • 4 likes
  • 5 in conversation