SAS Certification Handout #12: Adv. Prog. Ch. 11-12

/************ Ch. 11 ******************/

%let HOnum=12; libname cert ':/jrstevens/Teaching/SAS_Cert/AdvNotes'; /* In SAS Studio, after creating SAS_Cert folder with username jrstevens: */ * libname cert '/home/jrstevens/SAS_Cert';

/* sales */ data cert.sales&HOnum; format LastName $9. Sales dollar8.; input LastName $ FirstName $ EmplID Division $ Contracts Volume Sales; label EmplID = "Employee ID"; cards; Smith B 315 A 45 10015 553312 Nelson E 333 A 23 32162 555827 Riley X 125 B 61 32631 748391 Uribe 612 C 22 43221 867159 Larson A 331 B 61 12553 521592 Larsen L 216 B 62 12632 123513 Rasmussen K 217 A 33 43336 125321 Butikofer J 338 A 62 33921 332521 Knecht P 861 B 11 43111 983152 Larson A 331 C 62 6123 381592 ;

/* pp. 400-406 Different ways for macros to be set up: 1. positional parameters 2. keyword parameters 3. mixed parameter lists (positional [first] and keyword) 4. varying number of parameters (PARMBUFF) */

/* 1. positional parameters */ %macro getAve(setname, varname);

proc means data=&setname mean; var &varname; run; Analysis Variable : Sales %mend; Mean getAve % (cert.sales&HOnum, Sales) 519238.00 /* Note no semicolon after call (p. 394) */

1

/* 2. keyword parameters */ %macro getExpAve(setname=&syslast, varname=X, power=1);

data &setname; set &setname; new&varname = &varname**&power; label new&varname = "Power &power of &varname";

proc means data=&setname mean; var new&varname; run; Analysis Variable : newSales Power %mend; 1 of Sales Mean %getExpAve(setname=cert.sales&HOnum, varname=Sales, 519238.00 power=1) Analysis Variable : newSales Power %getExpAve(setname=cert.sales&HOnum, 2 of Sales varname=Sales, power=2) Mean 345294119712

%getExpAve() Analysis Variable LOG: NOTE: Variable X is uninitialized. : newX Power 1 of X Mean

.

/* 3. mixed parameter lists */ %macro getNewExpAve(setname, varname, power=1);

data &setname; set &setname; new&varname = &varname**&power; label new&varname = "Power &power of &varname";

proc means data=&setname mean; var new&varname; Analysis Variable run; : newSales Power 2 of Sales %mend; Mean %getnewExpAve(cert.sales&HOnum, Sales, power=2) 345294119712

2

/* 4. varying number of parameters (PARMBUFF) -- usually involve automatic macro variable SYSPBUFF and a DO loop */ %macro tempCall / parmbuff;

%put SYSPBUFF contains: &SYSPBUFF; %local i; %do i=1 %to %sysfunc(countw(&SYSPBUFF));

%let data_i = %scan(&SYSPBUFF,&i); proc corr data=&data_i nosimple; var Y X; run;

%end;

%mend; data temp1; set cert.sales&HOnum; Y = Sales; X = Volume; data temp2; set cert.sales&HOnum; Y = Sales; X = Contracts; data temp3; set cert.sales&HOnum; Y = Volume; X = Contracts;

%tempCall(temp1, temp2) Pearson Correlation Coefficients, N = 10 Prob > |r| under H0: Rho=0

Y X

Y 1.00000 0.37750

tempCall 0.2822 % (temp3)

Pearson Correlation Coefficients, N = 10 X 0.37750 1.00000 Prob > |r| under H0: Rho=0 0.2822 Y X

Y 1.00000 -0.67666 0.0317

X -0.67666 1.00000 Pearson Correlation Coefficients, N = 10 Prob > |r| under H0: Rho=0 0.0317 Y X

Y 1.00000 -0.53754 0.1090

X -0.53754 1.00000 0.1090

3

/* tools for debugging macros: (p. 393) MCOMPILENOTE - LOG message when macro is compiled (p. 397) MPRINT - text to be compiled is sent to LOG (p. 398) MLOGIC - LOG message for each step of macro execution (p. 399) %* comments [why not /* ... ?] (p. 412-415) MPRINTNEST & MLOGICNEST [specifically for nested macros] %PUT at various steps to see what current macro values are

pp. 426-430 DO loops */ options mcompilenote=all; %macro showNesting(maxI);

