Macro CallText

From sasCommunity
Jump to: navigation, search

Companion: Macro_CallMacr

Programs

CallText demo nrstr and unquote

This program shows the trick within calltext: provide the parameter value in macro function nrstr and return it -- resolve it -- using the macro function unquote.

*name       : calltext-demo-macro-statements.sas;
*description: demonstration of nrstr and unquote;
*purpose    : test suite for calltext;
 
%let sex = ?;
%let count = 0;
%let percent = 0;
%let text = %nrstr(tokens: sex: &sex count: &count percent: &percent);
%let sex = f;
%let count = 9;
%let percent = 47;
%put note: %unquote(&text);
%let sex = m;
%let count = 10;
%let percent = 52;
%put note: %unquote(&text);
%put;


Log:

note: tokens: sex: f count: 9 percent: 47
15         %let sex = m;
16         %let count = 10;
17         %let percent = 52;
18         %put note: %unquote(&text);
note: tokens: sex: m count: 10 percent: 52

CallText

note: published in MWSUG-2016, Tools of the Trade, paper TT06

 /* name: <UNC>\SAS-site\macros\calltext.sas
  author: Ronald J. Fehd 2012
          ----------------------------------------------------
Summary : description  : call text with specified
                         values in data set as parameters
          purpose      : provide generic method to call text
                         using list==control data set of parms
          ----------------------------------------------------
Contexts: program group: list processing token generator
          program  type: function
          SAS      type: macro assignment statement generator
          uses routines: n/a
          ----------------------------------------------------
Specifications: input  : required: data, text
                         optional: delimiter, hex16
                process: assemble macro variable assignment(s)
                         call
                output : from text
Parameters: data    = one- or two-level data set name
           ,text    = text of macro references for each row
      *-constraint-*: must be enclosed in nrstr:
           ,text    = %nrstr(%put note: name:&name sex=&sex;)
                      notes: may be any of:
                      * tokens
                      * statements including semicolons
                      * macro calls:
           ,text    = %nrstr(%mymacro(name=&name,sex=&sex))
           ,hex16   = 1 :: default, convert numeric to hex16
                      used to pass real numbers accurately
                           across step boundaries
           ,hex16   = 0 :: pass numerics as decimals
           ,testing = 0 :: default, no extra messages in log
           ,testing = 1 :: note: auto-reset when
                           options mprint source2;
           ,unquote = 1 :: default, write notes to log
           ,unquote = 0 :: do not   write notes to log
            --------------------------------------------------
bells, whistles: writes real time used to log
                 note: CALLTEXT used real time  0:00:00.016
-----------------------------------------------------------
usage example:
PROC freq data   = sashelp.class;
          tables   sex / noprint
             out = list_values;
run;*necessary;
DATA %calltext(data = list_values
              ,text = %nrstr(work.sex_&sex)
              );*end of data statement;
do until(endofile);
   set sashelp.class  end = endofile;
   %calltext(data      = list_values
            ,delimiter = %nrstr(else)
            ,text
   =%nrstr(if sex eq "&sex" then output work.sex_&sex;)
            )
   end;
stop;
run;
log: ---------------------------------------------------
13     DATA
MPRINT(CALLTEXT):   work.sex_F
MPRINT(CALLTEXT):   work.sex_M
18     do until(EndoFile);
19        set sashelp.class end = endoFile;
MPRINT(CALLTEXT): if Sex eq "F" then output work.sex_F;
MPRINT(CALLTEXT): else
MPRINT(CALLTEXT): if Sex eq "M" then output work.sex_M;
25        end;
26     stop;
27     run;
NOTE: There were 19 obs read from data set SASHELP.CLASS
NOTE: The data set WORK.sex_F has  9 obs and 5 variables
NOTE: The data set WORK.sex_M has 10 obs and 5 variables
--------------------------------------------------------
DateTime: 2016-09-21 7:14:10 AM
word count:       words:  696
                  lines:  159
characters(no   spaces): 4610
characters(with spaces): 6643
****** ...................... */
%MACRO calltext
        (data     = sashelp.class
        ,text     = %nrstr(%put note: name:&name sex=&sex;)
        ,delimiter= /* %nrstr(else) */
        ,global   = 0 /* make mvars global? */
        ,hex16    = 1 /* convert numerics to hex16? */
        ,testing  = 0
        ,unquote  = 1 /* write notes to log? */
)/ des =
'site: assemble mvar assignment statement(s), unquote text'
 /** * store source /* */
