%macro runby(version, data=_last_, by=, order=internal, lvlquote=YES, print=YES) / minoperator; /* Version and debug options */ %let _tm = %sysfunc(datetime()); %let _version = 1.1; %if &version ne %then %put NOTE: &sysmacroname macro Version &_version..; %let version=%upcase(&version); %let lvlquote=%upcase(&lvlquote); %let print=%upcase(&print); %let order=%upcase(&order); %let _opts = %sysfunc(getoption(notes)); %if %index(%upcase(&version),DEBUG) %then %do; options notes mprint %if %index(%upcase(&version),DEBUG2) %then mlogic symbolgen; ; ods select all; %put _user_; %end; %else %do; options nonotes nomprint nomlogic nosymbolgen; ods exclude all; %end; /* Check for newer version */ %let _notfound=0; filename _ver url 'http://ftp.sas.com/techsup/download/stat/versions.dat' termstr=crlf; data _null_; infile _ver end=_eof; input name:$15. ver; if upcase(name)="&sysmacroname" then do; call symput("_newver",ver); stop; end; if _eof then call symput("_notfound",1); run; options notes; %if &syserr ne 0 or &_notfound=1 %then %put NOTE: Unable to check for newer version of &sysmacroname macro.; %else %if %sysevalf(&_newver > &_version) %then %do; %put NOTE: A newer version of the &sysmacroname macro is available at; %put NOTE- this location: http://support.sas.com/ ; %end; %if %index(&version,DEBUG)=0 %then options nonotes;; /* Verify required parameters were specified */ %if &by= %then %do; %put ERROR: BY= is required.; %goto exit; %end; /* Verify DATA= data set exists */ %if &data=_last_ %then %let data=&syslast; %if %sysfunc(exist(&data)) ne 1 %then %do; %put ERROR: DATA= data set, &data, not found.; %goto exit; %end; /* Verify BY= variables are in DATA= data set */ %let i=1; %let _notfound=; %do %while (%scan(&by,&i) ne %str() ); %let _BY&i=%upcase(%scan(&by,&i)); %let dsid=%sysfunc(open(&data)); %if &dsid %then %do; %let vnum=%sysfunc(varnum(&dsid,&&_BY&i)); %if &vnum %then %let _TYP&i=%sysfunc(vartype(&dsid,&vnum)); %else %let _notfound=&_notfound &&_BY&i; %let rc=%sysfunc(close(&dsid)); %end; %let _nbyvars=&i; %let i=%eval(&i+1); %end; %if &_notfound ne %then %do; %put ERROR: Variable(s) %upcase(&_notfound) not found in data %upcase(&data).; %goto exit; %end; /* Verify valid values of ORDER= */ %let valorder=INTERNAL DATA FORMATTED FREQ; %if not (%upcase(&order) in &valorder) %then %do; %put ERROR: ORDER= must be INTERNAL, DATA, FORMATTED, or FREQ.; %goto exit; %end; /* Get BY group combinations */ %let _tablvars = %upcase(%sysfunc(translate(&by,"*"," "))); proc freq data=&data order=ℴ table &_tablvars / noprint out=_bylvls(keep=&by); run; data _null_; set _bylvls nobs=nby; call symput('_nby',cats(nby)); run; /* Select each BY group in turn and run user code */ %if &print=YES %then ods select all;; %do _bygrp=1 %to &_nby; %let _badchar=0; %if %index(%upcase(&version),DEBUG)=0 %then options nonotes;; %let BYlabel=; data _null_; pnt=&_bygrp; set _bylvls point=pnt; %do _v=1 %to &_nbyvars; if indexc(&&_BY&_v,'&%') then do; call symput('_badchar','1'); call symput(cats('_LVL',&_v),cats(translate(&&_BY&_v,'__','&%'))); end; else call symput(cats('_LVL',&_v),cats(&&_BY&_v)); %end; stop; run; options notes; %do _v=1 %to &_nbyvars; %let BYlabel=&BYlabel &&_BY&_v=&&_LVL&_v; %if &&_TYP&_v=C %then %do; %if %upcase(&lvlquote)=YES %then %let _LVL&_v="&&_LVL&_v"; %else %let _LVL&_v=&&_LVL&_v; %end; %end; %if &_badchar=0 %then %do; %code; %end; %else %do; %put WARNING: Percent (%) and ampersand (&) characters not allowed in BY= variables.; %put WARNING- Skipping BY group. These characters are replaced with underscores (_).; %end; %put NOTE: The above message(s) are for the following BY group:; %put NOTE- &BYlabel; %end; /* Clean up */ %exit: %if %index(%upcase(&version),DEBUG)=0 %then %do; options nonotes; proc datasets nolist nowarn; delete _bylvls; run; quit; %end; %if %index(%upcase(&version),DEBUG) %then %do; options nomprint nomlogic nosymbolgen; %put _user_; %end; ods select all; options &_opts; %let _tm = %sysfunc(round(%sysevalf(%sysfunc(datetime()) - &_tm), 0.01)); %put NOTE: The &sysmacroname macro used &_tm seconds.; %mend;