%do i=2 %to &maxI %by 2; /* %BY default is 1 */ %* p. 399 says can comment this way inside a macro ; * but this works too [both up to semicolon] ; %getnewExpAve(cert.sales&HOnum, Contracts, power=&i)

%end; LOG: NOTE: The macro SHOWNESTING completed compilation %mend; without errors. 15 instructions 312 bytes. options mprint mlogic mprintnest mlogicnest; %showNesting(7)

LOG: 369 370 options mprint mlogic mprintnest mlogicnest; 371 %showNesting(7) MLOGIC(SHOWNESTING): Beginning execution. MLOGIC(SHOWNESTING): Parameter MAXI has value 7 MLOGIC(SHOWNESTING): %DO loop beginning; index variable I; start value is 2; stop value is 7; by value is 2. MPRINT(SHOWNESTING): * but this works too [both up to semicolon] ; MLOGIC(SHOWNESTING.GETNEWEXPAVE): Beginning execution. MLOGIC(SHOWNESTING.GETNEWEXPAVE): Parameter SETNAME has value cert.sales12 MLOGIC(SHOWNESTING.GETNEWEXPAVE): Parameter VARNAME has value Contracts MLOGIC(SHOWNESTING.GETNEWEXPAVE): Parameter POWER has value 2 MPRINT(SHOWNESTING.GETNEWEXPAVE): data cert.sales12; MPRINT(SHOWNESTING.GETNEWEXPAVE): set cert.sales12; MPRINT(SHOWNESTING.GETNEWEXPAVE): newContracts = Contracts**2; MPRINT(SHOWNESTING.GETNEWEXPAVE): label newContracts = "Power 2 of Contracts"; ...

4

options mcompilenote=none nomprint nomlogic Analysis Variable nomprintnest nomlogicnest; : newContracts Power

2 of Contracts

Mean

2322.20

Analysis Variable : newContracts Power 4 of Contracts /* pp. 406-415: Mean %GLOBAL identifies macro variable as 7783597.40 existing both outside and inside macros.

%LOCAL identifies macro variable as only Analysis Variable exisiting inside macro itself (precedence : newContracts Power over same-named global variables). 6 of Contracts Mean %LET assigns macro variable value (inside or outside macro definition) 28329987642 */ data cert.sales&HOnum; set cert.sales&HOnum; row=_n_; run; %let A=3; %macro tryThis(b); %local A; %let A=1; proc print data=cert.sales&HOnum; where row <= &a; var LastName Sales Row; title1 &b ; run; Note %local precedence %mend;

Obs LastName Sales row

%tryThis("Note %local precedence"); 1 Smith $553,312 1

Now with global A=3 proc print data=cert.sales&HOnum; Obs LastName Sales row where row <= &a; var LastName Sales Row; 1 Smith $553,312 1 title1 "Now with global A=&a" ; 2 Nelson $555,827 2 run; title1; 3 Riley $748,391 3

5

/* pp. 415-426 %IF ... %THEN ... <%ELSE> ... -- only works inside macro, and only refers to macro variables -- case-sensitive condition checks (so maybe use %UPCASE, p. 425) -- evaluated to determine text for input stack */ %macro trial(X); %if %upcase(&x) = GLM %then %do; proc glm data=cert.sales&HOnum; class division; model Sales = Division; run; %end; %else %if %upcase(&x) = REG %then %do; proc reg data=cert.sales&HOnum; model Sales = Contracts; run; %end; %else %do; %put ; %put X=&X option resulted in no output; %put ; %end; %mend; The GLM Procedure

Dependent Variable: Sales Source DF Sum of Squares Mean Square F Value Pr > F

%trial(glm) Model 2 109579816127 54789908063 0.59 0.5785 Error 7 647280374551 92468624936

Corrected Total 9 756860190678

...

The REG Procedure

Analysis of Variance trial % (Reg) Source DF Sum of Mean F Value Pr > F Squares Square

Model 1 2.18696E11 2.18696E11 3.25 0.1090

Error 8 5.381642E11 67270523142

Corrected Total 9 7.568602E11