;/**** NOTE: _*: avoid name collisions w/data set vars ***/
%local _delimiter _global _hex16 _text _testing _unquote
       _dsid _rc  _obs _n_obs    _var  _n_vars
       _name _num _type          _time_start _time_end;
%let _testing = %eval(not(0 eq &testing)
        or(    %sysfunc(getoption(mprint))  eq MPRINT
           and %sysfunc(getoption(source2)) eq SOURCE2));
%let _time_start= %sysfunc(datetime(),hex16);
%let _delimiter = &delimiter;
%let _global    = %eval(not(0 eq &global));
%let _hex16     = %eval(not(0 eq &hex16));
%let _text      = &text;
%let _unquote   = %eval(not(0 eq &unquote));
 
%**  description: assertions;
%**  purpose    : if fail then exit;
%if  not(%sysfunc(exist(&data))) %then %do;
     %put Err%str()or &sysmacroname: not exist(&data);
     %return;
     %end;
%let _dsid   = %sysfunc(open (&data       ));
%let _n_obs  = %sysfunc(attrn(&_dsid,nobs ));
%let _n_vars = %sysfunc(attrn(&_dsid,nvars));
%put info: &sysmacroname &=data &=_n_obs &=_n_vars;
%if  not &_n_obs or not &_n_vars %then %do;
     %put Err%str()or &sysmacroname exit: not(nobs and vars);
     %goto close_exit;
     %end;
 
%** description: read data;
%** purpose    : make macro variable assignment(s), submit;
%do _obs = 1 %to &_n_obs;%*** fetchobs==read row;
    %let _rc       = %sysfunc(fetchobs(&_dsid,&_obs ));
    %do _var = 1 %to &_n_vars;
        %let _name = %sysfunc(varname (&_dsid,&_var ));
        %let _num  = %sysfunc(varnum  (&_dsid,&_name));
        %let _type = %sysfunc(vartype (&_dsid,&_num ));
        %if &_global %then %global &_name;
        %else              %local  &_name;
        %** assign: for type=C: value, type=N: hex16(value);
        %if   &_type eq C
           or(&_type eq N and not &_hex16) %then
              %let &_name = %left(%sysfunc(
                       getvar&_type(&_dsid,&_num)));
        %else %let &_name = %left(%sysfunc(putn(
              %sysfunc(getvar&_type(&_dsid,&_num)),hex16)));
        %if &_testing or &_global %then
            %put echo: &_name=&&&_name;
        %end;
 
    %if not &_testing and &_unquote %then
        %put &sysmacroname output: %cmpres(%unquote(&_text));
    %*** submit: note! no ending semicolon!;
    %unquote(&_text)
 
    %if %length(&_delimiter) and &_obs lt &_n_obs %then %do;
        %if not &_testing and &_unquote %then
            %put &sysmacroname output: %unquote(&_delimiter);
        %unquote(&_delimiter)
        %end;
    %end;
%if &_testing %then %put _local_;
 
%close_exit: %let _rc       = %sysfunc(close(&_dsid));
             %let _time_end = %sysfunc(datetime(),hex16);
%put info: &sysmacroname used real time %sysfunc
           (putn(&_time_end.x-&_time_start.x,time12.3));
%mend calltext;

Assertions

This function performs the following assertions:

  • exist data?
  • data has rows==Nobs and columns==Nvars

Code and demonstrations have been moved to: Macro_Assertions_for

Examples

Hard-coded Macro Statments

This program provides an illustration of the statements returned by the function which are passed to the SAS parser.

*          initialization for this demo;
6          %Let Sex     = ?;
7          %Let Count   = 0;
8          %Let Percent = 0;
9          %Let Text    = tokens: Sex: &Sex Count: &Count Percent: &Percent;

10         %Let Sex     = F;
11         %Let Count   = 9;
12         %Let Percent = 47;
13         %Put note:
14         &Text.
15         );
note: tokens: Sex: F Count: 9 Percent: 47

