sas >> Using Macro Code within a DATA Step Select Statement

by jack_clark » Wed, 01 Dec 2004 00:36:50 GMT

I am trying to use a macro program to dynamically create the WHEN clauses
in a DATA Step SELECT Statement. The code I am trying to create looks like
this:

data whatever;
set something;

select (cat);
when ("G") do;
vg = 1;
select;
when (decile = '0') vg0 = 1;
when (decile = '1') vg1 = 1;
when (decile = '2') vg2 = 1;
when (decile = '3') vg3 = 1;
when (decile = '4') vg4 = 1;
when (decile = '5') vg5 = 1;
when (decile = '6') vg6 = 1;
when (decile = '7') vg7 = 1;
when (decile = '8') vg8 = 1;
when (decile = '9') vg9 = 1;
end;
end;
when ("H") do;
vh = 1;
select;
when (decile = '0') vh0 = 1;
when (decile = '1') vh1 = 1;
when (decile = '2') vh2 = 1;
when (decile = '3') vh3 = 1;
when (decile = '4') vh4 = 1;
when (decile = '5') vh5 = 1;
when (decile = '6') vh6 = 1;
when (decile = '7') vh7 = 1;
when (decile = '8') vh8 = 1;
when (decile = '9') vh9 = 1;
end;
end;
when ("I") do;
vi = 1;
select;
when (decile = '0') vi0 = 1;
when (decile = '1') vi1 = 1;
when (decile = '2') vi2 = 1;
when (decile = '3') vi3 = 1;
when (decile = '4') vi4 = 1;
when (decile = '5') vi5 = 1;
when (decile = '6') vi6 = 1;
when (decile = '7') vi7 = 1;
when (decile = '8') vi8 = 1;
when (decile = '9') vi9 = 1;
end;
end;

end;
run;

I used the following macro program to cut down on lines of code:

data whatever;
set something;

%macro catdec (lettr);
when ("&lettr") do;
v&lettr = 1;
select;
%do xcnt = 0 %to 9;
when (decile = "&xcnt") v&lettr.&xcnt = 1;
%end;
end;
end;
%mend catdec;

%catdec (G);
%catdec (H);
%catdec (I);

end;
run;

The job runs properly when I type out the code, but when I switch to using
the macro, I get an error in the log:

1600 %catdec (G);
MPRINT(CATDEC): when ("G") do;
MPRINT(CATDEC): vG = 1;
MPRINT(CATDEC): select;
MPRINT(CATDEC): when (decile = "0") vG0 = 1;
MPRINT(CATDEC): when (decile = "1") vG1 = 1;
MPRINT(CATDEC): when (decile = "2") vG2 = 1;
MPRINT(CATDEC): when (decile = "3") vG3 = 1;
MPRINT(CATDEC): when (decile = "4") vG4 = 1;
MPRINT(CATDEC): when (decile = "5") vG5 = 1;
MPRINT(CATDEC): when (decile = "6") vG6 = 1;
MPRINT(CATDEC): when (decile = "7") vG7 = 1;
MPRINT(CATDEC): when (decile = "8") vG8 = 1;
MPRINT(CATDEC): when (decile = "9") vG9 = 1;
MPRINT(CATDEC): end;
MPRINT(CATDEC): end;
ERROR: Expecting "WHEN", "OTHERWISE", or "END".

The MPRINT logic seems to show the code generated by the macro is the same
as what I am trying to create, but obviously there is a problem. I think
it may have something to do with the SELECT statement not wanting to see
the macro call when it is expecting "WHEN", "OTHERWISE" or "END", but I
thought the ampersand in front of the macro call would alleviate this
problem?

Any suggestions would be appreciated. Thank you.


sas >> Using Macro Code within a DATA Step Select Statement

by diskin » Wed, 01 Dec 2004 00:57:42 GMT


Jack,

Just take the semicolons off of the macro call lines:
%catdec (G)
%catdec (H)
%catdec (I)

