GENERICITY New Metadata Concepts Applied to SAS Macro Programming 1

advertisement
12-Jul-16
1
GENERICITY
New Metadata Concepts Applied to SAS Macro
Programming
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
2
Preface
What it is and what it is not
This paper presents part of my experiences using SAS Macro technology
over years with great pleasure and, occasionally, with some success, at
least for customer satisfaction.
Starting with an example from an earlier presentation the question is raised,
how the positive correlation between program flexibility and number of
parameters can be extinguished.
By introducing the term “Reporting Specific Data Structures” one idea is
described that allows to make use from metadata already present in the
runtime environment.
This paper is NOT about Programming using the SAS Macro Facility.
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
3
Generic Programming
%MACRO cr_tbl_4_fct;
%GLOBAL fct src tbl otb;
%LOCAL i_frq n_frq l_frq i_ctl n_ctl l_ctl;
%IF %SCAN(&FCT.,1,'_') eq PRR %THEN %DO; /* start PRR main loop */
%LET n_frq = %SCAN(&FCT.,2,'_');
%LET n_ctl = %SCAN(&FCT.,3,'_');
proc sql noprint;
select
into
from
where
and
and
;
select
,
into
,
from
where
and
and
and
;
insert
select
,
into
,
from
where
and
and
and
;
name
:p_key
dictionary.columns
libname = %UPCASE("&SRC.")
memname = %UPCASE("&TBL.")
varnum eq 1
insert
insert
select
from
;
insert
select
from
;
into footer values("Stratification performed by: &L2CTL.");
into footer
compbl(put(count(*),8.)||" lines processed from %UPCASE(&DB_USR.).&TBL.")
&SRC..&TBL.
name
name
:l_frq separated by ' '
:l2frq separated by ' * '
dictionary.columns
libname = %UPCASE("&SRC.")
memname = %UPCASE("&TBL.")
varnum gt 1
varnum le %EVAL(&N_FRQ. + 1)
into header values("PRR values calculated for &L2FRQ.");
name
name
:l_ctl separated by ' , '
:l2ctl separated by ' * '
dictionary.columns
libname = %UPCASE("&SRC.")
memname = %UPCASE("&TBL.")
varnum gt %EVAL(&N_FRQ. + 1)
varnum le %EVAL(&N_FRQ. + &N_CTL. + 1)
into footer
compbl("Frequency tables based on "||put(count(distinct &P_KEY.),8.)||" distinct values from field &P_KEY.")
&SRC..&TBL.
Concept
create table &SRC..c_frq as
select distinct
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
,
%END;
%SCAN(&L_FRQ.,&I_FRQ.,' ')
%END;
from &SRC..&TBL.
where
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
and
%END;
%SCAN(&L_FRQ.,&I_FRQ.,' ') is not null
%END;
;
%LET n_c_frq = &SQLOBS.;
insert into footer values("&N_C_FRQ. combinations from &L2FRQ. processed");
quit;
data _null_;
set &SRC..c_frq;
%DO i_frq = 1 %TO &N_FRQ.;
call symput("%SCAN(&L_FRQ.,&I_FRQ.,' ')"||'_'||trim(left(put(_N_,4.))),trim(left(%SCAN(&L_FRQ.,&I_FRQ.,' '))));
%END;
run;
%DO i_c_frq = 1 %TO &N_C_FRQ.; /* start FREQ loop */
proc sql;
create view WORK.tbl as
select distinct
&P_KEY. ,
%DO i_frq = 1 %TO &N_FRQ.;
%LET %SCAN(&L_FRQ.,&I_FRQ.,' ') = %QUOTE(&&%SCAN(&L_FRQ.,&I_FRQ.,' ')_&I_C_FRQ.);
case
when sum( %SCAN(&L_FRQ.,&I_FRQ.,' ') = "&&%SCAN(&L_FRQ.,&I_FRQ.,' ')" ) > 0
then -1
else 0
end
as %SCAN(&L_FRQ.,&I_FRQ.,' ')_rnk ,
%END;
&L_CTL.
from &SRC..&TBL.
group by
&P_KEY.
;
quit;
proc freq noprint data = WORK.tbl;
tables &L2CTL.
%DO i_frq = 1 %TO &N_FRQ.;
%STR(*) %SCAN(&L_FRQ.,&I_FRQ.,' ')_rnk
%END;
/ cmh1
;
output out = WORK.cmh(keep = _mhrrc1_ l_mhrrc1 u_mhrrc1) cmh1;
run;
proc freq noprint data = WORK.tbl;
tables
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
%STR(*)
%END;
%SCAN(&L_FRQ.,&I_FRQ.,' ')_rnk
%END;
/ cmh1
;
output out = WORK.cmc(keep = l_mhrrc1 u_mhrrc1) cmh1;
run;
proc freq noprint data = WORK.tbl;
tables
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
%STR(*)
%END;
%SCAN(&L_FRQ.,&I_FRQ.,' ')_rnk
%END;
/ sparse out = WORK.cnt(keep = count)
;
run;
+
proc transpose data = WORK.cnt out = WORK.trp; run;
data WORK.res; merge WORK.trp WORK.cmh WORK.cmc(rename=(l_mhrrc1=l_mhrrcx u_mhrrc1=u_mhrrcx)); run;
proc sql noprint;
create table WORK.out as
select
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
,
%END;
"&&%SCAN(&L_FRQ.,&I_FRQ.,' ')"
as %SCAN(&L_FRQ.,&I_FRQ.,' ')
%END;
, col1 as a label = "cell A"
, col3 as b label = "cell B"
, col2 as c label = "cell C"
, col4 as d label = "cell D"
, (a/c)*((c+d)/(a+b)) as cmh_crude label = "PRR crude"
, l_mhrrcx as cmhlcrude label = "PRR crude lower CL"
, u_mhrrcx as cmhucrude label = "PRR crude upper CL"
, _mhrrc1_ as cmh_strat label = "PRR stratified"
, l_mhrrc1 as cmhlstrat label = "PRR stratified lower CL"
, u_mhrrc1 as cmhustrat label = "PRR stratified upper CL"
from WORK.res
;
%IF &I_C_FRQ. = 1 %THEN %DO;
drop table &SRC..&TBL.%SCAN(&FCT.,1,_);
%END;
create table &SRC..&TBL.%SCAN(&FCT.,1,_) as
%IF &I_C_FRQ. > 1 %THEN %DO;
select *
from &SRC..&TBL.%SCAN(&FCT.,1,_)
union
%END;
select *
from out
;
quit;
Metadata
%END; /* end FREQ loop */
proc sql;
create table &SRC..&TBL.%SCAN(&FCT.,1,_) as
%IF %SUBSTR(&RST.,2,1) ne C %THEN %DO;
select *
from &SRC..&TBL.%SCAN(&FCT.,1,_) as tbl
%IF %SUBSTR(&RST.,1,1) ne R %THEN %DO;
right
%END;
join &SRC..&TBL. as fct
on
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
and
%END;
fct.%SCAN(&L_FRQ.,&I_FRQ.,' ')
= tbl.%SCAN(&L_FRQ.,&I_FRQ.,' ')
%END;
%END;
%ELSE %DO;
select *
from &SRC..&TBL.%SCAN(&FCT.,1,_)
%END;
;
quit;
%LET otb = &TBL.;
%LET tbl = &TBL.%SCAN(&FCT.,1,_);
%END; /* end PRR main loop */
%MEND cr_tbl_4_fct;
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
4
Never forget
“A problem well stated is a
problem half solved“
Charles Kettering (1876-1958)
Founder of the General Motors Research Corporation
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
5
Number of Parameters
Np = f(F)
Flexibility
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
6
Intro
When producing reports
You might end up with
-
One appropriately sized program per report
-
One somehow macroized program per report type
-
One Macro System for reporting domains
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
7
Start
For example:
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
8
This Quest
Total subjects treated
2821 ( 100)
2823 ( 100)
Start of study medication within 24 hrs after
randomization
Yes
No
Surgery delayed
Other
Missing
Missing
2751
66
59
13
4
2775
47
41
13
1
Study medication only from kit allocated
Yes
Missing
2821 ( 100)
- ( - )
2823 ( 100)
- ( - )
26 ( 0.9)
12 ( 0.4)
3 ( 0.1)
17 ( 0.6)
6 ( 0.2)
3 ( 0.1)
8 ( 0.3)
1 ( 0.0)
2808 (99.5)
8 ( 0.3)
5 ( 0.2)
2819 (99.9)
4 ( 0.1)
- ( - )
86 ( 3.0)
101 ( 3.6)
79 ( 2.8)
4 ( 0.1)
98 ( 3.5)
4 ( 0.1)
Number of subjects who did not receive full loading
dose
Reason:
Adverse event
Technical failure / dosing error
Missing
At least 75% of loading dose administered
Yes
No
Missing
Number of subjects with maintenance dose
interruptions longer than 1 hour
Number of maintenance dose interruptions
1
2
(97.5)
( 2.3)
( 2.1)
( 0.5)
( - )
( 0.1)
(98.3)
( 1.7)
( 1.5)
( 0.5)
( - )
( 0.0)
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
9
Macro System
generated by a single macro system
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
10
System Architecture
User Modules
Generate datasets
carrying subtables
controlled by usersupplied parms
Core Modules
Perform input
transformation,
calculations and
output transformation
Info Modules
Service Modules
Provide information
about datasets and
variables for correct
processing
Provide frequently
requested tasks in a
standard format with
limited parameter set
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
11
Program Controlling
Required already a few parameters
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
12
full parameter set – user modules
%TWO_BOBO() – Build super row (=block) from boolean selections nested in boolean selection
dsn
input dataset name
row, row2
categorial variable name, 2=list of nested_var#true_value
rev
Y/N (output decodes of &ROW in reverse order)
use, use2
select decode from &ROW, 2=decode from &ROW used as nesting context
weight
Y/N (multiply percentages for &ROW and &ROW2)
col
categorial variable name used for columns
total
T/I/B/O/N/TC/IC/BC/OC/NC
head, head2
Y/N (block header, 2=nested variable)
indent, indinc
n (number of indent columns and increment for nested variable)
num
n (sequence number of output)
stat
Y/N (column with statistics names)
space
1/2/3 (blank line before or after output and between nesting levels)
struct, struct2
name of reference dataset used for full decode structure, 2=nested variable
condense
var#value (non-distinct variable and true value for &ROW)
misslin2
Y/N (force missing line for nested variable)
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
13
user parameters - common
DSN: Name of input dataset or view. This may be any valid SAS dataset name (one-level or two-level)
not accompanied by dataset options or other SAS syntax components.
COL: Name of variable used to construct columns. The variable is checked for number of levels and an
appropriate number of columns are generated.
ROW: Name of variable to construct rows, superrows, and subtables. Modules capable of processing
more than one variable accept a list here.
HEAD: Optionally specify “N” to suppress output of the header line for the row variable generated from
their label. In categorial processing the header is an additional 1st line whereas in continuous
processing the header text is written left-hand to the 1st stats line output. Default is “Y”.
STAT: Optionally select “Y” to generate an output column which contains the names of statistics
generated. Default is “N”.
INDENT: Optionally select a positive integer to indent the rows generated as one block. Default is “0”.
SPACE: Optionally select spacing mode for one-level subtables: 0=no blank lines; 1=1st output line is
blank; 2=last output line is blank. Default is “2”. For two-level subtables: 2=insert additional blank
line between upper and lower level output; 3=only last output line is a blank line. Default is “3”.
NUM: Assigns a unique number to the output generated. Only one digit is allowed here.
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
14
Not amused
This appears quite complicated
Isn’t there another way
-
To have limited source code
-
With a high level of flexibility
-
And not to drown in parameter flood
Let’s have a closer look…
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
15
Talk to me
When running macro programs
You may influence results on several levels
-
Parameter passing (“feed”)
-
Controlling (“feed and prevent”)
-
Communication (“feed, prevent and search”)
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
16
Search
Make your Macro a curious
Communicator
Implement a search mechanism that
-
makes it follow a set of rules
-
provides or
-
generates knowledge on metadata in reach
-
is fault-tolerant
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
17
Search
Of course, this should be a Macro
because you want to
- do it once
- do it generic
- use it forever
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
18
Metadata
Metadata are all around
-
Simple variable lists: “-NUMERIC-”
-
Libref “dictionary”: tables, columns, etc.
-
User defined repositories of any kind
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
19
Report Specific Data Structures
Let’s focus on one
One of the frequently neglected or simply overseen
information bits from dictionary.columns is VARNUM.
This may result from historical reasons, since the SAS
dataset structure was learned as more or less fixed.
Reordering a dataset’s variables was not supported very
well by the SAS System and hence, not used.
Since concepts emerge from programming habits…
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
20
Report Specific Data Structures
SQL Views
Today, since 1990, it is very easy to reorder the “virtual
physical” sequence of variables in a dataset.
The SAS System treats the properties of an SQL view
equally to those of an old-fashioned somewhat
“clumsy” SAS Dataset.
This is good News!
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
21
Report Specific Data Structures
Let’s try a small example:
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
22
Report Specific Data Structures
data testdsn;
label
a=Variable A in SAS Dataset
b=Variable B in SAS Dataset
;
a=22; b=190;
run;
proc sql;
create view testsql as
select b as a label="Variable B from SAS Dataset"
, a as b label="Variable A from SAS Dataset"
from testdsn
;
select memname
, name
, label
, varnum
from dictionary.columns
where memname like 'TEST___'
;
quit;
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
23
Report Specific Data Structures
Member Name
Column Name
Column Label
Column Number
in Table
TESTDSN
a
Variable A in SAS
Dataset
1
TESTDSN
b
Variable B in SAS
Dataset
2
TESTSQL
a
Variable B from
SAS Dataset
1
TESTSQL
b
Variable A from
SAS Dataset
2
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
24
Report Specific Data Structures
Access Layer
Obviously, utilizing the select clause in an SQL view adds
a high amount of information to the data structure. This is
not surprising, in case you
- are a physicist
- can count from zero to 1023 using ten fingers
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
25
Information Gain by Ordering
Source: http://courses.geoplanet.ca/ice3m/image/binary_hand_1-7.gif
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
26
Report Specific Data Structures
Now let’s have a look a real life:
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
27
Report Specific Data Structures
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
28
Report Specific Data Structures
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
29
Resulting Output
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
30
Howto
%MACRO cr_tbl_4_fct;
%GLOBAL fct src tbl otb;
%LOCAL i_frq n_frq l_frq i_ctl n_ctl l_ctl;
%IF %SCAN(&FCT.,1,'_') eq PRR %THEN %DO; /* start PRR main loop */
%LET n_frq = %SCAN(&FCT.,2,'_');
%LET n_ctl = %SCAN(&FCT.,3,'_');
proc sql noprint;
select
into
from
where
and
and
;
select
,
into
,
from
where
and
and
and
;
insert
select
,
into
,
from
where
and
and
and
;
name
:p_key
dictionary.columns
libname = %UPCASE("&SRC.")
memname = %UPCASE("&TBL.")
varnum eq 1
insert
insert
select
from
;
insert
select
from
;
into footer values("Stratification performed by: &L2CTL.");
into footer
compbl(put(count(*),8.)||" lines processed from %UPCASE(&DB_USR.).&TBL.")
&SRC..&TBL.
name
name
:l_frq separated by ' '
:l2frq separated by ' * '
dictionary.columns
libname = %UPCASE("&SRC.")
memname = %UPCASE("&TBL.")
varnum gt 1
varnum le %EVAL(&N_FRQ. + 1)
into header values("PRR values calculated for &L2FRQ.");
name
name
:l_ctl separated by ' , '
:l2ctl separated by ' * '
dictionary.columns
libname = %UPCASE("&SRC.")
memname = %UPCASE("&TBL.")
varnum gt %EVAL(&N_FRQ. + 1)
varnum le %EVAL(&N_FRQ. + &N_CTL. + 1)
into footer
compbl("Frequency tables based on "||put(count(distinct &P_KEY.),8.)||" distinct values from field &P_KEY.")
&SRC..&TBL.
create table &SRC..c_frq as
select distinct
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
,
%END;
%SCAN(&L_FRQ.,&I_FRQ.,' ')
%END;
from &SRC..&TBL.
where
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
and
%END;
%SCAN(&L_FRQ.,&I_FRQ.,' ') is not null
%END;
;
%LET n_c_frq = &SQLOBS.;
insert into footer values("&N_C_FRQ. combinations from &L2FRQ. processed");
quit;
data _null_;
set &SRC..c_frq;
%DO i_frq = 1 %TO &N_FRQ.;
call symput("%SCAN(&L_FRQ.,&I_FRQ.,' ')"||'_'||trim(left(put(_N_,4.))),trim(left(%SCAN(&L_FRQ.,&I_FRQ.,' '))));
%END;
run;
%DO i_c_frq = 1 %TO &N_C_FRQ.; /* start FREQ loop */
proc sql;
create view WORK.tbl as
select distinct
&P_KEY. ,
%DO i_frq = 1 %TO &N_FRQ.;
%LET %SCAN(&L_FRQ.,&I_FRQ.,' ') = %QUOTE(&&%SCAN(&L_FRQ.,&I_FRQ.,' ')_&I_C_FRQ.);
case
when sum( %SCAN(&L_FRQ.,&I_FRQ.,' ') = "&&%SCAN(&L_FRQ.,&I_FRQ.,' ')" ) > 0
then -1
else 0
end
as %SCAN(&L_FRQ.,&I_FRQ.,' ')_rnk ,
%END;
&L_CTL.
from &SRC..&TBL.
group by
&P_KEY.
;
quit;
proc freq noprint data = WORK.tbl;
tables &L2CTL.
%DO i_frq = 1 %TO &N_FRQ.;
%STR(*) %SCAN(&L_FRQ.,&I_FRQ.,' ')_rnk
%END;
/ cmh1
;
output out = WORK.cmh(keep = _mhrrc1_ l_mhrrc1 u_mhrrc1) cmh1;
run;
%LET n_frq = %SCAN(&FCT.,2,'_');
%LET n_ctl = %SCAN(&FCT.,3,'_');
proc freq noprint data = WORK.tbl;
tables
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
%STR(*)
%END;
%SCAN(&L_FRQ.,&I_FRQ.,' ')_rnk
%END;
/ cmh1
;
output out = WORK.cmc(keep = l_mhrrc1 u_mhrrc1) cmh1;
run;
proc freq noprint data = WORK.tbl;
tables
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
%STR(*)
%END;
%SCAN(&L_FRQ.,&I_FRQ.,' ')_rnk
%END;
/ sparse out = WORK.cnt(keep = count)
;
run;
proc transpose data = WORK.cnt out = WORK.trp; run;
data WORK.res; merge WORK.trp WORK.cmh WORK.cmc(rename=(l_mhrrc1=l_mhrrcx u_mhrrc1=u_mhrrcx)); run;
proc sql noprint;
create table WORK.out as
select
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
,
%END;
"&&%SCAN(&L_FRQ.,&I_FRQ.,' ')"
as %SCAN(&L_FRQ.,&I_FRQ.,' ')
%END;
, col1 as a label = "cell A"
, col3 as b label = "cell B"
, col2 as c label = "cell C"
, col4 as d label = "cell D"
, (a/c)*((c+d)/(a+b)) as cmh_crude label = "PRR crude"
, l_mhrrcx as cmhlcrude label = "PRR crude lower CL"
, u_mhrrcx as cmhucrude label = "PRR crude upper CL"
, _mhrrc1_ as cmh_strat label = "PRR stratified"
, l_mhrrc1 as cmhlstrat label = "PRR stratified lower CL"
, u_mhrrc1 as cmhustrat label = "PRR stratified upper CL"
from WORK.res
;
%IF &I_C_FRQ. = 1 %THEN %DO;
drop table &SRC..&TBL.%SCAN(&FCT.,1,_);
%END;
create table &SRC..&TBL.%SCAN(&FCT.,1,_) as
%IF &I_C_FRQ. > 1 %THEN %DO;
select *
from &SRC..&TBL.%SCAN(&FCT.,1,_)
union
%END;
select *
from out
;
quit;
%END; /* end FREQ loop */
proc sql;
create table &SRC..&TBL.%SCAN(&FCT.,1,_) as
%IF %SUBSTR(&RST.,2,1) ne C %THEN %DO;
select *
from &SRC..&TBL.%SCAN(&FCT.,1,_) as tbl
%IF %SUBSTR(&RST.,1,1) ne R %THEN %DO;
right
%END;
join &SRC..&TBL. as fct
on
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
and
%END;
fct.%SCAN(&L_FRQ.,&I_FRQ.,' ')
= tbl.%SCAN(&L_FRQ.,&I_FRQ.,' ')
%END;
%END;
%ELSE %DO;
select *
from &SRC..&TBL.%SCAN(&FCT.,1,_)
%END;
;
quit;
%LET otb = &TBL.;
%LET tbl = &TBL.%SCAN(&FCT.,1,_);
%END; /* end PRR main loop */
%MEND cr_tbl_4_fct;
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
31
Howto
%MACRO cr_tbl_4_fct;
%GLOBAL fct src tbl otb;
%LOCAL i_frq n_frq l_frq i_ctl n_ctl l_ctl;
%IF %SCAN(&FCT.,1,'_') eq PRR %THEN %DO; /* start PRR main loop */
%LET n_frq = %SCAN(&FCT.,2,'_');
%LET n_ctl = %SCAN(&FCT.,3,'_');
proc sql noprint;
select
into
from
where
and
and
;
select
,
into
,
from
where
and
and
and
;
insert
select
,
into
,
from
where
and
and
and
;
name
:p_key
dictionary.columns
libname = %UPCASE("&SRC.")
memname = %UPCASE("&TBL.")
varnum eq 1
insert
insert
select
from
;
insert
select
from
;
into footer values("Stratification performed by: &L2CTL.");
into footer
compbl(put(count(*),8.)||" lines processed from %UPCASE(&DB_USR.).&TBL.")
&SRC..&TBL.
name
name
:l_frq separated by ' '
:l2frq separated by ' * '
dictionary.columns
libname = %UPCASE("&SRC.")
memname = %UPCASE("&TBL.")
varnum gt 1
varnum le %EVAL(&N_FRQ. + 1)
into header values("PRR values calculated for &L2FRQ.");
name
name
:l_ctl separated by ' , '
:l2ctl separated by ' * '
dictionary.columns
libname = %UPCASE("&SRC.")
memname = %UPCASE("&TBL.")
varnum gt %EVAL(&N_FRQ. + 1)
varnum le %EVAL(&N_FRQ. + &N_CTL. + 1)
into footer
compbl("Frequency tables based on "||put(count(distinct &P_KEY.),8.)||" distinct values from field &P_KEY.")
&SRC..&TBL.
create table &SRC..c_frq as
select distinct
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
,
%END;
%SCAN(&L_FRQ.,&I_FRQ.,' ')
%END;
from &SRC..&TBL.
where
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
and
%END;
%SCAN(&L_FRQ.,&I_FRQ.,' ') is not null
%END;
;
%LET n_c_frq = &SQLOBS.;
insert into footer values("&N_C_FRQ. combinations from &L2FRQ. processed");
quit;
data _null_;
set &SRC..c_frq;
%DO i_frq = 1 %TO &N_FRQ.;
call symput("%SCAN(&L_FRQ.,&I_FRQ.,' ')"||'_'||trim(left(put(_N_,4.))),trim(left(%SCAN(&L_FRQ.,&I_FRQ.,' '))));
%END;
run;
%DO i_c_frq = 1 %TO &N_C_FRQ.; /* start FREQ loop */
proc sql;
create view WORK.tbl as
select distinct
&P_KEY. ,
%DO i_frq = 1 %TO &N_FRQ.;
%LET %SCAN(&L_FRQ.,&I_FRQ.,' ') = %QUOTE(&&%SCAN(&L_FRQ.,&I_FRQ.,' ')_&I_C_FRQ.);
case
when sum( %SCAN(&L_FRQ.,&I_FRQ.,' ') = "&&%SCAN(&L_FRQ.,&I_FRQ.,' ')" ) > 0
then -1
else 0
end
as %SCAN(&L_FRQ.,&I_FRQ.,' ')_rnk ,
%END;
&L_CTL.
from &SRC..&TBL.
group by
&P_KEY.
;
quit;
proc freq noprint data = WORK.tbl;
tables &L2CTL.
%DO i_frq = 1 %TO &N_FRQ.;
%STR(*) %SCAN(&L_FRQ.,&I_FRQ.,' ')_rnk
%END;
/ cmh1
;
output out = WORK.cmh(keep = _mhrrc1_ l_mhrrc1 u_mhrrc1) cmh1;
run;
proc freq noprint data = WORK.tbl;
tables
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
%STR(*)
%END;
%SCAN(&L_FRQ.,&I_FRQ.,' ')_rnk
%END;
/ cmh1
;
output out = WORK.cmc(keep = l_mhrrc1 u_mhrrc1) cmh1;
run;
proc freq noprint data = WORK.tbl;
tables
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
%STR(*)
%END;
%SCAN(&L_FRQ.,&I_FRQ.,' ')_rnk
%END;
/ sparse out = WORK.cnt(keep = count)
;
run;
select
into
from
where
and
and
;
name
:p_key
dictionary.columns
libname = %UPCASE("&SRC.")
memname = %UPCASE("&TBL.")
varnum eq 1
proc transpose data = WORK.cnt out = WORK.trp; run;
data WORK.res; merge WORK.trp WORK.cmh WORK.cmc(rename=(l_mhrrc1=l_mhrrcx u_mhrrc1=u_mhrrcx)); run;
proc sql noprint;
create table WORK.out as
select
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
,
%END;
"&&%SCAN(&L_FRQ.,&I_FRQ.,' ')"
as %SCAN(&L_FRQ.,&I_FRQ.,' ')
%END;
, col1 as a label = "cell A"
, col3 as b label = "cell B"
, col2 as c label = "cell C"
, col4 as d label = "cell D"
, (a/c)*((c+d)/(a+b)) as cmh_crude label = "PRR crude"
, l_mhrrcx as cmhlcrude label = "PRR crude lower CL"
, u_mhrrcx as cmhucrude label = "PRR crude upper CL"
, _mhrrc1_ as cmh_strat label = "PRR stratified"
, l_mhrrc1 as cmhlstrat label = "PRR stratified lower CL"
, u_mhrrc1 as cmhustrat label = "PRR stratified upper CL"
from WORK.res
;
%IF &I_C_FRQ. = 1 %THEN %DO;
drop table &SRC..&TBL.%SCAN(&FCT.,1,_);
%END;
create table &SRC..&TBL.%SCAN(&FCT.,1,_) as
%IF &I_C_FRQ. > 1 %THEN %DO;
select *
from &SRC..&TBL.%SCAN(&FCT.,1,_)
union
%END;
select *
from out
;
quit;
%END; /* end FREQ loop */
proc sql;
create table &SRC..&TBL.%SCAN(&FCT.,1,_) as
%IF %SUBSTR(&RST.,2,1) ne C %THEN %DO;
select *
from &SRC..&TBL.%SCAN(&FCT.,1,_) as tbl
%IF %SUBSTR(&RST.,1,1) ne R %THEN %DO;
right
%END;
join &SRC..&TBL. as fct
on
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
and
%END;
fct.%SCAN(&L_FRQ.,&I_FRQ.,' ')
= tbl.%SCAN(&L_FRQ.,&I_FRQ.,' ')
%END;
%END;
%ELSE %DO;
select *
from &SRC..&TBL.%SCAN(&FCT.,1,_)
%END;
;
quit;
%LET otb = &TBL.;
%LET tbl = &TBL.%SCAN(&FCT.,1,_);
%END; /* end PRR main loop */
%MEND cr_tbl_4_fct;
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
32
Howto
%MACRO cr_tbl_4_fct;
%GLOBAL fct src tbl otb;
%LOCAL i_frq n_frq l_frq i_ctl n_ctl l_ctl;
%IF %SCAN(&FCT.,1,'_') eq PRR %THEN %DO; /* start PRR main loop */
%LET n_frq = %SCAN(&FCT.,2,'_');
%LET n_ctl = %SCAN(&FCT.,3,'_');
proc sql noprint;
select
into
from
where
and
and
;
select
,
into
,
from
where
and
and
and
;
insert
select
,
into
,
from
where
and
and
and
;
name
:p_key
dictionary.columns
libname = %UPCASE("&SRC.")
memname = %UPCASE("&TBL.")
varnum eq 1
insert
insert
select
from
;
insert
select
from
;
into footer values("Stratification performed by: &L2CTL.");
into footer
compbl(put(count(*),8.)||" lines processed from %UPCASE(&DB_USR.).&TBL.")
&SRC..&TBL.
name
name
:l_frq separated by ' '
:l2frq separated by ' * '
dictionary.columns
libname = %UPCASE("&SRC.")
memname = %UPCASE("&TBL.")
varnum gt 1
varnum le %EVAL(&N_FRQ. + 1)
into header values("PRR values calculated for &L2FRQ.");
name
name
:l_ctl separated by ' , '
:l2ctl separated by ' * '
dictionary.columns
libname = %UPCASE("&SRC.")
memname = %UPCASE("&TBL.")
varnum gt %EVAL(&N_FRQ. + 1)
varnum le %EVAL(&N_FRQ. + &N_CTL. + 1)
into footer
compbl("Frequency tables based on "||put(count(distinct &P_KEY.),8.)||" distinct values from field &P_KEY.")
&SRC..&TBL.
create table &SRC..c_frq as
select distinct
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
,
%END;
%SCAN(&L_FRQ.,&I_FRQ.,' ')
%END;
from &SRC..&TBL.
where
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
and
%END;
%SCAN(&L_FRQ.,&I_FRQ.,' ') is not null
%END;
;
%LET n_c_frq = &SQLOBS.;
insert into footer values("&N_C_FRQ. combinations from &L2FRQ. processed");
quit;
data _null_;
set &SRC..c_frq;
%DO i_frq = 1 %TO &N_FRQ.;
call symput("%SCAN(&L_FRQ.,&I_FRQ.,' ')"||'_'||trim(left(put(_N_,4.))),trim(left(%SCAN(&L_FRQ.,&I_FRQ.,' '))));
%END;
run;
%DO i_c_frq = 1 %TO &N_C_FRQ.; /* start FREQ loop */
proc sql;
create view WORK.tbl as
select distinct
&P_KEY. ,
%DO i_frq = 1 %TO &N_FRQ.;
%LET %SCAN(&L_FRQ.,&I_FRQ.,' ') = %QUOTE(&&%SCAN(&L_FRQ.,&I_FRQ.,' ')_&I_C_FRQ.);
case
when sum( %SCAN(&L_FRQ.,&I_FRQ.,' ') = "&&%SCAN(&L_FRQ.,&I_FRQ.,' ')" ) > 0
then -1
else 0
end
as %SCAN(&L_FRQ.,&I_FRQ.,' ')_rnk ,
%END;
&L_CTL.
from &SRC..&TBL.
group by
&P_KEY.
;
quit;
proc freq noprint data = WORK.tbl;
tables &L2CTL.
%DO i_frq = 1 %TO &N_FRQ.;
%STR(*) %SCAN(&L_FRQ.,&I_FRQ.,' ')_rnk
%END;
/ cmh1
;
output out = WORK.cmh(keep = _mhrrc1_ l_mhrrc1 u_mhrrc1) cmh1;
run;
proc freq noprint data = WORK.tbl;
tables
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
%STR(*)
%END;
%SCAN(&L_FRQ.,&I_FRQ.,' ')_rnk
%END;
/ cmh1
;
output out = WORK.cmc(keep = l_mhrrc1 u_mhrrc1) cmh1;
run;
proc freq noprint data = WORK.tbl;
tables
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
%STR(*)
%END;
%SCAN(&L_FRQ.,&I_FRQ.,' ')_rnk
%END;
/ sparse out = WORK.cnt(keep = count)
;
run;
proc transpose data = WORK.cnt out = WORK.trp; run;
data WORK.res; merge WORK.trp WORK.cmh WORK.cmc(rename=(l_mhrrc1=l_mhrrcx u_mhrrc1=u_mhrrcx)); run;
proc sql noprint;
create table WORK.out as
select
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
,
%END;
"&&%SCAN(&L_FRQ.,&I_FRQ.,' ')"
as %SCAN(&L_FRQ.,&I_FRQ.,' ')
%END;
, col1 as a label = "cell A"
, col3 as b label = "cell B"
, col2 as c label = "cell C"
, col4 as d label = "cell D"
select
,
into
,
from
where
and
and
and
;
name
name
:l_frq separated by ' '
:l2frq separated by ' * '
dictionary.columns
libname = %UPCASE("&SRC.")
memname = %UPCASE("&TBL.")
varnum gt 1
varnum le %EVAL(&N_FRQ. + 1)
, (a/c)*((c+d)/(a+b)) as cmh_crude label = "PRR crude"
, l_mhrrcx as cmhlcrude label = "PRR crude lower CL"
, u_mhrrcx as cmhucrude label = "PRR crude upper CL"
, _mhrrc1_ as cmh_strat label = "PRR stratified"
, l_mhrrc1 as cmhlstrat label = "PRR stratified lower CL"
, u_mhrrc1 as cmhustrat label = "PRR stratified upper CL"
from WORK.res
;
%IF &I_C_FRQ. = 1 %THEN %DO;
drop table &SRC..&TBL.%SCAN(&FCT.,1,_);
%END;
create table &SRC..&TBL.%SCAN(&FCT.,1,_) as
%IF &I_C_FRQ. > 1 %THEN %DO;
select *
from &SRC..&TBL.%SCAN(&FCT.,1,_)
union
%END;
select *
from out
;
quit;
%END; /* end FREQ loop */
proc sql;
create table &SRC..&TBL.%SCAN(&FCT.,1,_) as
%IF %SUBSTR(&RST.,2,1) ne C %THEN %DO;
select *
from &SRC..&TBL.%SCAN(&FCT.,1,_) as tbl
%IF %SUBSTR(&RST.,1,1) ne R %THEN %DO;
right
%END;
join &SRC..&TBL. as fct
on
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
and
%END;
fct.%SCAN(&L_FRQ.,&I_FRQ.,' ')
= tbl.%SCAN(&L_FRQ.,&I_FRQ.,' ')
%END;
%END;
%ELSE %DO;
select *
from &SRC..&TBL.%SCAN(&FCT.,1,_)
%END;
;
quit;
%LET otb = &TBL.;
%LET tbl = &TBL.%SCAN(&FCT.,1,_);
%END; /* end PRR main loop */
%MEND cr_tbl_4_fct;
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
33
Howto
%MACRO cr_tbl_4_fct;
%GLOBAL fct src tbl otb;
%LOCAL i_frq n_frq l_frq i_ctl n_ctl l_ctl;
%IF %SCAN(&FCT.,1,'_') eq PRR %THEN %DO; /* start PRR main loop */
%LET n_frq = %SCAN(&FCT.,2,'_');
%LET n_ctl = %SCAN(&FCT.,3,'_');
proc sql noprint;
select
into
from
where
and
and
;
select
,
into
,
from
where
and
and
and
;
insert
select
,
into
,
from
where
and
and
and
;
name
:p_key
dictionary.columns
libname = %UPCASE("&SRC.")
memname = %UPCASE("&TBL.")
varnum eq 1
insert
insert
select
from
;
insert
select
from
;
into footer values("Stratification performed by: &L2CTL.");
into footer
compbl(put(count(*),8.)||" lines processed from %UPCASE(&DB_USR.).&TBL.")
&SRC..&TBL.
name
name
:l_frq separated by ' '
:l2frq separated by ' * '
dictionary.columns
libname = %UPCASE("&SRC.")
memname = %UPCASE("&TBL.")
varnum gt 1
varnum le %EVAL(&N_FRQ. + 1)
into header values("PRR values calculated for &L2FRQ.");
name
name
:l_ctl separated by ' , '
:l2ctl separated by ' * '
dictionary.columns
libname = %UPCASE("&SRC.")
memname = %UPCASE("&TBL.")
varnum gt %EVAL(&N_FRQ. + 1)
varnum le %EVAL(&N_FRQ. + &N_CTL. + 1)
into footer
compbl("Frequency tables based on "||put(count(distinct &P_KEY.),8.)||" distinct values from field &P_KEY.")
&SRC..&TBL.
create table &SRC..c_frq as
select distinct
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
,
%END;
%SCAN(&L_FRQ.,&I_FRQ.,' ')
%END;
from &SRC..&TBL.
where
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
and
%END;
%SCAN(&L_FRQ.,&I_FRQ.,' ') is not null
%END;
;
%LET n_c_frq = &SQLOBS.;
insert into footer values("&N_C_FRQ. combinations from &L2FRQ. processed");
quit;
data _null_;
set &SRC..c_frq;
%DO i_frq = 1 %TO &N_FRQ.;
call symput("%SCAN(&L_FRQ.,&I_FRQ.,' ')"||'_'||trim(left(put(_N_,4.))),trim(left(%SCAN(&L_FRQ.,&I_FRQ.,' '))));
%END;
run;
%DO i_c_frq = 1 %TO &N_C_FRQ.; /* start FREQ loop */
proc sql;
create view WORK.tbl as
select distinct
&P_KEY. ,
%DO i_frq = 1 %TO &N_FRQ.;
%LET %SCAN(&L_FRQ.,&I_FRQ.,' ') = %QUOTE(&&%SCAN(&L_FRQ.,&I_FRQ.,' ')_&I_C_FRQ.);
case
when sum( %SCAN(&L_FRQ.,&I_FRQ.,' ') = "&&%SCAN(&L_FRQ.,&I_FRQ.,' ')" ) > 0
then -1
else 0
end
as %SCAN(&L_FRQ.,&I_FRQ.,' ')_rnk ,
%END;
&L_CTL.
from &SRC..&TBL.
group by
&P_KEY.
;
quit;
proc freq noprint data = WORK.tbl;
tables &L2CTL.
%DO i_frq = 1 %TO &N_FRQ.;
%STR(*) %SCAN(&L_FRQ.,&I_FRQ.,' ')_rnk
%END;
/ cmh1
;
output out = WORK.cmh(keep = _mhrrc1_ l_mhrrc1 u_mhrrc1) cmh1;
run;
proc freq noprint data = WORK.tbl;
tables
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
%STR(*)
%END;
%SCAN(&L_FRQ.,&I_FRQ.,' ')_rnk
%END;
/ cmh1
;
output out = WORK.cmc(keep = l_mhrrc1 u_mhrrc1) cmh1;
run;
proc freq noprint data = WORK.tbl;
tables
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
%STR(*)
%END;
%SCAN(&L_FRQ.,&I_FRQ.,' ')_rnk
%END;
/ sparse out = WORK.cnt(keep = count)
;
run;
proc transpose data = WORK.cnt out = WORK.trp; run;
data WORK.res; merge WORK.trp WORK.cmh WORK.cmc(rename=(l_mhrrc1=l_mhrrcx u_mhrrc1=u_mhrrcx)); run;
proc sql noprint;
create table WORK.out as
select
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
,
%END;
"&&%SCAN(&L_FRQ.,&I_FRQ.,' ')"
as %SCAN(&L_FRQ.,&I_FRQ.,' ')
%END;
, col1 as a label = "cell A"
, col3 as b label = "cell B"
, col2 as c label = "cell C"
, col4 as d label = "cell D"
select
,
into
,
from
where
and
and
and
;
name
name
:l_ctl separated by ' , '
:l2ctl separated by ' * '
dictionary.columns
libname = %UPCASE("&SRC.")
memname = %UPCASE("&TBL.")
varnum gt %EVAL(&N_FRQ. + 1)
varnum le %EVAL(&N_FRQ. + &N_CTL. + 1)
, (a/c)*((c+d)/(a+b)) as cmh_crude label = "PRR crude"
, l_mhrrcx as cmhlcrude label = "PRR crude lower CL"
, u_mhrrcx as cmhucrude label = "PRR crude upper CL"
, _mhrrc1_ as cmh_strat label = "PRR stratified"
, l_mhrrc1 as cmhlstrat label = "PRR stratified lower CL"
, u_mhrrc1 as cmhustrat label = "PRR stratified upper CL"
from WORK.res
;
%IF &I_C_FRQ. = 1 %THEN %DO;
drop table &SRC..&TBL.%SCAN(&FCT.,1,_);
%END;
create table &SRC..&TBL.%SCAN(&FCT.,1,_) as
%IF &I_C_FRQ. > 1 %THEN %DO;
select *
from &SRC..&TBL.%SCAN(&FCT.,1,_)
union
%END;
select *
from out
;
quit;
%END; /* end FREQ loop */
proc sql;
create table &SRC..&TBL.%SCAN(&FCT.,1,_) as
%IF %SUBSTR(&RST.,2,1) ne C %THEN %DO;
select *
from &SRC..&TBL.%SCAN(&FCT.,1,_) as tbl
%IF %SUBSTR(&RST.,1,1) ne R %THEN %DO;
right
%END;
join &SRC..&TBL. as fct
on
%DO i_frq = 1 %TO &N_FRQ.;
%IF &I_FRQ. gt 1 %THEN %DO;
and
%END;
fct.%SCAN(&L_FRQ.,&I_FRQ.,' ')
= tbl.%SCAN(&L_FRQ.,&I_FRQ.,' ')
%END;
%END;
%ELSE %DO;
select *
from &SRC..&TBL.%SCAN(&FCT.,1,_)
%END;
;
quit;
%LET otb = &TBL.;
%LET tbl = &TBL.%SCAN(&FCT.,1,_);
%END; /* end PRR main loop */
%MEND cr_tbl_4_fct;
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
34
Questions welcome
Wolf-Dieter Batz: New Metadata Concepts
12-Jul-16
35
THANK YOU
Wolf-Dieter Batz: New Metadata Concepts
Download