16         %Let Sex     = M;
17         %Let Count   = 10;
18         %Let Percent = 52;
19         %Put note:
20         &Text.
21         );
note: tokens: Sex: M Count: 10 Percent: 52

Calling an Include

This program calls a parameterized include: Cardinality-Ratio-calculator.

The called program is shown on Cardinality_Ratio#Program:_CrdRatio-demo

 /*    name: <UNC>\SAS-site\macro-tests\
                  calltext-demo-CardRatio-caller.sas
description: test program for calltext
    purpose: demo of list processing routine
author: RJF2 5/12/2012
*************/
options mprint source2;*testing==on;
 
*note: parameters for SiteIncl(CrdRatio);
%Let In_Libname = sashelp;
%Let In_Memname = class;
%Let In_Memname = _all_;
 
%Let Libname    =;
%Let Memname    =;
 
%Let Out_Base   = NLevels_all;
%Let Out_Data   = NLevels    ;
%Let Out_Lib    = Work       ;
 
%Let Out_Contents = Contents;*here;
 
PROC Contents data  = &In_Libname..&In_Memname
              out   = &Out_Contents
             (keep  = Libname Memname Memtype Nobs
              where = (Memtype eq 'DATA'  and Nobs))
                      noprint;
 
PROC Sort data = &Out_Contents(drop = Memtype Nobs)
           out = &Out_Contents
                 nodupkey;
           by    Memname;
 
PROC SQL; describe table &Out_Contents;
          quit;
 
PROC Print data = &Out_Contents;
           title3 &Out_Contents;
run;
 
*testing;
%calltext(data = &Out_Contents
         ,text = %nrstr(%put include SiteIncl(CrdRatio);)
         );
 
options nomprint nosource2;*testing==off;
 
%calltext(data = &Out_Contents
         ,text = %nrstr(%include SiteIncl(CrdRatio);)
         );
 
Proc Print data   = &Out_Lib..&Out_Base;
           title3  "Nlevels of &In_Libname..&In_Memname. &Out_Base";
run;

log

NOTE: The data set WORK.CONTENTS has 78 observations and 2 variables.

[... Big Snip ...]

36         %calltext(data = &Out_Contents
37                  ,text = %nrstr(%put include SiteIncl(CrdRatio);)
38                  );
note: CALLTEXT reading Contents obs=78 vars=2
note: testing LIBNAME=SASHELP
note: testing MEMNAME=PRDSAL2
include SiteIncl(CrdRatio)
note: testing LIBNAME=SASHELP
note: testing MEMNAME=PRDSAL3
include SiteIncl(CrdRatio)
note: testing LIBNAME=SASHELP
note: testing MEMNAME=PRDSALE
include SiteIncl(CrdRatio)

[... Big Snip ...]

note: testing LIBNAME=SASHELP
note: testing MEMNAME=ZIPCODE
include SiteIncl(CrdRatio)

[... Big Snip ...]

39         %calltext(data = &Out_Contents
40                  ,text = %nrstr(%include SiteIncl(CrdRatio);)
41                  );
note: CALLTEXT reading Contents obs=76 vars=2
note: CALLTEXT returns %include SiteIncl(CrdRatio);
note: CALLTEXT returns PROC Freq data = SASHELP.ADSMSG nlevels ;

[... Big Snip ...]

note: CALLTEXT used real time  0:00:15.297
7452       Proc Print data   = &Out_Lib..&Out_Base;
7453                  title3  "Nlevels of &In_Libname..&In_Memname. &Out_Base";
7454       run;

NOTE: There were 692 observations read from the data set WORK.NLEVELS_ALL.
NOTE: The PROCEDURE PRINT printed page 78.
NOTE: PROCEDURE PRINT used (Total process time):
      real time           0.01 seconds

[... Big Snip ...]

NOTE: SAS Institute Inc., SAS Campus Drive, Cary, NC USA 27513-2414
NOTE: The SAS System used:
      real time           15.70 seconds

listing

Site Macro Testing
Nlevels of SASHELP._all_ NLevels_all
                                                                           Cardi-
                                                                           nality