Beacuse the macro calls do not need terminating semicolons, the semicolons are interperted as NULL statements which do not fit in the SELET structure.

HTH,
Dennis Diskin



I am trying to use a macro program to dynamically create the WHEN clauses
in a DATA Step SELECT Statement. The code I am trying to create looks like
this:

data whatever;
set something;

select (cat);
when ("G") do;
vg = 1;
select;
when (decile = '0') vg0 = 1;
when (decile = '1') vg1 = 1;
when (decile = '2') vg2 = 1;
when (decile = '3') vg3 = 1;
when (decile = '4') vg4 = 1;
when (decile = '5') vg5 = 1;
when (decile = '6') vg6 = 1;
when (decile = '7') vg7 = 1;
when (decile = '8') vg8 = 1;
when (decile = '9') vg9 = 1;
end;
end;
when ("H") do;
vh = 1;
select;
when (decile = '0') vh0 = 1;
when (decile = '1') vh1 = 1;
when (decile = '2') vh2 = 1;
when (decile = '3') vh3 = 1;
when (decile = '4') vh4 = 1;
when (decile = '5') vh5 = 1;
when (decile = '6') vh6 = 1;
when (decile = '7') vh7 = 1;
when (decile = '8') vh8 = 1;
when (decile = '9') vh9 = 1;
end;
end;
when ("I") do;
vi = 1;
select;
when (decile = '0') vi0 = 1;
when (decile = '1') vi1 = 1;
when (decile = '2') vi2 = 1;
when (decile = '3') vi3 = 1;
when (decile = '4') vi4 = 1;
when (decile = '5') vi5 = 1;
when (decile = '6') vi6 = 1;
when (decile = '7') vi7 = 1;
when (decile = '8') vi8 = 1;
when (decile = '9') vi9 = 1;
end;
end;

end;
run;

I used the following macro program to cut down on lines of code:

data whatever;
set something;

%macro catdec (lettr);
when ("&lettr") do;
v&lettr = 1;
select;
%do xcnt = 0 %to 9;
when (decile = "&xcnt") v&lettr.&xcnt = 1;
%end;
end;
end;
%mend catdec;

%catdec (G);
%catdec (H);
%catdec (I);

end;
run;

The job runs properly when I type out the code, but when I switch to using
the macro, I get an error in the log:

1600 %catdec (G);
MPRINT(CATDEC): when ("G") do;
MPRINT(CATDEC): vG = 1;
MPRINT(CATDEC): select;
MPRINT(CATDEC): when (decile = "0") vG0 = 1;
MPRINT(CATDEC): when (decile = "1") vG1 = 1;
MPRINT(CATDEC): when (decile = "2") vG2 = 1;
MPRINT(CATDEC): when (decile = "3") vG3 = 1;
MPRINT(CATDEC): when (decile = "4") vG4 = 1;
MPRINT(CATDEC): when (decile = "5") vG5 = 1;
MPRINT(CATDEC): when (decile = "6") vG6 = 1;
MPRINT(CATDEC): when (decile = "7") vG7 = 1;
MPRINT(CATDEC): when (decile = "8") vG8 = 1;
MPRINT(CATDEC): when (decile = "9") vG9 = 1;
MPRINT(CATDEC): end;
MPRINT(CATDEC): end;
ERROR: Expecting "WHEN", "OTHERWISE", or "END".

The MPRINT logic seems to show the code generated by the macro is the same
as what I am trying to create, but obviously there is a problem. I think
it may have something to do with the SELECT statement not wanting to see
the macro call when it is expecting "WHEN", "OTHERWISE" or "END", but I
thought the ampersand in front of the macro call would alleviate this
problem?

Any suggestions would be appreciated. Thank you.



sas >> Using Macro Code within a DATA Step Select Statement

by jack_clark » Wed, 01 Dec 2004 01:08:12 GMT

Thank you Dennis. This solved my problem.


Using Macro Code within a DATA Step Select Statement

