%macro setup; **** defines common librefs and SAS options.; %let path=c:\projects\cdisc\cdisc-sas-book; %let sasroot=%sysfunc(sysget(SASROOT)); %put sasroot=&sasroot path=&path; options ls=256 nocenter sasautos=("&sasroot/core/sasmacro", "&path/programs/macros") ; libname sdtm "&path/data/sdtm"; libname adam "&path/data/adam"; proc format; value _0n1y 0 = 'N' 1 = 'Y' ; value avisitn 1 = '3' 2 = '6' ; value popfl 0 - high = 'Y' other = 'N' ; value $trt01pn 'Analgezia HCL 30 mg' = '1' 'Placebo' = '0' ; value agegr1n 0 - 54 = "1" 55-high= "2" ; value agegr1_ 1 = "<55 YEARS" 2 = ">=55 YEARS" ; value $aereln 'NOT RELATED' = '0' 'POSSIBLY RELATED' = '1' 'PROBABLY RELATED' = '2' ; value $aesevn 'MILD' = '1' 'MODERATE' = '2' 'SEVERE' = '3' ; value relgr1n 0 = 'NOT RELATED' 1 = 'RELATED' ; value evntdesc 0 = 'PAIN RELIEF' 1 = 'PAIN WORSENING PRIOR TO RELIEF' 2 = 'PAIN ADVERSE EVENT PRIOR TO RELIEF' 3 = 'COMPLETED STUDY PRIOR TO RELIEF' ; run; %mend setup; %macro cfb(indata= ,outdata= ,avalvar= ,dayvar= ,keepvars= ); *---------------------------------------------------------; * Macro for deriving ABLFL, BASE, CHG, and PCHG for a BDS * formatted ADaM data set; * Assumes baseline is the last non-missing value on or before * study day 1 and that the INDDATA is an SDTM data set with * variables USUBJID and VISITNUM *---------------------------------------------------------; proc sort data = &indata out = &outdata (rename = (&avalvar = aval)); by usubjid visitnum; run; * Baseline is defined as the last non-missing value prior to study day 1 first dose; * (note, values on Day 1 are assumed to occur before the first dose); data base1 (keep = usubjid visitnum) base2 (keep = usubjid base); set &outdata; where &dayvar<=1 and aval > .z; by usubjid visitnum; rename aval = base; if last.usubjid; run; * Do one merge to identify the baseline record; data &outdata; merge &outdata base1 (in = inbase); by usubjid visitnum; if inbase then ablfl = 'Y'; run; * Do another merge to merge in the baseline value; data &outdata; merge &outdata base2; by usubjid; %if &keepvars^= %then keep &keepvars; ; chg = aval - base; pchg = chg/base*100; run; %mend cfb; %macro dtc2dt(dtcvar , prefix=a, refdt= ); if length(trim(&dtcvar))=10 and index(&dtcvar,'--')=0 then &prefix.dt = input(&dtcvar, yymmdd10.); else if length(&dtcvar)=16 and index(&dtcvar,'--')=0 and index(&dtcvar,'-:')=0 then do; &prefix.dtm = input(trim(&dtcvar)||":00", e8601dt19.); &prefix.dt = datepart(&prefix.dtm); * optionally add formats: ; * format &prefix.dtm datetime16.; end; %if &refdt^= %then %do; if .<&prefix.dt<&refdt then &prefix.dy = &prefix.dt - &refdt; else if &prefix.dt>=&refdt then &prefix.dy = &prefix.dt - &refdt + 1; %end; * optionally add formats: ; * format &prefix.dt yymmdd10. ; %mend dtc2dt; %macro mergsupp(sourcelib=library, outlib=WORK, domains= , suppqual=0); *------------------------------------------------------------; * Merge supplemental qualifiers into the parent SDTM domain; * This can either be for an entire library or for specified * domains; *------------------------------------------------------------; %local domain; %** de-normalize suppqual and merge into the given domain; %macro domainx(domain= ,suppqual=0); %local suppdata idvar varlist nvars; %if &suppqual %then %let suppdata=suppqual; %else %let suppdata=supp&domain; ; %* count the number of supplemental qualifiers for the given domain; proc sort data = &sourcelib..&suppdata out = nvars nodupkey; where rdomain=upcase("&domain"); by qnam idvar; run; data _null_; set nvars end=eof; by qnam idvar; length varlist $200; retain varlist; if not first.qnam then put 'PROB' 'LEM: More than one IDVAR for the domain-- ' rdomain= qnam= idvar= ; else do; nvars + 1; varlist = trim(varlist) || " " || trim(qnam); end; if eof then do; call symput("nvars", put(nvars, 2.)); call symput("varlist", trim(left(varlist))); call symput("idvar", trim(idvar)); end; run; %put domain=&domain idvar=&idvar nvars=&nvars varlist=&varlist; proc sort data = &sourcelib..&suppdata out = supp&domain; where rdomain=upcase("&domain"); by usubjid idvar idvarval; run; %* determine whether IDVAR in the parent domain is character or numeric; %if &idvar^= %then %do; %let dsetnum=%sysfunc(open(&sourcelib..&domain)); %let varnum=%sysfunc(varnum(&dsetnum,&idvar)); %let idtype=%sysfunc(vartype(&dsetnum,&varnum)); %let rc=%sysfunc(close(&dsetnum)); %end; %else %let idtype= ; data supp&domain; set supp&domain; by usubjid idvar idvarval; drop q: idvarval idvar i rdomain; length &varlist $200.; retain &varlist; array vars{*} &varlist; if first.idvarval then do i = 1 to dim(vars); vars{i} = ''; end; do i = 1 to dim(vars); if upcase(qnam)=upcase(vname(vars{i})) then vars{i} = qval; end; %** convert to numeric if numeric in the parent domain; %if &idvar^= and &idtype=N %then &idvar = input(idvarval, best.); %else %if &idvar^= %then &idvar = idvarval; ; if last.idvarval; run; proc sort data = supp&domain; by usubjid &idvar; proc sort data = &sourcelib..&domain out = __tmp; by usubjid &idvar; data &outlib..&domain; merge __tmp supp&domain ; by usubjid &idvar; run; %mend domainx; %*-------------------------------------------------------; %* If DOMAINS parameter specified, then loop through those %* domains; %* otherwise, dynamically identify the SUPPxx data sets and %* go through them all; %*-------------------------------------------------------; %let _wrd=1; %if &DOMAINS^= %then %do %while(%scan(&domains,&_wrd)^= ); %let domain=%scan(&domains,&_wrd); %domainx(domain=&domain,suppqual=0); %let _wrd=%eval(&_wrd+1); %end; %else %do; %** find all of the SUPPxx datasets and loop through each one; ods output members=members; proc contents data = &sourcelib.._all_ memtype=data nods ; run; data members; set members; if upcase(name)=:'SUPP' and upcase(name)^=:'SUPPQUAL' then do; rdomain = substr(name,5,2); put name= rdomain= ; output; end; else if upcase(name)=:'SUPPQUAL' then call symput("suppqual","1"); run; %** loop through each domain; proc sql noprint; select count(distinct rdomain) into :domn from %if &suppqual %then &sourcelib..suppqual; %else members; ; select distinct rdomain into :domain1 - :domain%left(&domn) from %if &suppqual %then &sourcelib..suppqual; %else members; ; %do _i=1 %to &domn; %domainx(domain=&&domain&_i,suppqual=&suppqual); %end; %end; %* if domains not specified explicitly...; %mend mergsupp;