If I use the last suggestion I would have to spell out 20 times or so the different indicators that I want to create. That's why I wanted to use the var_list macro.
Can you please explain why
do j=1 to countw('&&&var..', ',');
will only iterate until the first string? In the example that you provided B3 is still found if use the code above.
Try it yourself and see.
1 data _null_; 2 do j=1 to countw('&&&var..', ','); 3 put j=1; 4 end; 5 run; j=1 NOTE: DATA statement used (Total process time): real time 0.00 seconds cpu time 0.00 seconds
Try this to see WHY.
1 data _null_; 2 string = '&&&var..'; 3 put string=; 4 run; string=&&&var.. NOTE: DATA statement used (Total process time): real time 0.00 seconds cpu time 0.00 seconds
The macro processor IGNORES strings made using single quote characters instead of double quote characters.
You have to type the same information either way.
Better to have the lists in data
data dxgroups;
infile datalines truncover;
length varname $32 num 8 code $10 disease $50;
input varname disease $50. ;
do codenum=1 by 1 until(missing(code));
input code @ ;
if not missing(code) then output;
end;
cards;
hpb Hypertension
A1 A2 A3 A4
db Diabetes
B1 B2 B3
;
and use the data to generate the code.
For example you could use it to generate calls to %DXTEST() macro I showed.
filename code temp;
data _null_;
file code;
set dxgroups;
by varname notsorted;
if first.varname then put '%dxtest(' varname ',' @;
put code :$quote. @;
if last.varname then put ')';
run;
And then you can use %INCLUDE to execute that generated code where you want.
data want;
set have;
array var_[20] ;
%include code / source2;
drop i;
run;
But this should be okay then?
%let testA=("A1" "A2" "A3");
%let testB=("B1" "B2" "B3");
data NEED;
set HAVE;
array cond_testA {20} VAR_1-VAR_20;
do i=1 to dim (cond_testA);
do j=1 to countw('&testA.', ',');
if (cond_testA {i} in &testA.) then do;
testA=1;
end;
end;
end;
array cond_testB {20} VAR_1-VAR_20;
do i=1 to dim (cond_testB);
do j=1 to countw('&testB.', ',');
if (cond_testB {i} in &testB.) then do;
testB=1;
end;
end;
end;
run;
The problem only arises with &&&var..? Sorry I'm still confused. I'm coming from stata where it's super easy to loop through strings.
There is a huge difference to SAS macro processor between
do j=1 to countw('&testB.', ',');
and
do j=1 to countw("&testB.", ',');
And even if you changed to the second then SAS code that the macro processor would pass onto SAS to execute would look like:
do j=1 to countw("("B1" "B2" "B3")", ',');
if (cond_testB {i} in ("B1" "B2" "B3")) then do;
testB=1;
end;
Which not only is invalid syntax it does not make any sense.
Did you mean to do something like this?
testB=0;
do i=1 to dim(cond_testB);
if (cond_testB[i] in &testB.) then testB=1;
end;
If so then that is what the DO loops I showed are doing,
do i=1 to dim(cond_testB) until(testB);
testB= (cond_testB[i] in &testB.) ;
end;
but they stop looping as soon as there is a match.
So your code worked because the two mistakes (miss use of macro code and the extra unneeded DO loop) cancel each other out.
Note also that [ and ] are easier to type and easier to distinguish from ( and ) in the code than { and }.
data have;
row+1;
input (var_1-var_4) ($);
cards;
A1 C1 . .
D1 C2 B3 .
. . . .
;
data want;
set have;
array var_[4];
do i=1 to dim(var_) until(testA);
testA = var_[i] in ('A1' 'A2' 'A3');
end;
do i=1 to dim(var_) until(testB);
testB = var_[i] in ('B1' 'B2' 'B3');
end;
drop i;
run;
I will just use this code as you suggested. One question though. Why do you need until(testA)?
@trevand wrote:...
Why do you need until(testA)?
So it does not have to keep hunting after it already found the result.
Also if you don't stop when it finds a match you have to add extra code to prevent the later comparisons from overwriting the earlier positive result. And something to set the negatives results to zero instead of missing.
Great, thanks! I think I'm understanding it now. I will use the following code that you proposed and add %let for the codelists:
data have;
row+1;
input (var_1-var_4) ($);
cards;
A1 C1 . .
D1 C2 B3 .
. . . .
;
%let codesetA=('A1' 'A2' 'A3')
%let codesetB=('B1' 'B2' 'B3')
data want;
set have;
array var_[4];
do i=1 to dim(var_) until(testA);
testA = var_[i] in &codesetA. ;
end;
do i=1 to dim(var_) until(testB);
testB = var_[i] in &codesetB.;
end;
drop i;
run;
The code set though could have either "" or '', right? In other words
%let codesetA=('A1' 'A2' 'A3')
would be the same as
%let codesetA=("A1" "A2" "A3")
Yes. The regular SAS language does not care which character you use to make a string literal. It is just the macro processor that cares. The reason is so you can have a string with & or % characters in them that are NOT meant to be processed by the macro processor.
If you try to set company name to AT&T and you use double quotes then the macro processor will look for the value of the macro variable named T. So use single quotes instead.
company_name='AT&T';
It's finally time to hack! Remember to visit the SAS Hacker's Hub regularly for news and updates.
SAS' Charu Shankar shares her PROC SQL expertise by showing you how to master the WHERE clause using real winter weather data.
Find more tutorials on the SAS Users YouTube channel.