by chang_y_chung » Wed, 01 Dec 2004 02:29:17 GMT

n Tue, 30 Nov 2004 11:36:50 -0500, Jack Clark <jack_clark@HARTE-
HANKS.COM> wrote:

like

Hi, Jack,

Another way of avoiding this "wall-paper" code is to use arrays and loops.
I tried this way and I have to admit that it does not look any simpler
than your macro codes. Still, it does some more than your code: it sets
variables either 1, 0, or missing depending on the values of cat and
decile with appropriate notes on log. HTH.

Cheers,
Chang

/* test data -- with some missings */
data one(drop=_:);
do _cat = 'G', 'H', 'I';
do _decile = 0 to 9;
cat = _cat;
decile = put(_decile, 1.);
if _cat = 'G' and decile = 1 then do;
cat = '';
end; else if _cat = 'G' and decile = 2 then do;
decile = '';
end;
output;
end;
end;
run;

/* create dummy variables */
data two(drop=_:);

set one;

array vxs[0:2,0:9] vg0-vg9 vh0-vh9 vi0-vi9 (30*0);
array vs[0:2] vg vh vi (3*0);

/* init */
if not _done then do;
do _cat = 0 to 2;
vs[_cat] = .;
do _var = 0 to 9;
vxs[_cat, _var] = .;
end;
end;
end;

/* check */
if missing(cat) then do;
put "NOTE: missing cat. obs no=" _n_;
goto next;
end;

/* big loop */

_done = 0; /* false */
_i = -1;

do _catValue = "G", "H", "I" until (_done);
_i = _i + 1;
if cat = _catValue then do;
do _cat = 0 to 2;
vs[_cat] = (_cat = _i);
if missing(decile) then do;
put "NOTE: missing decile. obs no=" _n_;
goto next;
end;
do _var = 0 to 9;
vxs[_cat, _var] = (
vs[_cat] and input(decile,?? best.) = _var
);
end;
end;
_done = 1; /* true */
end;
end;

next:;
run;

