BookmarkSubscribeRSS Feed
glmsas
Calcite | Level 5
i'm stepping through a data step with 20 vars and i have a if / then condition that determines to call the macro or not.

if notdigit(prod,1) > 0 then
%sel(prod );

this code calls the macro every step through the data step. is there another way to conditionally process this?
10 REPLIES 10
sbb
Lapis Lazuli | Level 10 sbb
Lapis Lazuli | Level 10
It would be helpful to explain inclusively what you want to accomplish. Also, seeing all of your code helps, as well.

Scott Barry
SBBWorks, Inc.
glmsas
Calcite | Level 5
Basically, just checking each var to insure each col of the var is numeric and not blank or missing.

%macro sel(col);
do;
bad_dig=substr(&col,notdigit(&col,1),1);
if bad_dig ne '.' then
do;
bad_col=notdigit(&col,1);
bad_var="&col";
keep &col bad_var;
output;
end;
end;
%mend;

data ck;
infile error_ck obs =100 lrecl=732;
record_num=_n_;

input
@1 prod $9.
@10 cred $11.
@21 campaign $9.
@30 issuer_no $2.
@61 RMDR_CK_RQST_1_CT $4.
@75 RMDR_CK_RQST_2_CT $4.
@89 RMDR_CK_RQST_3_CT $4.
@103 BUSUNIT_01_NO $9.
@112 BUSUNIT_02_NO $9.
@121 BUSUNIT_03_NO $9.
@130 BUSUNIT_04_NO $9.
@139 BUSUNIT_05_NO $9.
@148 BUSUNIT_06_NO $9.
@157 BUSUNIT_07_NO $9.
@166 BUSUNIT_08_NO $9.
@175 BUSUNIT_09_NO $9.
@184 BUSUNIT_10_NO $9.
@293 BUSUNIT_01_DEC_PT $14.
@307 BUSUNIT_02_DEC_PT $14.
@321 BUSUNIT_03_DEC_PT $14.
@335 BUSUNIT_04_DEC_PT $14.
@349 BUSUNIT_05_DEC_PT $14.
@363 BUSUNIT_06_DEC_PT $14.
@377 BUSUNIT_07_DEC_PT $14.
@391 BUSUNIT_08_DEC_PT $14.
@405 BUSUNIT_09_DEC_PT $14.
@419 BUSUNIT_10_DEC_PT $14.
;
length bad_var $18;
keep record_num bad_dig;

if notdigit(prod,1)> 0 then
%sel(prod);

if notdigit(cred,1)> 0 then
%sel(Cred);

if notdigit(campaign,1)> 0 then do;
put campaign= prod=;
%sel(Campaign);
end;

if notdigit(issuer_no,1)> 0 then
%sel(Issuer_no);

if notdigit(RMDR_CK_RQST_1_CT,1)> 0 then
%sel(RMDR_CK_RQST_1_CT);

if notdigit(RMDR_CK_RQST_2_CT,1)> 0 then
%sel(RMDR_CK_RQST_2_CT);

if notdigit(RMDR_CK_RQST_3_CT,1)> 0 then
%sel(RMDR_CK_RQST_3_CT);
........
Cynthia_sas
SAS Super FREQ
Hi:
The description of the NOTDIGIT function says that NOTDIGIT function searches a string for the first occurrence of any character that is not a digit. If such a character is found, NOTDIGIT returns the position in the string of that character. If no such character is found, NOTDIGIT returns a value of 0.

So, IF your character variable has all numbers, then the DO block will NOT be executed. However, more about this later.

In your original post, you said:

i'm stepping through a data step with 20 vars and i have a if / then condition that determines to call the macro or not.

if notdigit(prod,1) > 0 then
%sel(prod );

this code calls the macro every step through the data step. is there another way to conditionally process this?