...

6

LOG: %trial(something) X=something option resulted in no output

/* p. 420 WHERE ALSO -- not a macro thing, just to add multiple WHERE statements (AND) */ proc print data=cert.sales&HOnum; where division="A"; Obs LastName Sales Division where also Sales > 500000; 1 Smith $553,312 A var lastname sales division; title1; 2 Nelson $555,827 A run; /* LOG: NOTE: WHERE clause has been augmented. */

/* p. 430: %EVAL for simple integer arithmetic & logical p. 432: %SYSEVALF for floating-point arithmetic ( incl. non-integer) */

%let a = 7; %let b = 8; %let c = &a + &b; %put C = &C; LOG: C = 7 + 8

%let a = 7; %let b = 8; %let c = %eval(&a + &b); %put C = &C; LOG: C = 15

%let a = 7.3; %let b = -8.13; %let c = %eval(&a + &b); LOG: ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric operand is required. The condition was: 7.3 + -8.13

%let a = 7.3; %let b = -8.13; %let c = %sysevalf(&a + &b); %put C = &C; /* LOG: C = -0.83 */ LOG: C = -0.83

/* pp. 395-397: how macros are processed */

7

/************ Ch. 12 ******************/

/* Four main ways to store macro definitions in external files: 1. (p. 443) %INCLUDE (calls full .sas files, not just macros) 2. (p. 446) store macro as SOURCE in CATALOG 3. (p. 450) autocall (with OPTIONS MAUTOSOURCE SASAUTOS= ) 4. (p. 454) stored compiled macro using STORE option (with OPTIONS MSTORED SASMSTORE= )

The 'source' program is the original .sas file defining the macro.

*/

/* 1. (p. 443) %INCLUDE (calls full .sas files, not just macros) */ %include "C:/jrstevens/Teaching/SAS_Cert/AdvNotes/getAve1.sas"; %getAve1(cert.sales&HOnum, Sales)

Source Program (getAve1.sas) SAS Output

Using %include %macro getAve1(setname, varname); proc means data=&setname mean; The MEANS Procedure var &varname; Analysis Variable title1 "Using %include"; : Sales run; Mean %mend; 519238.00

/* 2. (p. 446) store macro as SOURCE in CATALOG; with getAve2.sas open: File --> Save As Object --> (make new Mymacs folder in CERT library) --> enter name as getAve2 and description as "Macro to get average of a given variable in given data set", then Save */

/* See summary (value here is seeing description) */ proc catalog cat=cert.Mymacs; contents; quit;

Contents of Catalog CERT.MYMACS # Name Type Create Date Modified Date Description 1 GETAVE2 SOURCE 02/18/2015 02/18/2015 Macro to get average of a given 12:57:25 12:57:25 variable in given data set

8

/* Call it -- still need %include */ filename getAve2 catalog "cert.mymacs.getAve2.source"; %include getAve2; %getAve2(cert.sales&HOnum, Sales)

Source Program (getAve2.sas) SAS Output

Using CATALOG Source %macro getAve2(setname, varname); proc means data=&setname mean; The MEANS Procedure var &varname; Analysis Variable title1 "Using CATALOG Source "; : Sales run; Mean %mend; 519238.00

/* 3. (p. 450) autocall (with OPTIONS MAUTOSOURCE SASAUTOS= ) - First save getAve3.sas (same name as defined macro) in specified folder - This approach essentially makes all .sas files in the referenced Folder available for macro calls (so SAS will look in all of them to find called macros not already available to it (first in currently-defined, then in permanent, and then in autocall).

*/ options mautosource sasautos="C:\jrstevens\Teaching\SAS_Cert\AdvNotes\macros12"; %getAve3(cert.sales&HOnum, Sales)

Source Program (getAve3.sas, SAS Output in ...\macros12 folder) Using autocall %macro getAve3(setname, varname); proc means data=&setname mean; The MEANS Procedure var &varname; Analysis Variable title1 "Using autocall"; : Sales run; Mean %mend; 519238.00

/* 4. (p. 454) stored compiled macro using STORE option (with OPTIONS MSTORED SASMSTORE= ) ; need to first execute source program */ /* See it: */ proc catalog cat=cert.sasmacr; contents; run;

9