/* check */
proc print data=two noobs;
var cat decile vg vh vi vg0 vg1 vh0 vh1 vi0 vi1;
run;
/* on log
NOTE: missing cat. obs no=2
NOTE: missing decile. obs no=3
*/
/* on lst
cat decile vg vh vi vg0 vg1 vh0 vh1 vi0 vi1

G 0 1 0 0 1 0 0 0 0 0
1 . . . . . . . . .
G 1 . . . . . . . .
G 3 1 0 0 0 0 0 0 0 0
G 4 1 0 0 0 0 0 0 0 0
G 5 1 0 0 0 0 0 0 0 0
G 6 1 0 0 0 0 0 0 0 0
G 7 1 0 0 0 0 0 0 0 0
G 8 1 0 0 0 0 0 0 0 0
G 9 1 0 0 0 0 0 0 0 0
H 0 0 1 0 0 0 1 0 0 0
H 1 0 1 0 0 0 0 1 0 0
H 2 0 1 0 0 0 0 0 0 0
H 3 0 1 0 0 0 0 0 0 0
H 4 0 1 0 0 0 0 0 0 0
H 5 0 1 0 0 0 0 0 0 0
H 6 0 1 0 0 0 0 0 0 0
H 7 0 1 0 0 0 0 0 0 0
H 8 0 1 0 0 0 0 0 0 0


Using Macro Code within a DATA Step Select Statement

by jack_clark » Wed, 01 Dec 2004 03:15:48 GMT

David,

I tried to trim a lot of extraneous stuff from the code I posted to the
list. If we use what was posted as the example...

I have a variable (cat) with 3 possible values ('G', 'H', 'I'). The decile
variable contains values 0-9.

The reason why I am coding all of the dummy variables is because these are
fields that are required on a client report. I am using a PROC SUMMARY
(with a couple of CLASS variables) to sum the dummy variables once coded.
The report is to show the following columns:

# of records where cat=G
# of records where cat=G and Decile = 0
# of records where cat=G and Decile = 1
etc.

# of records where cat=H
# of records where cat=H and Decile = 0
# of records where cat=H and Decile = 1
etc.

# of records where cat=I
# of records where cat=I and Decile = 0
# of records where cat=I and Decile = 1
etc.

If you have a better suggestion, I am certainly interested. FYI - we do
not have SAS STAT on the operating system where this production job will be
run. Thanks.

Jack Clark


Similar Threads

1. select statement in data step

I have code like this
if (a='0001' and b='001') or (a='0002 and b='002') then c='sweet';
how can I code by using select and when in a datastep?

Xiao

2. difficulty referencing global variable in macro within a data step

3. Using variable value in data step output statement

Earlier, today, someone asked about building multiple files based on one
of the table's values.

Yes, I'm quite aware that the practice isn't as efficient as it seems, but
I found it interesting that the suggested solutions had to revert to
macros and hashes.

Not that those aren't good suggestions, but I'd think that there would be
an easier to understand solution.

The last step of the following doesn't work, as I can't remember if there
is a way to specify the needed values in the output statement.

Can anyone remind me about what I've probably forgotten?

*build some test data;
data have;
  input code   DATE yymmdd8.  CT;
  cards;
600000 20050105    21
600000 20050105    8
600000 20050105    61
600000 20050606   257
600000 20050606   103
600000 20051207   3491
600000 20051207   43
600000 20051207   873
;

*create a macro variable to use in the data statement;
proc sql noprint;
  select distinct 'F'||put(date,date9.) into :dates
    separated by ' '
      from have
;
quit;

*create the output files -- this is the part that doesn't work!;
data &dates.;
  set have;
  output cat('F',put(date,date9.));
run;

Thanks in advance,
Art

4. How to declare macro variable within datastep and using it within

5. data step code used by Proc import

Hello:

I was using Proc Import to import a csv file. The importing process was
unsuccessful due to data errors. Now I'd like to modify the data step
code generated by proc import, so that I can use it to import the file
with no errors.

How do I capture the data step code used by proc import?
I know I can use proc printto to route the log to an external file, but
every line of the data step code is preceded by a number.  Is there
anyway to get rid of those preceding line numbers?

Thanks.

6. Problem that Data Step Statements doesn't work after a Macro

7. Problem that Data Step Statements doesn't work after a Macro execution

Hi, all SAS-Ls

I have made a Macro to manupilate _infile_ variable from input buffer,
which intends to read in a raw data that is sperated by a specific
delimiter but a record of it may be broken into lines due to line feed
or carriage return characters contained in a field value.
Data sample as below:

                 1|2|3|4 4
                  4
                 |5|6 6
                 2|2|3|4 4 4|5|6
                  6
                 3|2|3|4 4 4|5|6 6

It should appear like below in table,

                 1|2|3|4 4(LF or CR) 4(LF or CR)|5|6 6
                 2|2|3|4 4 4|5|6(LF or CR) 6
                 3|2|3|4 4 4|5|6 6

but the LF or CR cause the raw data broken into lines.
The Macro I made below is to solve this situation.

                %Macro BLRDR(dlm,dlm_n,span_cut);
                       format tmp_infile_line $32767.;
                       informat tmp_infile_line $32767.;
                       retain tmp_infile_line;
                       input @;
                       do while (&dlm_n - count(trimn
(tmp_infile_line),"&dlm") >= &span_cut.);
                                tmp_infile_line = trimn
(tmp_infile_line)||_infile_;
                                input;
                                input @;
                                if &dlm_n - count(trimn
(tmp_infile_line)||_infile_,"&dlm") < &span_cut. then do;
                                           input @@;
                                           _infile_ = tmp_infile_line;
                                           tmp_infile_line = "";
                                end;
                       end;
                       *drop tmp_infile_line;
                 %mend;

&dlm: specify the delimiter;
&dlm_n: indicate the delimiter number in one complete record
&span_cut: set a broken line without &dlm (like " 6" between line4 and
line6 in the data sample) as partial value of last field of last
record or first field of next record. 0 as last field of last record;1
as belonging to first field of next record. For example, when
&span_cut = o then line4-6 records will be read as
                 2|2|3|4 4 4|5|6 6
                 3|2|3|4 4 4|5|6 6
when &span_cut = 1 then line4-6 records will be read as
                 2|2|3|4 4 4|5|6
                  63|2|3|4 4 4|5|6 6

If it worked as what I expect, many of my broken raw data would be
read in correctly. However the result run out does not appear like
that.
I put this Macro into raw data reading in code.

                Data rst.test;
                      infile "C:\My SAS\SD.txt" dlm="|" dsd;
                      length n m l o p q $10.;
                      %BLRDR(|,5,1);
                      input n m l o p q;
                      put tmp_infile_line= n= m= l= o= p= q=;
                Run;

Logs as below after run,

61         Data rst.test;
62         	infile "C:\My SAS\SD.txt" dlm="|" dsd;
63         	length n m l o p q $10.;
64         	%BLRDR(|,5,1);
MLOGIC(BLRDR):  Beginning execution.
MLOGIC(BLRDR):  Parameter DLM has value |
MLOGIC(BLRDR):  Parameter DLM_N has value 5
MLOGIC(BLRDR):  Parameter SPAN_CUT has value 1
MPRINT(BLRDR):   format tmp_infile_line $32767.;
MPRINT(BLRDR):   informat tmp_infile_line $32767.;
MPRINT(BLRDR):   retain tmp_infile_line;
MPRINT(BLRDR):   input @;
SYMBOLGEN:  Macro variable DLM_N resolves to 5
SYMBOLGEN:  Macro variable DLM resolves to |
SYMBOLGEN:  Macro variable SPAN_CUT resolves to 1
MPRINT(BLRDR):   do while (5 - count(trimn(tmp_infile_line),"|") >=
1);
MPRINT(BLRDR):   tmp_infile_line = trimn(tmp_infile_line)||_infile_;
MPRINT(BLRDR):   input;
MPRINT(BLRDR):   input @;
SYMBOLGEN:  Macro variable DLM_N resolves to 5
SYMBOLGEN:  Macro variable DLM resolves to |
SYMBOLGEN:  Macro variable SPAN_CUT resolves to 1
MPRINT(BLRDR):   if 5 - count(trimn(tmp_infile_line)||_infile_,"|") <
1 then do;
MPRINT(BLRDR):   input @@;
MPRINT(BLRDR):   _infile_ = tmp_infile_line;
MPRINT(BLRDR):   tmp_infile_line = "";
MPRINT(BLRDR):   end;
MPRINT(BLRDR):   end;
MPRINT(BLRDR):   *drop tmp_infile_line;
MLOGIC(BLRDR):  Ending execution.
66         	input n m l o p q;
69         	put _infile_ tmp_infile_line= n= m= l= o= p= q=;
70         Run;

NOTE: The infile "C:\My SAS\SD.txt" is:
      File Name=C:\My SAS\SD.txt,
      RECFM=V,LRECL=256

NOTE: 6 records were read from the infile "C:\My SAS\SD.txt".
      The minimum record length was 2.
      The maximum record length was 17.
NOTE: The data set RST.TEST has 0 observations and 7 variables.


NO obs was read in! At first I guess that _infile_ turned missing just
before input statement. So I inserted some put statemnt into this
Macro and data steps. Codes inserted as below,

                %Macro BLRDR(dlm,dlm_n,span_cut);
                       format tmp_infile_line $32767.;
                       informat tmp_infile_line $32767.;
                       retain tmp_infile_line;
                       input @;
                       put _infile_;
                       do while (&dlm_n - count(trimn
(tmp_infile_line),"&dlm") >= &span_cut.);
                                tmp_infile_line = trimn
(tmp_infile_line)||_infile_;
                                input;
                                input @;
                                put _infile_;
                                if &dlm_n - count(trimn
(tmp_infile_line)||_infile_,"&dlm") < &span_cut. then do;
                                           input @@;
                                           put _infile_;
                                           _infile_ = tmp_infile_line;
                                           put _infile_;
                                           tmp_infile_line = "";
                                end;
                       end;
                       *drop tmp_infile_line;
                 %mend;

                Data rst.test;
                      infile "C:\My SAS\SD.txt" dlm="|" dsd;
                      length n m l o p q $10.;
                      %BLRDR(|,5,1);
                      put "######";
                      input n m l o p q;
                      put "######";
                      put _infile_;
                      put _infile_ tmp_infile_line= n= m= l= o= p= q=;
                Run;

And here the logs,

61         Data rst.test;
62         	infile "C:\My SAS\SD.txt" dlm="|" dsd;
63         	length n m l o p q $10.;
64         	%BLRDR(|,5,1);
MLOGIC(BLRDR):  Beginning execution.
MLOGIC(BLRDR):  Parameter DLM has value |
MLOGIC(BLRDR):  Parameter DLM_N has value 5
MLOGIC(BLRDR):  Parameter SPAN_CUT has value 1
MPRINT(BLRDR):   format tmp_infile_line $32767.;
MPRINT(BLRDR):   informat tmp_infile_line $32767.;
MPRINT(BLRDR):   retain tmp_infile_line;
MPRINT(BLRDR):   input @;
MPRINT(BLRDR):   put _infile_;
SYMBOLGEN:  Macro variable DLM_N resolves to 5
SYMBOLGEN:  Macro variable DLM resolves to |
SYMBOLGEN:  Macro variable SPAN_CUT resolves to 1
MPRINT(BLRDR):   do while (5 - count(trimn(tmp_infile_line),"|") >=
1);
MPRINT(BLRDR):   tmp_infile_line = trimn(tmp_infile_line)||_infile_;
MPRINT(BLRDR):   input;
MPRINT(BLRDR):   input @;
MPRINT(BLRDR):   put _infile_;
SYMBOLGEN:  Macro variable DLM_N resolves to 5
SYMBOLGEN:  Macro variable DLM resolves to |
SYMBOLGEN:  Macro variable SPAN_CUT resolves to 1
MPRINT(BLRDR):   if 5 - count(trimn(tmp_infile_line)||_infile_,"|") <
1 then do;
MPRINT(BLRDR):   input @@;
MPRINT(BLRDR):   put _infile_;
MPRINT(BLRDR):   _infile_ = tmp_infile_line;
MPRINT(BLRDR):   put _infile_;
MPRINT(BLRDR):   tmp_infile_line = "";
MPRINT(BLRDR):   end;
MPRINT(BLRDR):   end;
MPRINT(BLRDR):   *drop tmp_infile_line;
MLOGIC(BLRDR):  Ending execution.
65         	put "######";
66         	input n m l o p q;
67         	put "######";
68         	put _infile_;
69         	put _infile_ tmp_infile_line= n= m= l= o= p= q=;
70         Run;

NOTE: The infile "C:\My SAS\SD.txt" is:
      File Name=C:\My SAS\SD.txt,
      RECFM=V,LRECL=256

1|2|3|4 4
 4
|5|6 6
|5|6 6
1|2|3|4 4
4


2|2|3|4 4 4|5|6
2|2|3|4 4 4|5|6
1|2|3|4 4
4


 6
3|2|3|4 4 4|5|6 6
3|2|3|4 4 4|5|6 6
1|2|3|4 4 4
6
NOTE: 6 records were read from the infile "C:\My SAS\SD.txt".
      The minimum record length was 2.
      The maximum record length was 17.
NOTE: The data set RST.TEST has 0 observations and 7 variables.



We can see that no put into log after the Macro execution, which seems
the statements after the Macro doesn't work and no error msg here. It
confused me a lot. Is there anyone knowing why?

8. Macro analogue to data step continue statement?