This code does NOT call the macro program or not. Your macro code is NOT being executed conditionally...not exactly. There is a HUGE difference between a macro %IF/%DO and a regular DATA step program with a %mac invocation. When you have the kind of syntax that you show, essentially, at compile time, all your %sel(...) invocations are being expanded into code like this (shown for PROD and CRED variables). This expansion (or resolution) happens at code COMPILE time. So by the time your program executes, these are the complete sets of IF statements that SAS is actually executing or not executing:
[pre]
** for PROD section of code;
if notdigit(prod,1)> 0 then
do;
bad_dig=substr(prod,notdigit(prod,1),1);
if bad_dig ne '.' then
do;
bad_col=notdigit(prod,1);
bad_var="prod";
keep prod bad_var;
output;
end;
end;

** for CRED section of code;
if notdigit(cred,1)> 0 then
do;
bad_dig=substr(cred,notdigit(cred,1),1);
if bad_dig ne '.' then
do;
bad_col=notdigit(cred,1);
bad_var="cred";
keep cred bad_var;
output;
end;
end;
[/pre]

So by the time SAS executes these statements, the DO block contained in the %SEL macro program will be executed based on the NOTDIGIT test in the DATA step IF statement. So the resolved statements are only going to be executed AT ALL if the results of using the NOTDIGIT function return a result greater than 0.

That being the case, let's turn our attention to the NOTDIGIT function. If you are using the NOTDIGIT function on a character variable, unless your numbers fill the entire length of the variable, you might be surprised to find that you get different results for the value of the NOTDIGIT function, depending on whether you have trailing blanks in the character variable value (which is possible). Consider the following program:
[pre]
data _null_;
length string $20;
infile datalines;
input string $;

test1 = notdigit(trim(string),1);
test2 = notdigit(string,1);
put '=======>' string= test1= test2=;
return;
datalines;
11a1
2222
333z3
4444
12345678901234567890
;
run;
[/pre]