Obs  Data_Set         name        T_Len  label                             Ratio       is_a     NLevels   Nobs
[... Big Snip ...]
659  SASHELP.ZIPCODE.CITY        c: 35  Name of city/org                 0.45109  foreign key?   18705  41466
660  SASHELP.ZIPCODE.COUNTY      n:  8  FIPS county code.                0.00786  foreign key?     326  41466
661  SASHELP.ZIPCODE.COUNTYNM    c: 25  Name of county/parish.           0.04657  foreign key?    1931  41466
[... Big Snip ...]
666  SASHELP.ZIPCODE.STATE       n:  8  Two-digit number (FIPS code) f   0.00140  foreign key?      58  41466
667  SASHELP.ZIPCODE.STATECODE   c:  2  Two-letter abbrev. for state n   0.00140  foreign key?      58  41466
668  SASHELP.ZIPCODE.STATENAME   c: 25  Full name of state/territory     0.00140  foreign key?      58  41466
669  SASHELP.ZIPCODE.TIMEZONE    c:  9  Time Zone for ZIP Code.          0.00027  foreign key?      11  41466
670  SASHELP.ZIPCODE.X           n:  8  Longitude (degrees) of the cen   0.99756  nmbr: fact?    41365  41466
671  SASHELP.ZIPCODE.Y           n:  8  Latitude (degrees) of the cent   0.99641  nmbr: fact?    41317  41466
672  SASHELP.ZIPCODE.ZIP         n:  8  The 5-digit ZIP Code             1.00000  primary key!?  41466  41466
673  SASHELP.ZIPCODE.ZIP_CLASS   c:  1  ZIP Code Classification:P=PO B   0.00007  foreign key?       3  41466


Splitting a Data Set

Hard-coded

DATA work.Sex_F   (label = "Count: 9 Pcnt:47")
     work.Sex_M   (label = "Count:10 Pcnt:53")
     ;*end data statement;
do until(EndoFile);
   set sashelp.class
       end = endoFile;
   if      Sex eq "F" then output work.Sex_F;
   else if Sex eq "M" then output work.Sex_M;
   else ;  *** semicolon needed for ending else;
   end;
stop;
run;
 
PROC SQL; describe table work.Sex_F;
          describe table work.Sex_M;
          quit;

Using the CallText function

sashelp.Class.Sex
PROC Freq data   = sashelp.Class;
          tables   Sex
                 / noprint
             out = Work.Freq_Class_Sex;
run;
DATA %calltext(data = Work.Freq_Class_Sex
              ,text = %nrstr
     (work.Sex_&Sex(label="Count:&Count Pcnt:%scan(&Percent.,1,.)"))
              )
     ;*end: data statement;
do until(EndoFile);
   set sashelp.class
       end = endoFile;
   %calltext(data = Work.Freq_Class_Sex
            ,text =
   (if Sex eq "&Sex" then output work.Sex_&Sex; else)
            )
      ;*semicolon needed for end: else;
   end;
stop;
run;
 
PROC SQL;  %calltext(data = Work.Freq_Class_Sex
                    ,text = (describe table work.Sex_&Sex;)
                    )
           quit;
run;

Log:

8          PROC Freq data   = sashelp.Class;
9                    tables   Sex
10                          / noprint
11                      out = Work.Freq_Class_Sex;
12         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.

13         DATA %calltext(data = Work.Freq_Class_Sex
14                       ,text =
15        (work.Sex_&Sex(label="Count:&Count Pcnt:%scan(&Percent.,1,.)"))
17                       )
note: CALLTEXT reading Work.Freq_Class_Sex obs=2 vars=3
note: CALLTEXT returns work.Sex_F(label="Count:9 Pcnt:47")
note: CALLTEXT returns work.Sex_M(label="Count:10 Pcnt:52")
18              ;*end: data statement;
19         do until(EndoFile);
20            set sashelp.class
21                end = endoFile;
22            %calltext(data = Work.Freq_Class_Sex
23                     ,text =
24            (if Sex eq "&Sex" then output work.Sex_&Sex; else)
25                     )
note: CALLTEXT reading Work.Freq_Class_Sex obs=2 vars=3
note: CALLTEXT returns if Sex eq "F" then output work.Sex_F; else
note: CALLTEXT returns if Sex eq "M" then output work.Sex_M; else
26               ;*semicolon needed for end: else;
27            end;
28         stop;
29         run;

