Macro Array-Do

From sasCommunity
Jump to: navigation, search

This article describes a macro routine which is a follow-up to Fehd and Carpenter's List Processing Basics List_Processing_Basics_Creating_and_Using_Lists_of_Macro_Variables

predecessor: Making_Lists

keywords: using control data sets

Programs

Macro Array_Do

 /*       Name: array_do.sas   date: 2012-May-05 author: Ronald J. Fehd
                -------------------------------------------------------
Summary       : description  : make macro array and call macro with
                               all values in data set as parameters
                purpose      : provide generic method to call macro
                               using list==control data set of parms
                -------------------------------------------------------
Contexts      : program group: list processing macro call generator
                program  type: routine
                SAS      type: macro clause generator
                uses routines: macro named in parameter
                -------------------------------------------------------
Specifications: input  : required: Data, MacroName
                         optional: MacroParms
                process: make macro array, assemble macro-call, call
                output : from MacroName
-----------------------------------------------------------------------
Usage Example:
PROC Freq data   = sashelp.Class;
          tables   Sex
                 / noprint
             out =     Work.Freq_Class_Sex;
run;
%array_do(Data       = Work.Freq_Class_Sex
         ,MacroName  = Put note:
         ,MacroParms = %nrstr(data=sashelp.class,var=sex)
         )
log:
note:(data=sashelp.class,var=sex,Sex=F,COUNT=9,PERCENT=47.36842)
*** .................................... */
%MACRO array_do
      (data       = sashelp.class
      ,macroname  = put note:
      ,macroparms = /* %nrstr(data=sashelp.class,var=sex) */
      ,semicolon  = 0
      ,testing    = 0
      )
/ des = 'site: make macro array + do loop';
%local _Col        _ColName   _Row
       _Dim_List   _Dim_Names
       _Libname    _Memname
       _ListArrays _ListMvars _ListVars
       ;
%let Semicolon = %eval(   &Semicolon
                       or %scan(%upcase(&MacroName),1,%str( )) eq PUT);
%let Testing   = %eval(   &Testing
                       or(    %sysfunc(getoption(Mprint )) eq MPRINT
                          and %sysfunc(getoption(Source2)) eq SOURCE2
                      )  );
%let Data = %upcase(&Data);
%**  Data is.a two-level name? libref.data;
%if %index(&Data.,.) %then %do;
    %let _Libname = %scan(&Data.,1,.);
    %let _Memname = %scan(&Data.,2,.);
    %end;
%else %do;
    %***  Data is.a one-level name: (default=work.) data;
    %let _Libname = WORK;
    %let _Memname = &Data;
    %end;
 
%**  description: assertions;
%**  purpose    : if fail then exit;
%if  not(%sysfunc(exist(&Data))) %then %do;
     %put Err%str()or: &SysMacroname exiting: not exist(&Data);
     %return;
     %end;
%let _C_Dsid  = %sysfunc(open (&Data         ));
%let _C_Nobs  = %sysfunc(attrn(&_C_Dsid,Nobs ));
%let _C_Nvars = %sysfunc(attrn(&_C_Dsid,Nvars));
%if  not &_C_Nobs or not &_C_Nvars %then %do;
  %put Err%str()or: &SysMacroName &Data obs=&_C_Nobs vars=&_C_Nvars;
     %let _C_Rc = %sysfunc(close(&_C_Dsid));
     %return;
     %goto CloseExit;
     %end;
%else
 %put note: &SysMacroname. reading &Data. obs=&_C_Nobs vars=&_C_Nvars;
 
PROC SQL noprint;
         %** make mvar with dimension==Nrows of control data set;
         select Nobs
           into :_Dim_List
           from Dictionary.Tables
          where Libname eq "&_Libname"
            and Memname eq "&_Memname";
         %let _Dim_List = &_Dim_List;
         %if &Testing. %then %Put note: _Dim_List: &_Dim_List;
 
         %** make statements for macro array construction;
         select Name
               ,Name
               ,':' !! trim(Name) !! '1 - :'
                    !! trim(Name) !! "&_Dim_List"
         into :_ListNames  separated by  ' '
             ,:_ListVars   separated by ', '
             ,:_ListArrays separated by ', '
           from Dictionary.Columns
          where Libname eq "&_Libname"
            and Memname eq "&_Memname";
           %let _Dim_Names = &SQLObs;
         %if &Testing. %then %do;
             %Put note: Dim_Names: &_Dim_Names;
             %Put note: ListNames: &_ListNames;
             %Put note: ListVars: &_ListVars;
             %Put note: ListArrays: &_ListArrays;
             %end;
 
         %** make macro array(s);
         select &_ListVars
           into &_ListArrays
           from &Data.;
         quit;
 
%** loop over macro arrays: do Row = 1 to N;
%do _Row = 1 %to &_Dim_List.;
    %if   %length(&MacroParms) %then
          %let _ListParms = %unquote(&MacroParms),;
    %else %let _ListParms = ;
    %**  Col = A to Z;
    %do _Col = 1 %to &_Dim_Names;
        %let _ColName   = %scan(&_ListNames,&_Col,%str( ));
        %*** _ListParms = &_ListParms  ColA   =    ColA.RowN;
        %let _ListParms = &_ListParms&_ColName=&&&_ColName&_Row;
        %if &_Col ne &_Dim_Names %then
           %let _ListParms = &_ListParms,;
        %end;
    %if not &Semicolon %then
        %put note: &SysMacroName: &MacroName(&_ListParms);
    %&MacroName(&_ListParms)
    %if &Semicolon %then %do;
        ;
        %end;
    %end;