Contents of Catalog CERT.SASMACR # Name Type Create Date Modified Date Description 1 GETAVE4 MACRO 02/18/2015 02/18/2015 Get Average of VARNAME from 14:14:18 14:14:18 SETNAME

/* Call it after pointing first to where stored macros reside: */ options mstored sasmstore=cert; %getAve4(cert.sales&HOnum, Sales)

Source Program (getAve4.sas) SAS Output

Using autocall libname cert 'C:/jrstevens/Teaching/SAS_Cert/AdvNotes'; The MEANS Procedure options mstored sasmstore=cert; Analysis Variable : Sales /* This stores compiled macros defined here in CERT library, and creates Mean sasmacr.sas7bcat there */ 519238.00

%macro getAve4(setname, varname) / store source des="Get Average of VARNAME from SETNAME"; proc means data=&setname mean; var &varname; title1 "Using Stored Compiled Macro"; run; %mend;

/* p. 457: See source of stored compiled macro */ options mstored sasmstore=cert; %copy getAve4 / source;

LOG: %macro getAve4(setname, varname) / store source des="Get Average of VARNAME from SETNAME"; proc means data=&setname mean; var &varname; title1 "Using Stored Compiled Macro"; run; %mend;

10

/************ Misc. SAS Macro Tools (not on exams) ****************/

/* Sometimes you need a unique value for a variable every time you call a macro (like if you needed different-named figures to avoid over-writing figures from previous macro calls). One hack approach is to use the system clock. */ %global timevar; data macroTemp; timevar=1000*time(); data macroTemp; set macroTemp; format timevar best9.; usetime=timevar; CALL SYMPUT('timevar',usetime);

%global runnum; %let runnum=&timevar; proc sgplot data=cert.sales&HOnum; scatter x=Contracts y=Sales; title1 "Run Number:" &runnum; run;

11

/* Sometimes inside a macro (and even outside a macro), you want to extract "data" from a table produced by a PROC, so you can do something with it. You may also want to suppress output for some intermediate steps inside a macro. Take advantage of the Output Delivery System to do both of these. */ ods trace on; /* Send (to LOG) the names of all tables in output */ proc corr data=cert.sales&HOnum; var Sales Contracts; run; ods trace off; /* Turn it off */

The CORR Procedure 2 Variables: Sales Contracts

Simple Variable N Mean Std Dev Sum Minimum Maximum Sales 10 519238 289992 5192380 123513 983152 Contracts 10 44.20000 20.23638 442.00000 11.00000 62.00000

Pearson Correlation Coefficients, N = 10 Prob > |r| under H0: Rho=0 Sales Contracts

Sales 1.00000 -0.53754 0.1090

Contracts -0.53754 1.00000 0.1090

LOG:

Output Added: ------Name: VarInformation Label: Variables Information Template: base.corr.VarInfo Path: Corr.VarInformation ------

12

Output Added: ------Name: SimpleStats Label: Simple Statistics Template: base.corr. Path: Corr.SimpleStats ------

Output Added: ------Name: PearsonCorr Label: Pearson Correlations Template: base.corr.StackedMatrix Path: Corr.PearsonCorr

ods select none; /* Suppress output (using NOPRINT option in PROC will prevent table from being constructed. */ proc corr data=cert.sales&HOnum; var Sales Contracts; ods output SimpleStats = out1; title1 'This will not show up'; run; ods select default; /* Return to output */

proc print data=out1; title1 'Simple Statistics Table from PROC CORR'; title2 '(as a SAS data set)'; run;

Simple Statistics Table from PROC CORR (as a SAS data set)

Obs Variable NObs Mean StdDev Sum Min Max 1 Sales 10 519238 289992 5192380 123513 983152 2 Contracts 10 44.20000 20.23638 442.00000 11.00000 62.00000

13

/* See a couple of examples of macros:

http://www.stat.usu.edu/jrstevens/stat5100/resid_num_diag.sas - demo here: http://www.stat.usu.edu/jrstevens/stat5100/3.a.Diagnostics.pdf

http://www.stat.usu.edu/jrstevens/stat5100/simEnv.macro.sas - demo here: http://www.stat.usu.edu/jrstevens/stat5100/14.a.LogisticRegression.pdf

*/

14