NOTE: There were 19 observations read from the data set SASHELP.CLASS.
NOTE: The data set WORK.SEX_F has  9 observations and 5 variables.
NOTE: The data set WORK.SEX_M has 10 observations and 5 variables.

31         PROC SQL;
31       !            %calltext(data = Work.Freq_Class_Sex
32                             ,text = (describe table work.Sex_&Sex;)
33                             )
note: CALLTEXT reading Work.Freq_Class_Sex obs=2 vars=3
note: CALLTEXT returns describe table work.Sex_F;
NOTE: SQL table WORK.SEX_F was created like:

create table WORK.SEX_F( label='Count:9 Pcnt:47' bufsize=4096 )
  (
   Name char(8),
   Sex char(1),
   Age num,
   Height num,
   Weight num
  );

note: CALLTEXT returns describe table work.Sex_M;
NOTE: SQL table WORK.SEX_M was created like:

create table WORK.SEX_M( label='Count:10 Pcnt:52' bufsize=4096 )
  (
   Name char(8),
   Sex char(1),
   Age num,
   Height num,
   Weight num
  );
34                    quit;


sashelp.Zipcode.Zip3

USPS has a central processing facility for a zipcode whose code is ???00.

Task: provide a data set with the all the zipcodes of the central processing facility.

*name       : \SAS-site\macros-tests\calltext-demo-sashelp-class-sex.sas
*description: split a data set based on values of class variable
*purpose    : demo and test suite for CallText;
 
*options mprint;*testing macro;
*options mprint source2;*testing function notes;
*options obs = 33;
 
%Let In_Lib   = sashelp;
%Let In_Data  = ZipCode;
 
%Let Out_Data  = work.ZipCode3;
%Let Out_List = Work.List_State_Zip3;
 
Proc SQL; describe table &In_Lib..&In_Data.;
          quit;
 
DATA Work.&In_Data;*(drop = Zip prev_Zip3);
attrib Zip3 length = $3;
do until(EndoFile);
   set &In_Lib..&In_Data.
       end = EndoFile;
   Zip3 = put(Zip,z5.);
   output;
   end;
stop;
run;
 
Proc SQL; describe table &SysLast.;
          quit;
 
PROC Freq data   = &SysLast.(keep = StateCode Zip3);
          tables   StateCode* Zip3
                 / list missing noprint
            out = &Out_List.;
 
*note: optimize for data subsetting if;
PROC SORT data = &SysLast.
           out = &SysLast.;
           by    descending Count;