run;
%CloseExit:
%mend array_do;

Macro Array_Do-Test

 /*    name: <UNC>\SAS-site\macro-tests\array_do_test.sas
description: test program for macro array_do
    purpose: demo of list processing routine
author: RJF2 5/4/2012 
*************/    
options mprint source2;*testing==on;
*Include SiteMacr(array_do)/source2;
 
* initialize: make list::control.data.set; 
PROC Freq data = sashelp.Class;
          tables Sex
                 / list missing noprint
             out = Work.freq_class_sex;
run;
*testing: show macro-call;
%array_do(data = Work.freq_class_sex
         ,macroname = put note: PrintThis
         ,semicolon = 1
         );
 
*description: this is the processing routine;         
*purpose    : for each row of control.data.set, do something;
%Macro PrintThis(data = sashelp.class
                ,sex=
                ,count=
                ,percent=);
Proc Print data  = &data.
          (where = (Sex eq "&Sex"));
           title3 "&Data where Sex = &Sex";
           title3 "Count: &Count.";
           title4 "Percent: &Percent.";
run;
%mend;
 
options nomprint nosource2;*testing==off;
 
%array_do(data = Work.freq_class_sex
         ,macroname = PrintThis
         );

Log:

10         PROC Freq data = sashelp.Class;
11                   tables Sex
12                          / list missing noprint
13                      out = Work.freq_class_sex;
14         run;

NOTE: There were 19 observations read from the data set SASHELP.CLASS.
NOTE: The data set WORK.FREQ_CLASS_SEX has 2 observations and 3 variables.

15         *testing: show macro-call;
16         %array_do(data = Work.freq_class_sex
17                  ,macroname = put note: PrintThis
18                  ,semicolon = 1
19                  );
note: ARRAY_DO reading WORK.FREQ_CLASS_SEX obs=2 vars=3
MPRINT(ARRAY_DO):   PROC SQL noprint;
MPRINT(ARRAY_DO):   select Nobs into :_Dim_List 
                    from Dictionary.Tables 
                    where Libname eq "WORK" 
                      and Memname eq "FREQ_CLASS_SEX";
note: _Dim_List: 2

MPRINT(ARRAY_DO):   select Name 
                          ,Name 
                          ,':' !! trim(Name) !! '1 - :' !! trim(Name) !! "2" 
                    into :_ListNames separated by ' ' 
                        ,:_ListVars separated by ', ' 
                        ,:_ListArrays separated by ', ' 
                    from Dictionary.Columns 
                    where Libname eq "WORK" 
                      and Memname eq "FREQ_CLASS_SEX";
note: _Dim_Names: 3
note: ListNames: Sex COUNT PERCENT
note: ListVars: Sex, COUNT, PERCENT
note: ListArrays: :Sex1 - :Sex2, :COUNT1 - :COUNT2, :PERCENT1 - :PERCENT2

MPRINT(ARRAY_DO):   select Sex, COUNT, PERCENT 
                    into :Sex1 - :Sex2
                        ,:COUNT1 - :COUNT2
                        ,:PERCENT1 - :PERCENT2 
                    from WORK.FREQ_CLASS_SEX;
MPRINT(ARRAY_DO):   quit;
      

note: PrintThis(Sex=F,COUNT=9,PERCENT=47.36842)
note: PrintThis(Sex=M,COUNT=10,PERCENT=52.63158)
MPRINT(ARRAY_DO):   run;

36         %array_do(data = Work.freq_class_sex
37                  ,macroname = PrintThis
38                  );
note: ARRAY_DO reading WORK.FREQ_CLASS_SEX obs=2 vars=3

note: ARRAY_DO: PrintThis(Sex=F,COUNT=9,PERCENT=47.36842)
NOTE: There were 9 observations read from the data set SASHELP.CLASS.
      WHERE Sex='F';
NOTE: The PROCEDURE PRINT printed page 1.
      

note: ARRAY_DO: PrintThis(Sex=M,COUNT=10,PERCENT=52.63158)
NOTE: There were 10 observations read from the data set SASHELP.CLASS.
      WHERE Sex='M';
NOTE: The PROCEDURE PRINT printed page 2.

Listing:

sashelp.class where Sex = F
Count: 9
Percent: 47.36842

Obs    Name       Sex    Age    Height    Weight

  2    Alice       F      13     56.5       84.0
  3    Barbara     F      13     65.3       98.0
... 
 
sashelp.class where Sex = M
Count: 10
Percent: 52.63158

Obs    Name       Sex    Age    Height    Weight

  1    Alfred      M      14     69.0      112.5
  5    Henry       M      14     63.5      102.5
...

References

--Ronald_J._Fehd macro.maven == the radical programmer 20:57, 6 May 2012 (EDT)