Files
macroarray/extras/Paper_108-PharmaSUG2024.sas
Bart Jablonski 05e19344d5 PharmaSUG 2024 conference materials
- Paper 108
- Code snippets
2024-05-21 23:53:26 +02:00

1320 lines
18 KiB
SAS

/** Macro Variabel Array **/
/* mva A */
%LET a1 = Macro;
%let a2 = Variables;
%let a3 = Array;
/* mva B */
data _null_;
length val $ 9;
do val = "Next", "Macro", "Variables", "Array";
i+1;
CALL SYMPUTX(cats("b", i), val);
end;
run;
/* mva C */
data have;
input val $9.;
cards;
Proc
SQL
Macro
Variables
Array
;
run;
proc SQL;
select val
INTO :c1-
from have
;
quit;
/* preview */
%put _user_;
/* call to mva A */
%macro loop(i);
%do i = 1 %to &i.;
%put NOTE- &i.) &&a&i;
%end;
%mend loop;
%loop(3)
/* SETUP *//*
----------------------------------------------------------------------
Only the first time:
^^^^^^^^^^^^^^^^^^^^
filename packages "/path/to/my/packages";
filename SPFinit url "https://bit.ly/SPFinit"; *(1);
%include SPFinit;
%installPackage(SPFinit macroArray BasePlus) *(2);
----------------------------------------------------------------------
On start of fresh SAS session:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
filename packages "/path/to/my/packages";
%include packages(SPFinit.sas);
%loadPackageS(macroArray BasePlus) *(3);
----------------------------------------------------------------------*/
/*footnotes**********************************************************
*(1); Bitly points to:
https://github.com/yabwon/SAS_PACKAGES/blob/main/SPF/SPFinit.sas
*(2); BasePlus is loaded for this macro:
%symdelGlobal(quiet);
resetline;
*(3); symdelGlobal can be just "cherry picked" from BasePlus:
%loadPackage(BasePlus, cherryPick=symdelGlobal)
*********************************************************************/
/* simple macro array - numeric */
%array(X[6] (101:106))
%put _user_;
data _null_;
array X[6] (101:106);
put (_all_) (=/);
run;
/* simple macro array - character */
%array(X[7] $ ("A" "B" "C" "D" "E" "F" "G"))
%put _user_;
data _null_;
array X[7] $ ("A" "B" "C" "D" "E" "F" "G");
put (_all_) (=/);
run;
/* vnames=Y option and "character" macro array */
%array(days_A[*] Monday Tuesday Wednesday Saturday, vnames=Y)
%array(days_B[4] $ 20 ("Monday" "Tuesday" "Wednesday" "Saturday"))
%put _user_;
/* vnames = N */
%array(days_A[*] Monday Tuesday Wednesday Saturday)
%put _user_;
/* easy special characters masking */
%array(days_B[4] $ 20 ('&Monday.' '%Tuesday()' "W;e;d;n;e;s;d;a;y;" "S,a,t,u,r,d,a,y"))
%put _user_;
%let Monday=123;
%macro Tuesday();
4567890
%mend Tuesday;
%array(days_D[4] $ 20 ('&Monday' '%Tuesday()' "Wednesday" "Saturday"))
%put _user_;
%put &days_D1. &days_D2. &days_D3. &days_D4.;
/* dates works too */
%array(days_E[4] ('20may2024'd '21may2024'd '22may2024'd '25may2024'd))
%put _user_;
/* function= argument */
/* three constant expressions */
%array(e[1:3] $, function = "A" )
/* first six powers of 2 */
%array(f[0:5], function = (2**_i_) )
/* five random numbers form uniform distribution on (0,1) interval */
%array(g[0:4], function = ranuni(123) )
/* a formated list of twelve months */
%array(h[0:11] $ 11, function = put(intnx("MONTH", '19may2024'd, _i_), yymmd.))
/* Fibonacci sequence, first ten elements */
%array(i[10] (10*0)
,function = ifn(_i_ < 2, 1, sum(i[max(_i_-2,1)], i[max(_i_-1,2)]) )
,before=put _all_
,after=put _all_
)
%put _user_;
/* before= and after= */
%array(j[10]
,function = round(rand('Uniform',0,1),0.001)
,before = call streaminit(42)
,after = call sortn(of j[*]); j[1]=0; j[10]=1; put _all_
)
%put _user_;
/* indexes */
%array(k[5:7] $ ("fifth" "sixth" "seventh"))
%put _user_;
/* negative indexes */
%array(l[-3:3], function = 2**_i_)
%put _user_;
/* why not "_" for negative indexes?
%array(m[-2:-1] (1 2))
%array(m_[1:2] (1 2))
%put _user_;
*/
/* using datasets */
%array(ds=sashelp.class(where=(age=13)), vars=name)
%put _user_;
/* multiple variables */
%array(ds=sashelp.class(where=(age=14)), vars=name height weight)
%put _user_;
/* "Highlander" aka "there can be only one only one" */
data work.class_1 work.class_2;
set sashelp.class(obs=3);
run;
%array(ds=work.class_:, vars=name) /* !!! */
/* no data set */
%array(ds=NoSuchDataSet, vars=name) /* !!! */
%put _user_;
/* no data */
data work.class;
stop;
set sashelp.class;
run;
%array(ds=work.class, vars=name) /* !!! */
%array(ds=sashelp.class(where=(age>20)), vars=name) /* !!! */
%put _user_;
/* no variable */
%array(ds=sashelp.class, vars=x) /* !!! !!! !!! */
%put _user_;
/* unique values only and the pipe operator */
%array(ds=sashelp.class, vars=age|)
%put _user_;
/* alternative names and # operator */
%array(ds=sashelp.class, vars=name#N age|A)
%put _user_;
/* adding quotes */
%array(Letters[6] $ 3, function = byte(rank('A')+_I_-1) , q=2) /* 2 for "double" */
%array(ds=sashelp.class(obs=6), vars=name, q=1) /* 1 for 'single' */
%put _user_;
/* session setting for macro memory */
proc options group = MACRO;
run;
/*
MEXECSIZE - specifies the maximum macro size that can be executed in memory.
MSYMTABMAX - specifies the maximum amount of memory available to the macro
variable symbol table or tables.
MVARSIZE - specifies the maximum size for a macro variable that is stored in memory.
*/
/* looping over macro arrays */
%macro someMacro();
%array(ARRX[1:3] $ ("first" "second" "third"))
%do i=1 %to 3;
%put for &=i. value is &&ARRX&i ;
%end;
%mend someMacro;
resetline;
%someMacro()
%put _user_;
/* macarray = Y */
%macro someMacro2();
%array(ARRY[1:3] $ ("first" "second" "third"), macarray=Y)
%do i=1 %to 3;
%put for &=i. value is %ARRY(&i) ;
%end;
%mend someMacro2;
resetline;
%someMacro2()
%put _user_;
%macro someMacro3();
%array(ds=sashelp.cars, vars=Origin|ARRZ, macarray=Y)
%do i=&ARRZLBOUND. %to &ARRZHBOUND.;
%put for &=i. value is %ARRZ(&i) ;
%end;
%mend someMacro3;
resetline;
%someMacro3()
%put _user_;
/* alternating values */
%array(ABC[3] (1:3), macarray=Y);
%put _user_;
%let %ABC(2,i) = 999999999; /* i for "input mode" */
%put _user_;
/* "filling blanks" */
%let ARR1=1;
%let ARR3=3;
%let ARR5=5;
%let ARRX7=7;
%put _user_;
%array(ARR,macarray=m)
%put _user_;
/* get help on array */
%helpPackage(macroArray, '%array()')
/* deleteMacArray - cleaning after work */
%array(Letters[26] $ 26
, function = repeat(byte(rank("A")+_I_-1),_I_-1)
, macarray=Y
, q=2)
%put _user_;
%macro someMacroX();
%do i=&LettersLBOUND. %to &LettersHBOUND.;
%put %Letters(&i.);
%end;
%mend someMacroX;
%someMacroX()
%deleteMacArray(Letters)
/* %deleteMacArray(Letters, macarray=Y) */
%put _user_;
/* get help on deleteMacArray */
%helpPackage(macroArray, '%deleteMacArray()')
/* sortMacroArray - reordering values */
%array(n[6] $ 3 ("C33" "B22" "A11" "A1" "A2" "X42"))
%put _user_;
%sortMacroArray(n)
%put _user_;
%array(hij [4:9] $ 512 ("C33" "B22" "A11" "A01" "A02" "X42"), macarray=Y)
%put _user_;
%sortMacroArray(hij, outSet = A_NULL_(compress=char), valLength=3)
%put _user_;
/* get help on sortMacroArray */
%helpPackage(macroArray, '%sortMacroArray()')
/* appendArray and concatArrays - combibining macro arrays */
%array(o[2:4] $ 1 ("A" "B" "C"))
%array(p[3] (1 2 3))
%put _user_;
%appendArray(o, p)
%put _user_;
%array(q[2:4] $ 1 ("E" "F" "G"), macarray=Y)
%array(r[3] (4 5 6), macarray=Y)
%put _user_;
%concatArrays(q, r)
%put _user_;
/* get help on appendArray and concatArrays */
%helpPackage(macroArray, '%concatArrays()')
%helpPackage(macroArray, '%appendArray()')
/* do_over - iterating over array values and more! */
%array(ds=sashelp.class(where=(age=12)), vars=name, q=2, macarray=Y)
%put _user_;
/* loop over NAME - stupid example *//* options mprint; */
%do_over(name)
/*
"James"
"Jane"
"John"
"Louise"
"Robert"
*/
/* loop over NAME - not stupid example */
%put %do_over(name);
/* loop over NAME - practical example */
data test;
set sashelp.class;
where name in (%do_over(name));
run;
/* between= operator */
%array(ds=sashelp.class(where=(age=12)), vars=name, macarray=Y) /* no q=*/
%put "%do_over(name, between=|)";
data test;
set sashelp.class;
where find("%do_over(name, between=|)", name,"IT");
run;
/* phrase= operator */
/* Ex.1 */
%array(ds=sashelp.class, vars=age|, macarray=Y)
%put %do_over(age);
options mprint;
data
%do_over(age, phrase=%nrstr( data_%age(&_i_.) ))
;
set sashelp.class;
select(age);
%do_over(age, phrase=%nrstr(
/* when ( 15 ) output data_15; */
when ( %age(&_i_.) ) output data_%age(&_i_.);
))
otherwise put "unknown Age value!";
end;
run;
options nomprint;
/* Ex.2 */
%macro someMacro4(period);
title "Running macro for &period.";
data data_for_&period.;
put "Running macro for &period.";
x = &period.;
run;
proc print;
run;
title;
%mend someMacro4;
%someMacro4(199912)
%array(date[4], function=202400 + _i_, macarray=Y)
%put %do_over(date);
%do_over(date, phrase=%nrstr(
%someMacro4( %date(&_i_.) )
))
/* Ex.3 */
%array(ds=sashelp.class(obs=6), vars=name age, macarray=Y)
%do_over(name, phrase=%nrstr(
%put &_i_.. %name(&_i_.) has %age(&_i_.) years.;
))
/* which= operator */
%array(test[*] x01-x06, vnames= Y, macarray=Y)
%put 1) %do_over(test);
%put 2) %do_over(test, which=H:L:-1);
%array(test[*] x0001-x007, vnames= Y, macarray=Y)
%put #%do_over(test)#;
%put #%do_over(test, which= h:l:-1 h:l:-2 h:l:-3)#;
%array(test[*] x01-x99, vnames=Y, macarray=Y)
%put **%do_over(test, which= L:3 97:H, between=**)**;
/* get help on do_over */
%helpPackage(macroArray, '%do_over()')
/* do_over2 and do_over3 - iterating over multiple arrays */
%array(alpha[*] a b c, vnames=Y, macarray=Y)
%array( beta[2] (101 102), macarray=Y)
%do_over2(beta, alpha, phrase = %NRSTR(
%put alpha=%alpha(&_j_.), beta=%beta(&_i_) ;
));
resetline;
/*options nofullstimer nostimer;*/
%array(one[3] $ ("A" "B" "C"), macarray=Y)
%array(two[3] ( 1 2 3 ), macarray=Y)
%macro letsPlay(x,y);
data &x&y;
x="&x.";
y= &y. ;
put x= y=;
run;
%mend letsPlay;
%do_over2(one, two
,phrase = %NRSTR(
%letsPlay(%one(&_I_.), %two(&_J_))
))
/*options stimer;*/
%array(a[2] (0 1), macarray=Y)
%do_over3(a, a, a
, phrase = %NRSTR(
%put sum(%a(&_I_.), %a(&_J_), %a(&_K_)) = %sysevalf(%a(&_I_.) + %a(&_J_) + %a(&_K_));
))
/* get help on do_over2 and do_over3 */
%helpPackage(macroArray, '%do_over2()')
%helpPackage(macroArray, '%do_over3()')
/* make_do_over - when three is not enough! */
%make_do_over(2);
%make_do_over(5);
%array(a5_[2] (0 1), macarray=Y)
%do_over5(a5_, a5_, a5_, a5_, a5_
,phrase = %NRSTR(
%put (%a5_(&_I1_.),%a5_(&_I2_.),%a5_(&_I3_.),%a5_(&_I4_.),%a5_(&_I5_.));
))
/* get help on make_do_over */
%helpPackage(macroArray, '%make_do_over()')
/* zipArrays - zipper for macro arrays */
%array(a[*] $ x1-x3 ("A" "B" "C"))
%array(b[*] x1-x5 (11:15))
%put _user_;
%zipArrays(a, b);
%put _user_;
/* result= and macarray= */
%array(a[6] (1:6))
%array(b[3] (77 88 99))
%zipArrays(a, b, result=A_and_B, macarray=Y);
%put %do_over(A_and_B);
%put %A_and_B(5);
/* operator= */
%array(c[0:4] (000 100 200 300 400))
%array(d[2:16] (1002:1016))
%zipArrays(c, d, operator=+, result=C_plus_D, macarray=Y);
%put %do_over(C_plus_D);
%put %C_plus_D(1);
%zipArrays(c, d, operator=*, result=C_times_D, macarray=Y);
%put %do_over(C_times_D);
%put %C_times_D(1);
/* function= argBf= format= */
%array(one[3] A B C, vnames=Y)
%array(two[5] p q r s t, vnames=Y)
%zipArrays(
one
,two
,function = catx
,argBf = %str( & )
,format = $quote.
,macarray=Y
)
%put %do_over(onetwo);
/* reuse= */
%array(e[3] (10 20 30))
%array(f[2] (5:6))
%zipArrays(e, f, reuse=n, operator=+, macarray=Y, result=_noReuse);
%zipArrays(e, f, reuse=y, operator=+, macarray=Y, result=_yesReuse);
%zipArrays(e, f, reuse=cp, operator=+, macarray=Y, result=_cartProdReuse);
%put %do_over(_noReuse);
%put %do_over(_yesReuse);
%put %do_over(_cartProdReuse);
/* "incompatibility" */
%array(e[3] (10 20 30))
%array(f[2] $ ("A" "B"))
%zipArrays(e, f, reuse=n, operator=+, macarray=Y, result=_noReuseX);
%zipArrays(e, f, reuse=y, operator=+, macarray=Y, result=_yesReuseX);
%zipArrays(e, f, reuse=cp, operator=+, macarray=Y, result=_cartProdReuseX);
%put %do_over(_noReuseX);
%put %do_over(_yesReuseX);
%put %do_over(_cartProdReuseX);
/* the same name */
%array(e[5] (10 20 30 40 50))
%array(f[3] (7 8 9))
%put _user_;
%zipArrays(e, f, reuse=y, operator=+, macarray=Y, result=e);
%put _user_;
/* !!!
options mprint;
%zipArrays(e, f, reuse=y, operator=+, macarray=Y, result=F);
%put _user_;
*/
%array(a[3] (100 200 300))
%put _user_;
%zipArrays(a, a
, function=SUM
, reuse=CP
, macarray=Y
)
%put %do_over(AA);
/* macro dictionary */
%mcDictionary(myDict)
resetline;
%myDict(List)
resetline;
%myDict(ADD)
%put _user_;
/* note on "special cases" */
%myDict(ADD)
%myDict(List)
%myDict(ADD,key=,data=ABC)
%myDict(List)
%myDict(ADD,key=_,data=EFG)
%myDict(List)
%myDict(ADD,key=%str( ),data=HIJ)
%myDict(List)
%myDict(ADD,key=%str( ),data=KLM)
%myDict(List)
%put _user_;
%mcDictionary(myDict)
resetline;
%myDict(ADD,key=A,data=I)
%myDict(A ,key=B,data=<3)
%myDict(A ,key=C,data=SAS)
%myDict(L)
resetline;
%put
A:%myDict(CHECK,key=A)
B:%myDict(C,key=B)
C:%myDict(C,key=C)
D:%myDict(C,key=D) /* ! */
;
resetline;
%put
"%myDict(FIND,key=A)
%myDict(F,key=B)
%myDict(F,key=C)
%myDict(F,key=D)"
;
/*
%myDict(A,key=D,data=9)
*/
resetline;
%myDict(DEL,key=B)
%myDict(L)
%myDict(CLEAR)
%myDict(L)
resetline;
%mcDictionary()
%mcDictionary(_)
%mcDictionary(ABCDEFGHIJKLMNOPQ) %* bad;
%mcDictionary(ABCDEFGHIJKLM) %* good;
%mcDictionary(ABCDEFGHIJKLM) %* good;
%ABCDEFGHIJKLM(ADD,key=A,data=I)
%ABCDEFGHIJKLM(A ,key=B,data=<3)
%ABCDEFGHIJKLM(A ,key=C,data=SAS)
%ABCDEFGHIJKLM(L)
%put _user_;
/* populating from data set */
data work.have;
input kVar :$1. @3 dVar $20.;
cards;
A I
B <3
C SAS Institute
C SAS software
C SAS9
;
run;
proc print;
run;
%mcDictionary(myDSdict,DCL,DS=work.have,k=kVar,d=dVar)
%myDSdict(L)
/* small usecase */
data work.metadata;
infile cards missover;
input key :$16. data :$128.;
cards;
ID ABC-123-XYZ
path /path/to/study/data
cutoffDT 2023-01-01
startDT 2020-01-01
endDT 2024-12-31
MedDRA v26.0
XXX 123
YYY
ZZZ 789
OBS 13
;
run;
proc print;
run;
%mcDictionary(Study,dcl,DS=work.metadata)
%put _user_;
%Study(L)
/* "size matters" ;-) */
%put *%Study(F,key=ID)**%Study(C,key=ID)*;
%put *%Study(F,key=id)**%Study(C,key=id)*;
title1 "Study %Study(F,key=ID) is located at %Study(F,key=path)";
title2 "it starts %Study(F,key=startDT) and ends %Study(F,key=endDT)";
footnote1 "MedDRA version: %Study(F,key=MedDRA)";
footnote2
%if %Study(C,key=XXX)
%then %do; "XXX: %Study(F,key=XXX)"; %end;
;
proc print data=sashelp.class(obs=%Study(F,key=OBS));
run;
title;
footnote;
/* lets have a big use case */
/*
%symdelGlobal(quiet);
options mprint nofullstimer stimer;
resetline;
*/
/* project driving data */
/* data set with list of functions to "run over data" */
data work.functions;
input fName $12.;
cards;
sum
mean
median
min
max
nmiss
std
range
stderr
var
;
run;
title "list of functions";
proc print;
run;
/* data set with project metadata */
data work.projectMetadata;
infile cards dsd dlm=",";
input key :$16. data :$128.;
cards;
ID,ABC-123-XYZ
TITLE,Use case of the MacroArray package
PATH,/path/to/study/data
INDATASET,sashelp.cars
OUTDATASET,work.results
VARIABLE,invoice
GROUPBY,origin
STARTDT,2020-01-01
ENDDT,2024-12-31
;
run;
title "project metadata";
proc print;
run;
title;
resetline;
/* project code */
/* create macro array FN and macro dictionary PRJ */
%array(ds=work.functions, vars=fName#fN, macarray=Y)
%mcDictionary(prj, DCL, DS=work.projectMetadata)
%put _user_;
title1 "Title: %prj(F,key=TITLE)";
title2 "Project %prj(F,key=ID), located at: %prj(F,key=PATH)";
title3 "starts %prj(F,key=STARTDT) and ends %prj(F,key=ENDDT)";
footnote1 "Input data set: %prj(F,key=INDATASET)";
footnote2 "Output data set: %prj(F,key=OUTDATASET)";
footnote3 "Analyzed variable: %prj(F,key=VARIABLE)";
/* check if the grouping variable exists */
footnote4
%if %prj(C,key=GROUPBY) %then
%do; "Analysis in groups by %prj(F,key=GROUPBY)" %end;
;
/*
proc print data=sashelp.vtitle;
run;
*/
/* aggregate data */
options mprint;
Proc SQL feedback;
create table %prj(F,key=OUTDATASET) as
select
/* check if the grouping variable exists */
%if %prj(C,key=GROUPBY) %then %do; %prj(F,key=GROUPBY), %end;
/* loop over aggregating functions */
%do_over(fN
,phrase=%NRSTR(
/* apply function to analysisVariable
and name the result "analysisVariable_functionName" */
%fN(&_i_.)(%prj(F,key=VARIABLE)) as %prj(F,key=VARIABLE)_%fN(&_i_.)
/* e.g., avg(x) as x_avg */
)
,between=%str(,)
)
from
%prj(F,key=INDATASET)
/* check if the grouping variable exists */
%if %prj(C,key=GROUPBY) %then
%do;
group by
%prj(F,key=GROUPBY)
%end;
;
Quit;
/* print data */
proc print data = %prj(F,key=OUTDATASET) ;
run;
title;
footnote;
%prj(CLEAR)
%deleteMacArray(fN, macarray=Y)
/* end of project code */
/* since you get up to here, here is a bonus for you */
/*- a bonus - aboiut SAS macro BUG ;-) -*/
%let Monday=123;
%macro Tuesday();
ABC
%mend Tuesday;
%array(days_E[4] $ 20 ('&Monday' '%Tuesday()' "Wednesday" "Saturday"), macArray=Y)
%put _user_;
%put %days_E(1) %days_E(2) %days_E(3) %days_E(4);
data X%days_E(2)Y;
set sashelp.class;
run;
data X%unquote(%days_E(2))Y;
set sashelp.class;
run;
data X%days_E(1)Y;
set sashelp.class;
if 123=%days_E(1);
if "123"="%days_E(1)";
run;
%let bug=%days_E(2);
%let bugFix=%days_E(2);
data _null_;
set sashelp.vmacro;
where name="BUG";
put value hex20.;
run;
data X&bugFix.Y;
set sashelp.class;
run;
data _null_;
x=resolve('data X%days_E(2)Y;set sashelp.class;run;');
put x hex100.;
call execute(x);
run;