Note that I have given the STRING variable an explicit length of 20 characters, yet of my 5 observations, only the last observation completely uses up all 20 positions allocated to the variable length. The TEST1 variable uses the NOTDIGIT function with the TRIM function (to strip off trailing blanks (because a character variable that does not "fill" its length will be padded to the right with blanks). The TEST2 variable uses the NOTDIGIT function in the same way that you do in your program, without trimming or stripping out the trailing blanks. The SAS log shows different values for the TEST1 vs the TEST2 variable:
[pre]
SAS Log:
=======>string=11a1 test1=3 test2=3
=======>string=2222 test1=0 test2=5
=======>string=333z3 test1=4 test2=4
=======>string=4444 test1=0 test2=5
=======>string=12345678901234567890 test1=0 test2=0
[/pre]

As you can see from my results, shown in the log, that observation 2 (2222) and observation 4 (4444) in the untrimmed comparison show a value for TEST2 of 5 -- which would be the position of the first blank that pads the character variable value. So, that means it is possible for your NOTDIGIT function to return a value greater than 0 for your character variables, unless you are absolutely sure that your variable values completely fill up the length of the variable field.

Rather than focus on whether or not your macro code is being called conditionally, one thing to do would be to make your code more efficient. For example,by my count, your program is generating a call to the NOTDIGIT function possibly 3 times -- for EVERY variable you test. It would be more efficient to do execute the NOTDIGIT function one time, make a temporary variable and then test this temporary variable or use it in further function calls (like the SUBSTR function) -- something like this:

[pre]
notdig_val = notdigit(trim(prod),1); /* execute the NOTDIGIT function one time */
if notdig_val> 0 then
do;
bad_dig=substr(prod,notdig_val,1);
if bad_dig ne '.' then
do;
bad_col=notdig_val;
bad_var="prod";
keep prod bad_var;
output;
end;
end;
[/pre]

This change for efficiency purposes might mean that you need to redesign your macro program code a bit. Just remember that the macro processor is just acting like a big typewriter and that your %sel(...) macro program invocation is being resolved or expanded at code COMPILE time -- by the time your program executes, all the %sel(...) references are gone and have been replaced by the now complete DATA step IF statement. Your macro program is just saving you some tedious typing or repetitive statements for every variable you want to test.

cynthia
glmsas
Calcite | Level 5
Thanks Cynthia,
the data that i'm running this all on, is suppose, to have all the columns of the txt var with a digit in them, even if it's padded like 000012.

As far as the nodigit func, the code from above was still in flux and i wasn't really trying for efficient code at this point, just functionality. The macro is working but not quite like I wanted.

In the live data there should only be one or two records out of 10M, so the second nodigit func shouldn't run too many times.

Your explanation of the macro processing is spot on, and a great way to look at how SAS Macro's work.

thanks again
gary Message was edited by: glmsas
Cynthia_sas
SAS Super FREQ
Hi:
When you say: "The macro is working but not quite like I wanted."
it's hard to envision what you mean. The output dataset does not contain what you think it will contain? The wrong observations are getting written? You're getting messages in the log?

One way to ensure that your macro program WILL work the way you want is to start with a working SAS program that generates the desired results (yes, even if it means you have to hard-code 20 IF statements). If you refine your logic in a program that does NOT have any macro triggers or macro code, then when you "macro-ize" the working code you have a better chance of the resolved macro code working the way you intend.

The first thing we recommend to anyone who takes the Macro class is that you should NOT write or use a single & or % in your code until you have a program that produces exactly the output you want, as described in this paper:
http://www2.sas.com/proceedings/sugi28/056-28.pdf

cynthia
glmsas
Calcite | Level 5
Can anyone explain the reason for this error? the if-then-else works if i take it out of the macro and put it inline in the data step.


1283
1284 if notdigit(BUSUNIT_01_DEC_PT,1)> 0 then
1285 %sel(BUSUNIT_01_DEC_PT);
MPRINT(SEL): do;
MPRINT(SEL): bad_dig=substr(BUSUNIT_01_DEC_PT,notdigit(BUSUNIT_01_DEC_PT,1),1);
MPRINT(SEL): if notdigit(BUSUNIT_01_DEC_PT,1) ne 6 then do;
MPRINT(SEL): bad_col=notdigit(BUSUNIT_01_DEC_PT,1);
MPRINT(SEL): bad_var="BUSUNIT_01_DEC_PT";
MPRINT(SEL): keep BUSUNIT_01_DEC_PT bad_var;
MPRINT(SEL): output bad;
MPRINT(SEL): t=1;
MPRINT(SEL): end;
MPRINT(SEL): end;
1286 else if
----
160
ERROR 160-185: No matching IF-THEN clause.
Cynthia_sas
SAS Super FREQ
Do you have working code, with ELSE IF conditions (and WITHOUT any macro calls) that either generates the error or does not generate the error?? There is something about your generated code that is making the ELSE IF condition complain about not having a matching IF condition.

What I would recommend is to make a subset of data with just 3 columns and then hardcode the program with IF/THEN/ELSE statements and your NOTDIGIT test to see what statements work correctly and generate the desired results. Then, macro-ize that working program and make sure that the new program, with macro calls is still error free. There should be something different between the working "unmacro-ized" code and the code generated by your macro program that will point you in the right direction.

cynthia
ArtC
Rhodochrosite | Level 12
You have placed a semicolon after the macro call. This semicolon is eventually added to the second END, which is then END;; This null statement interrupts the IF-THEN/ELSE sequence thus isolating the ELSE. The solution is to call the macro without the semicolon.
sbb
Lapis Lazuli | Level 10 sbb
Lapis Lazuli | Level 10
We really need to see the rest of your DATA step which would precede the macro invocation - suggest you paste the entire SAS-generated log output as a post/reply, but after verifying the DATA step logic yourself, for an extra END; statement of some type, before your RUN; or the next PROC / DATA step.

Scott Barry
SBBWorks, Inc.
Flip
Fluorite | Level 6
Even if you fix this error your code will not work. You are trying to generate multiple "KEEP" statements. Only one may be used. Perhaps you are looking for an OUTPUT statement..

sas-innovate-2024.png

Don't miss out on SAS Innovate - Register now for the FREE Livestream!

Can't make it to Vegas? No problem! Watch our general sessions LIVE or on-demand starting April 17th. Hear from SAS execs, best-selling author Adam Grant, Hot Ones host Sean Evans, top tech journalist Kara Swisher, AI expert Cassie Kozyrkov, and the mind-blowing dance crew iLuminate! Plus, get access to over 20 breakout sessions.

 

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
  • 1424 views
  • 0 likes
  • 5 in conversation