run;
DATA %calltext(data = &Out_List
              ,text = %nrstr
              (work.&StateCode._&Zip3.
              (label="Count:&Count.
                      Percent:%scan(&Percent.,1,.).%substr
              (%scan(&Percent.,2,.),1,3)"))
              ,hex16=0
              )
     ;*end: data statement;
attrib Zip3 length = $3;
do until(EndoFile);
   set Work.&In_Data.
       end = endoFile;
   *Zip3 = put(Zip,z5.);
   %calltext(data = &Out_List
            ,text = %nrstr
      (if StateCode eq "&StateCode" and Zip3 eq "&Zip3." then
          output work.&StateCode._&Zip3.; else)
            )
      ;*semicolon needed for end: else;
   end;
stop;
run;
 
/**********************************************************************
PROC SQL;  %calltext(data = &Out_List
                    ,text = %nrstr
                    (describe table work.&StateCode._&Zip3.;)
                    )
           quit;
/**********************************************************************/
 
PROC SORT data = &Out_List.
           out = &Out_List.;
           by    StateCode Zip3;
run;
%calltext(data = &Out_List
         ,text = %nrstr
         (Proc Print data = work.&StateCode._&Zip3.(obs=3);
          title3 "State: &StateCode. Zip3: &Zip3.";
          title4 "Count: &Count.";
          title5 "Percent: &Percent.";run;
         )
         ,hex16=0
         )
run;

Log:

[... Big Snip ...]
NOTE: There were 41466 observations read from the data set WORK.ZIPCODE.
[... Big Snip ...]
NOTE: The data set WORK.LIST_STATE_ZIP3 has 922 observations and 4 variables.
[... Big Snip ...]
42         DATA %calltext(data = &Out_List
43                       ,text = %nrstr
44                       (work.&StateCode._&Zip3.
45                       (label="Count:&Count.
46                               Percent:%scan(&Percent.,1,.).%substr
47                       (%scan(&Percent.,2,.),1,2)"))
48                       ,hex16=0
49                       )
note: CALLTEXT reading Work.List_State_Zip3 obs=922 vars=4
note: CALLTEXT returns work.TX_770 (label="Count:99 Percent:0.23")
note: CALLTEXT returns work.MN_553 (label="Count:97 Percent:0.23")
note: CALLTEXT returns work.CA_900 (label="Count:95 Percent:0.22")

[... Big Snip ...]

note: CALLTEXT returns work.VA_205 (label="Count:1 Percent:0.00")
note: CALLTEXT returns work.WY_821 (label="Count:1 Percent:0.00")
note: CALLTEXT returns work.WY_834 (label="Count:1 Percent:0.00")
note: CALLTEXT used real time  0:00:07.672
50              ;*end: data statement;
51         attrib Zip3 length = $3;
52         do until(EndoFile);
53            set Work.&In_Data.
54                end = endoFile;
55            *Zip3 = put(Zip,z5.);
56            %calltext(data = &Out_List
57                     ,text = %nrstr
58               (if StateCode eq "&StateCode" and Zip3 eq "&Zip3." then
59                   output work.&StateCode._&Zip3.; else)
60                     )
note: CALLTEXT reading Work.List_State_Zip3 obs=922 vars=4
note: CALLTEXT returns if StateCode eq "TX" and Zip3 eq "770" then output work.TX_770; else
note: CALLTEXT returns if StateCode eq "MN" and Zip3 eq "553" then output work.MN_553; else
note: CALLTEXT returns if StateCode eq "CA" and Zip3 eq "900" then output work.CA_900; else

[... Big Snip ...]

note: CALLTEXT returns if StateCode eq "VA" and Zip3 eq "205" then output work.VA_205; else
note: CALLTEXT returns if StateCode eq "WY" and Zip3 eq "821" then output work.WY_821; else
note: CALLTEXT returns if StateCode eq "WY" and Zip3 eq "834" then output work.WY_834; else
note: CALLTEXT used real time  0:00:05.046
61               ;*semicolon needed for end: else;
62            end;
63         stop;
64         run;

NOTE: There were 41466 observations read from the data set WORK.ZIPCODE.
NOTE: The data set WORK.TX_770 has 99 observations and 20 variables.
NOTE: The data set WORK.MN_553 has 97 observations and 20 variables.
NOTE: The data set WORK.CA_900 has 95 observations and 20 variables.

[... Big Snip ...]

NOTE: The data set WORK.VA_205 has 1 observations and 20 variables.
NOTE: The data set WORK.WY_821 has 1 observations and 20 variables.
NOTE: The data set WORK.WY_834 has 1 observations and 20 variables.
NOTE: DATA statement used (Total process time):
      real time           39.40 seconds

[... Big Snip ...]

note: CALLTEXT reading Work.List_State_Zip3 obs=922 vars=4
note: CALLTEXT returns Proc Print data = work.AK_995(obs=3);
title3 "State: AK Zip3: 995";
title4 "Count: 69";
title5 "Percent: 0.16640138908985";
run;
NOTE: There were 3 observations read from the data set WORK.AK_995.

[... Big Snip ...]

note: CALLTEXT returns Proc Print data = work.WY_834(obs=3);
title3 "State: WY Zip3: 834"; title4 "Count: 1"; title5 "Percent: run;
NOTE: There were 1 observations read from the data set WORK.WY_834.

NOTE: The PROCEDURE PRINT printed page 922.
NOTE: PROCEDURE PRINT used (Total process time):
      real time           0.01 seconds

[... Big Snip ...]

note: CALLTEXT used real time  0:00:43.609
87
88
89         run;

NOTE: SAS Institute Inc., SAS Campus Drive, Cary, NC USA 27513-2414
NOTE: The SAS System used:
      real time           1:29.34

Data Review using formats

predecessor: Macro_Invalid_for_Data_Review

 /*    name: ...\SAS-site\sas-macro-tests
              calltext-data-review-demo-sashelp-class.sas;
description: data review with user-defined formats to identify outliers
purpose    : test suite for CallText
RJF2 4/11/2012 7:58:09 AM
RJF  1/13/2013 3:24:54 PM for sas.wiki.CallText
****/
 
*options mprint;* source2;*testing;
*options mprint source2;*testing;
 
**** used in proc format and macro DataRevu;
%Let Fmt_Invalid = invalid;
 
*description: make user-defined formats with other=;
PROC Format library = work
            cntlout = Work.Format_Values_Other
            (where = (HLO = "O"));
            title3 fmtlib;
            value $Gender
                  'F' = 'female'
                  'M' = 'male'
                other = "&Fmt_Invalid.";
            value Height
                51.3 <-< 72 = 'expected'
                other = "&Fmt_Invalid.";
            value Weight
                50.5 <-< 150 = 'expected'
                other = "&Fmt_Invalid.";
run;
 
*description: make a value for formats with other;
*purpose    : use with CallText;
DATA Work.CntlIn;
     retain FmtName '$_HaveOther'
            HLO    ' '
            Label  '1' %*one==true;
            Type   'C';
do until(EndoFile);
   set Work.Format_Values_Other
      (keep =    FmtName Type
       rename = (FmtName = Start
                 Type    = TypeOld))
       end = EndoFile;
   if TypeOld = 'C' then Start = catt('$',Start);
   output;
   end;
HLO   = "O";*Oh==Other;
Label = '0';*zero==false;
output;
stop;
 
PROC Print data = &SysLast.;
           title3 &SysLast.;
 
PROC Format library = Work
            cntlin  = Work.CntlIn
            ;*fmtlib;
 
run;
 
* description: add formats and invalid Gender;
DATA Work.Class;
     if 0 then set sashelp.class;
     attrib Sex    format = $Gender. label = 'gender'
            Height format = Height.
            Weight format = Weight.;
do until(EndoFile);
   set sashelp.class end = EndoFile;
   output;
   end;
****   add the invalid row;
Name = 'Chris';
Sex  = 'I';
output;
stop;
run;
 
*description: save Contents with Name and Format;
PROC Contents data = Work.Class
                     noprint
              out  = Work.Contents
             (keep = NAME    %*char(32) label='Variable Name',  ;
                     LABEL   %*char(256) label='Variable Label',;
                     FORMAT  %*char(32) label='Variable Format',;
             where = (        Format ne ' '
                      and put(Format,$_HaveOther.)
             )
             );*note keep list must match macro DataRevu parm list;
 
PROC SQL; describe table Work.Class;
          describe table Work.Contents;
          quit;
 
PROC Print data = Work.Contents;
           title3 Work.Contents;
 
DATA Work.Data_For_Review;
do until(EndoFile);
   set Work.Class end = EndoFile;
   %calltext(Data = Work.Contents
            ,text = %nrstr
   (if put(&Name.,&Format..) eq "&Fmt_Invalid." then output;else)
            );
   *note: semicolon after close paren terminates else statement;
   end;
stop;
 
Proc Print data = &SysLast.;
           title3 &SysLast.;
           format _all_;
run;

output

WORK.CNTLIN
                                                        Type
Obs      FmtName      HLO    Label    Type     Start     Old

 1     $_HaveOther             1       C      HEIGHT      N
 2     $_HaveOther             1       C      WEIGHT      N
 3     $_HaveOther             1       C      $GENDER     C
 4     $_HaveOther     O       0       C      $GENDER     C


Work.Contents
Obs    NAME      LABEL     FORMAT

 1     Height              HEIGHT
 2     Sex       gender    $GENDER
 3     Weight              WEIGHT


WORK.DATA_FOR_REVIEW
Obs     Name     Sex    Age    Height    Weight
---    ------    ---    ---    ------    ------
 1     Joyce      F      11     51.3       50.5
 2     Philip     M      16     72.0      150.0
 3     Chris      I      15     66.5      112.0

References

--Ronald_J._Fehd macro.maven == the radical programmer 07:14, 24 April 2012 (EDT)