**************************************************************************************************** * PROGRAM OVERVIEW **************************************************************************************************** * * PROGRAM: create_tableofcontents.sas * Created (mm/dd/yyyy): 02/09/2021 * *-------------------------------------------------------------------------------------------------- * PURPOSE: This macro createas a dataset with 1 row per table and figure that is used as the * report table of contents * * Program inputs: * * Program outputs: * -tableofcontents: dataset containing table of contents * * PARAMETERS: * * Programming Notes: * * *-------------------------------------------------------------------------------------------------- * CONTACT INFO: * Sentinel Coordinating Center * info@sentinelsystem.org * ***************************************************************************************************; %macro create_tableofcontents(); %put =====> MACRO CALLED: create_tableofcontents; /*********************************************************************************************/ /* Utility macro to add row to tableofcontents file */ /*********************************************************************************************/ %macro addtotoc(tabnum=, caption=, appendixtype=); data tableofcontents; set tableofcontents end=eof; output; if eof then do; tabnum = "&tabnum."; caption = "&caption."; appendixtype = "&appendixtype."; output; end; run; %mend; /*********************************************************************************************/ /* Initialize empty table and table number */ /*********************************************************************************************/ data tableofcontents; length tabnum $25 caption $500 appendixtype $30; call missing(tabnum, caption, appendixtype); run; %let number = 1; %let tablenum = 1; /*********************************************************************************************/ /* Build table of contents */ /*********************************************************************************************/ /*********************************************************************************************/ /* Glossary rows */ /*********************************************************************************************/ %addtotoc(tabnum=Glossary (CIDA), caption=List of Terms to Define the Cohort Identification and Descriptive Analysis (CIDA) Found in this Report); %if %str("&reporttype") = %str("T2L2") | %str("&reporttype") = %str("T4L2") %then %do; %addtotoc(tabnum=Glossary (PSA), caption=List of Terms to Define the Propensity Score Analysis (PSA) Found in this Report); %end; /*********************************************************************************************/ /* Baseline Table */ /*********************************************************************************************/ %if %eval(&numbaselinetablegrp.>0) %then %do; /*counter for determining table letter*/ %let tablecount = 1; /* counter for determining table number */ %let tablenum = 2; /*loop through each baseline table*/ %do b = 1 %to &numbaselinetablegrp.; %let analysisgrp = ; %let analysisgrp2 = ; %let baselinegroupnum = ; %let pregnancylabel = ; %let pregnancylabel2 = ; %let includenonpregnant = N; /*for L2 tables - need to reference PS/CS specific files to pull additional parameters*/ %let ratio = F; %let psfile = ; %let weightlabel = ; %let weightscheme = ; %let pstrim = ; %let percentiles=; %let ratiolabel = ; %let caliperlabel = ; %let truncationlabel = ; %let psestimategrp = ; %let unadjusted = ; data _null_; set baselinefile(where=(order=&b.)); if _n_ = 1 then do; call symputx('analysisgrp', analysisgrp); call symputx('runid', runid); call symputx('cohort', cohort); call symputx('unique_psestimate',unique_psestimate); call symputx('unique_psestimate_orig',unique_psestimate); %if %sysfunc(prxmatch(m/T4L1/i,&reporttype.)) > 0 %then %do; if cohort in ('preg', 'nopreg') then do; call symputx('pregnancylabel',preg_outcome_label); end; call symputx('includenonpregnant', upcase(includenonpregnant)); %end; if missing(baselinegroupnum)=0 then call symputx('baselinegroupnum', baselinegroupnum); end; /*if baselinegroupnum is specified, a 2nd row will exist in the file*/ if _n_ = 2 then do; if missing(baselinegroupnum)=0 then do; call symputx('analysisgrp2',analysisgrp); %if %index(&reporttype,T4L1) %then %do; call symputx('pregnancylabel2', preg_outcome_label); %end; end; end; run; %if %sysfunc(prxmatch(m/T2L2|T4L2/i,&reporttype.)) > 0 %then %do; data _null_; set pscs_masterinputs(where=(analysisgrp = "&analysisgrp." and missing(subgroup))); call symputx('psfile', strip(file)); call symputx('psestimategrp', psestimategrp); call symput('unadjusted', 'Unadjusted '); /*for unadjusted table label*/ if file = 'psmatchfile' then do; call symputx('ratio',upcase(ratio)); if upcase(ratio)='F' then call symputx("ratiolabel",'Fixed Ratio 1:'||strip(put(ceiling, 8.))); if upcase(ratio)='V' then call symputx("ratiolabel",'Variable Ratio 1:'||strip(put(ceiling, 8.))); call symputx("caliperlabel", cat(', Caliper: ', caliper)); end; if file = 'stratificationfile' then do; call symputx('pstrim', pstrim); call symputx('percentiles', percentiles); if missing(strataweight) =0 then call symputx('weightscheme', strataweight); if upcase(strataweight)= 'ATE' or missing(strataweight) then call symputx("weightlabel","Average Treatment Effect (ATE)"); else if upcase(strataweight)= 'ATT' then call symputx("weightlabel","Average Treatment Effect in the Treated (ATT)"); end; if file = 'iptwfile' then do; if upcase(ipweight)= 'ATE' then call symputx("weightlabel","Average Treatment Effect (ATE)"); else if upcase(ipweight)= 'ATES' then call symputx("weightlabel","Average Treatment Effect, Stabilized (ATES)"); else if upcase(ipweight)= 'ATT' then call symputx("weightlabel","Average Treatment Effect in the Treated (ATT)"); call symputx('truncationlabel',strip(put(truncweight, best.))); end; run; %end; /*For L1 tables, determine if only 1 baseline table and set &tablecount to 0. Will occur if all the following are true: - 1 monitoring period - DP stratification = N - max(order) in baselinefile = 1 For L2 tables, tablecount assigned in macro baselinetoc*/ %if %eval(&b.=1) & %eval(&look_start.) = %eval(&look_end.) & &stratifybydp. = N & %eval(&numbaselinetablegrp.=1) %then %do; %if %sysfunc(prxmatch(m/T2L2|T4L2/i,&reporttype.)) = 0 %then %do; %let tablecount = 0; %end; %end; /*Assign labels*/ %let baselinelabel = ; %let grouplabel = &analysisgrp.; %let psestimatelabel = &psestimategrp.; %if %length(&baselinegroupnum.)>0 %then %do; %let grouplabel2 = &analysisgrp2.; %end; %isdata(dataset=labelfile); %if %eval(&nobs.>0) %then %do; data _null_; set labelfile(in=a where=(group="&analysisgrp" and runid = "&runid")) %if %length(&baselinegroupnum.)>0 %then %do; labelfile(in=b where=(group="&analysisgrp2" and runid = "&runid")) %end; %if %sysfunc(prxmatch(m/T2L2|T4L2/i,&reporttype.)) > 0 %then %do; labelfile(in=c where=(group="&psestimategrp" and runid = "&runid")) %end; ; if a then do; if labeltype = 'grouplabel' then call symputx('grouplabel',label); if labeltype = 'baselinelabel' then call symputx('baselinelabel',cat(', ',strip(label), ',')); end; %if %length(&baselinegroupnum.)>0 %then %do; if b then do; if labeltype = 'grouplabel' then call symputx('grouplabel2',label); end; %end; %if %sysfunc(prxmatch(m/T2L2|T4L2/i,&reporttype.)) > 0 & &psfile. ne covstratfile %then %do; if c then do; if labeltype = 'grouplabel' then call symputx('psestimatelabel',label); end; %end; run; %end; /*1 block of code for both aggregate and DP tables*/ %macro baselinetoc(aggregated=, dpinparenthesis=, dpcomma=); * Process L2 subgroups; %let numsubgroups=0; %let subgroup=; %let subgroupcat=; %let subgrouptitle=; %if &reporttype = T2L2 or &reporttype = T4L2 %then %do; proc sort nodupkey data=Pscs_masterinputs(where=(analysisgrp="&analysisgrp." and runid="&runid." and not missing(subgroup))) out=_subgroups(keep=subgroup subgroupcat subgrouporder subgroupcatorder combinedlabel); by subgrouporder subgroupcatorder; run; proc sql noprint; select count(*) into :numsubgroups from _subgroups; quit; %end; %do sub=0 %to &numsubgroups.; %if &sub. > 0 %then %do; data _null_; set _subgroups; if _N_=&sub.; call symputx("subgrouptitle",combinedlabel); run; %let captionlabel = %bquote(&grouplabel.&pregnancylabel&baselinelabel.); %let unique_psestimate = 1; %end; %else %do; %let captionlabel = %bquote(&grouplabel.&pregnancylabel&baselinelabel.); %if %length(&baselinegroupnum.)>0 %then %do; %if %index(&reporttype,T4L1) %then %let pregnancylabel = &pregnancylabel2; %let captionlabel = %bquote(&grouplabel.&pregnancylabel and &grouplabel2.&pregnancylabel&baselinelabel.); %end; %if %sysfunc(prxmatch(m/T2L2|T4L2/i,&reporttype.)) >0 & &psfile. ne covstratfile %then %do; %let captionlabel = %bquote(&psestimatelabel.); %end; %let unique_psestimate = &unique_psestimate_orig; %end; /*For L2 queries, set &tablecount to 0 if covariate stratification AND no subgroups*/ %if &psfile. = covstratfile and &numsubgroups. = 0 and %eval(&look_start.) = %eval(&look_end.) and &stratifybydp. = N and %eval(&numbaselinetablegrp.=1) %then %let tablecount = 0; %if %eval(&unique_psestimate.) = 1 %then %do; %tableletter(); %addtotoc(tabnum=Table 1&tableletter., caption=%quote(&aggregated.&unadjusted.Characteristics of &captionlabel. &dpinparenthesis.in the &database. from &startdateformatted. to &&enddate&periodid.formatted.&subgrouptitle.)); %end; /*For L2 tables - up to 2 additional adjusted tables*/ %if %sysfunc(prxmatch(m/T2L2|T4L2/i,&reporttype.)) > 0 %then %do; /*PS Match Adjusted*/ %if &psfile. = psmatchfile %then %do; %tableletter(); %addtotoc(tabnum=Table 1&tableletter., caption=%quote(&aggregated.Adjusted Characteristics of &grouplabel. (Propensity Score Matched&dpcomma., &ratiolabel.&caliperlabel.), in the &database. from &startdateformatted. to &&enddate&periodid.formatted.&subgrouptitle.)); %end; /*Unweighted - IPTW and PS Stratum*/ %if (&psfile. = iptwfile & %eval(&unique_psestimate.) = 1) | (&psfile. = stratificationfile & ("&weightscheme." = "ATE" | "&weightscheme." = "ATT") & %eval(&pstrim.>=0)) %then %do; %tableletter(); %addtotoc(tabnum=Table 1&tableletter., caption=%quote(&aggregated.Unweighted Characteristics of &grouplabel. (Unweighted, Trimmed&dpcomma.) in the &database. from &startdateformatted. to &&enddate&periodid.formatted.&subgrouptitle.)); %end; /*Weighted - IPTW, PS Stratum, PS Stratification*/ %if &psfile. = iptwfile | &psfile. = stratificationfile %then %do; %if &psfile. = iptwfile %then %let stratumtitle = Inverse Probability of Treatment Weighted, Trimmed&dpcomma., Weight: &weightlabel., Truncation: &truncationlabel.%nrbquote(%); %else %if "&weightscheme." = "ATE" | "&weightscheme." = "ATT" %then %let stratumtitle = Propensity Score Stratum Weighted, Trimmed&dpcomma., Percentiles: &percentiles., Weight: &weightlabel.; %else %let stratumtitle =Propensity Score Stratified&dpcomma., Percentiles: &percentiles.; %tableletter(); %addtotoc(tabnum=Table 1&tableletter., caption=%quote(&aggregated.Weighted Characteristics of &grouplabel. (&stratumtitle.), in the &database. from &startdateformatted. to &&enddate&periodid.formatted.&subgrouptitle.)); %end; %end; /*Additional L2 tables*/ %end; /*Subgroups looping*/ %mend; /*loop through each periodid*/ %do periodid = %eval(&look_start.) %to %eval(&look_end.); /*Aggregated*/ %baselinetoc(%if %eval(&num_dp.)=1 %then %do; aggregated=, %end; %else %do; aggregated=%str(Aggregated ), %end; dpinparenthesis=, dpcomma=); /*Output separate table for each Data Partner - loop through each DP*/ %if &stratifybydp. = Y %then %do; %do dps = 1 %to %eval(&num_dp.); %let maskedID = %scan(&masked_dplist,&dps); %baselinetoc(aggregated = , dpinparenthesis=%str((&maskedid.) ), dpcomma=%str(, &maskedid.)); %end; %end; /*DP stratification*/ %end; /*loop through each periodid*/ %end; /*loop through each row in baselinefile*/ %end; /*include baseline tables in toc*/ /*********************************************************************************************/ /* Covariate Profile Table */ /*********************************************************************************************/ %if &numprofilecovarstoinclude > 0 %then %do; /* reset counter to reset table letter */ %let tablecount = 1; %do periodid = %eval(&look_start.) %to %eval(&look_end.); proc sql noprint ; select 'order='||strip(put(order,8.))||' and runid='||quote(strip(runid))||' and group='||quote(strip(group))||' and cohort='||quote(strip(cohort)) into :whereexpr separated by '@' from (select order, runid, group, case when(cohort is missing) then 'all' else cohort end as cohort from baselinefile where not missing(profilecovarstoinclude)) order by order; quit; %do wherenum = 1 %to %sysfunc(countw(&whereexpr, @)); %let where = %scan(&whereexpr,&wherenum, @); data _temp_agg_order_profile; set aggregate_profile(where=(periodid=&periodid and &where)); grouplabel=''; if cohort = 'mi' then group2=scan(group,1,'_'); else group2=group; if cohort = 'switch' then switchlabel = ' '; call symputx('runid',runid); run; %let profileswitches = 0; %if &reporttype = T6 %then %do; proc sql noprint; select max(switchstep) into :profileswitches from _temp_agg_order_profile; quit; %end; %isdata(dataset=_temp_agg_order_profile); %if &nobs > 0 %then %do; %do s = 0 %to &profileswitches; %if &reporttype = T6 %then %do; data _temp_agg_profile; set _temp_agg_order_profile (where=(switchstep = &s)); run; /* Join treatment file onto profile table to obtain product group */ proc sql noprint undo_policy=none; create table _temp_agg_profile as select a.*, b.group as productswitchgroup from _temp_agg_profile a inner join infolder.&&&runid._treatmentpathways b on a.group = b.analysisgrp and a.switchstep = b.switchevalstep; quit; %end; %else %do; proc datasets lib=work nolist; change _temp_agg_order_profile = _temp_agg_profile; run; %end; /* Check for existence of label file and join to profile dataset. Set grouplabel to missing if no labelfile */ %isdata(dataset=labelfile); %if &nobs > 0 %then %do; proc sql noprint undo_policy=none; create table _temp_agg_profile as select a.group, a.runid, a.order, a.periodid, b.label as grouplabel %if %index(&where,%str(cohort="mi")) %then %do; ,c.label as grouplabel2, a.group2 %end; %if %index(&where,%str(cohort="switch")) %then %do; ,d.label as switchlabel, a.productswitchgroup %end; from _temp_agg_profile(drop=grouplabel %if &reporttype = T6 %then %do; switchlabel %end;) a left join labelfile(where=(lowcase(labeltype)='grouplabel')) b on a.group = b.group and a.runid = b.runid %if %index(&where,%str(cohort="mi")) %then %do; left join labelfile(where=(lowcase(labeltype)='grouplabel')) c on a.group2 = c.group and a.runid = c.runid %end; %if %index(&where,%str(cohort="switch")) %then %do; left join labelfile(where=(lowcase(labeltype)='grouplabel')) d on a.productswitchgroup = d.group and a.runid = d.runid %end; ; quit; %end; %else %do; data _temp_agg_profile; set _temp_agg_profile; grouplabel2=''; switchlabel=''; run; %end; data _null_; set _temp_agg_profile(keep=group grouplabel order %if %index(&where,%str(cohort="mi")) %then %do; group2 grouplabel2 %end; %if %index(&where,%str(cohort="switch")) %then %do; productswitchgroup switchlabel %end; ); if _n_ = 1; if not missing(grouplabel) then call symputx('grouplabel',grouplabel); else call symputx('grouplabel',group); %if %index(&where,%str(cohort="nopreg")) %then %do; if not missing(grouplabel) then call symputx('grouplabel',catx(' ',grouplabel,'Non-Pregnancy')); else call symputx('grouplabel',catx(' ',group,'Non-Pregnancy')); %end; %else %if %index(&where,%str(cohort="preg")) %then %do; if not missing(grouplabel) then call symputx('grouplabel',catx(' ',grouplabel,'Pregnancy')); else call symputx('grouplabel',catx(' ',group,'Pregnancy')); %end; %else %if %index(&where,%str(cohort="mi")) %then %do; if not missing(grouplabel) then do; if not missing(grouplabel2) then call symputx('grouplabel',grouplabel2); else call symputx('grouplabel',group2); end; else do; call symputx('grouplabel',group2); end; %end; %else %if &reporttype = T6 %then %do; %if &s = 0 %then %do; if not missing(grouplabel) then do; call symputx('grouplabel',grouplabel); end; else do; call symputx('grouplabel',group); end; %end; %else %if &s = 1 %then %do; if not missing(grouplabel) then do; call symputx('grouplabel',grouplabel); end; else do; call symputx('grouplabel',group); end; %end; %else %if &s = 2 %then %do; if not missing(grouplabel) then do; call symputx('grouplabel',grouplabel); end; else do; call symputx('grouplabel',group); end; %end; %end; run; %tableletter(); %if &numprofilecovarstoinclude = 1 and &reporttype ^= T6 %then %let tableletter =; %else %if &numprofilecovarstoinclude = 1 and &profileswitches = 0 and &reporttype = T6 %then %let tableletter =; %addtotoc(tabnum=Table &tablenum.&tableletter., caption=%quote(Characteristic Profile of &grouplabel in the &database. from &startdateformatted. to &&enddate&periodid.formatted.)); %end; /* _temp_agg_order_profile */ %end; /* switch loop */ proc datasets library=work nowarn noprint; delete _temp_agg_profile _temp_agg_order_profile; quit; %end; /* where loop */ %end; /* periodid loop */ %let tablenum = %eval(&tablenum + 1); %end; /* &numprofilecovarstoinclude > 0 */ /*********************************************************************************************/ /* Effect estimate table */ /*********************************************************************************************/ %if &numl2comparisons > 0 and %sysfunc(exist(input.&treeaggfile.)) eq 0 %then %do; /*loop through each comparison*/ %do c = 1 %to &numl2comparisons; /* reset counter to reset table letter */ %let tablecount = 1; data _null_; set l2comparisonfile(where=(order=&c.)); call symputx('analysisgrp', analysisgrp); call symputx('runid', runid); run; /* Merge in group labels if they exist */ %let grouplabel = &analysisgrp; %isdata(dataset=labelfile); %if %eval(&nobs>0) %then %do; data _null_; set labelfile(in=a where=(group="&analysisgrp" and runid = "&runid")) ; if labeltype = 'grouplabel' then call symputx('grouplabel',label); run; %end; /* Store subgroup to determine subgroup labels */ proc sort nodupkey data=l2_effectestimates_&look_end.(where=(analysisgrp="&analysisgrp.")) out=_subgroups(keep= subgroup subgrouporder); by subgrouporder; run; proc sql noprint; select distinct subgroup into :subgrouplist separated by ' ' from _subgroups; quit; %if %str(&subgrouplist.) = %str() %then %do; %let tablecount = 0; %let numsubgroups=0; %end; %else %let numsubgroups=%sysfunc(countw(&subgrouplist)); /* loop subgroup and assign subgroup label */ %do subgroupcount = 0 %to &numsubgroups.; %if &subgroupcount. = 0 %then %let subgroup=; %else %let subgroup= %scan(&subgrouplist,&subgroupcount); %let subgrouplabel=; proc sql noprint; select distinct strip(tabletitle) into: subgrouplabel trimmed from Pscs_masterinputs where subgroup = "&subgroup."; quit; %if %str(&subgroup.) = %str() %then %do; %let titleend = %str(); %end; %else %if %substr(%upcase(&subgroup.),1,2) eq DP %then %do; %let titleend = %str(and Data Partner); %end; %else %do; %let titleend = %str(and &subgrouplabel); %end; %tableletter(); %addtotoc(tabnum=Table &tablenum.&tableletter., caption=%quote(Effect Estimates for &grouplabel. in the &database. from &startdateformatted. to &&enddate&look_end.formatted., by Analysis Type &titleend.)); %end; /* covarcount */ %let tablenum = %eval(&tablenum + 1); %end; /* numl2comparison do loop */ %end; /* numl2comparison */ /*********************************************************************************************/ /* Type 1 and 2 summary tables */ /*********************************************************************************************/ %if %sysfunc(prxmatch(m/t1cida|t2cida|t2conc/i,&tdatasetlist.)) %then %do; %do td = 1 %to &tdatasetlistnum.; %let reporttable = %scan(&tdatasetlist, &td.); %if ^%sysfunc(prxmatch(m/t1cida|t2cida|t2conc/i,&reporttable.)) %then %goto leavet1t2conc; proc sql noprint; %let tableobs = 0; select max(stratificationorder) into :tableobs trimmed from tablefile where dataset="&reporttable"; quit; %do z = 1 %to %eval(&tableobs.); data _null_; set tablefile(where=(dataset="&reporttable" and stratificationorder=&z)); call symputx('tabletitle', tabletitle); run; %if &stratifybydp = Y %then %let tablecount=1; %else %let tablecount=0; %tableletter(); %addtotoc(tabnum=Table &tablenum.&tableletter., caption=%bquote(Summary of &reporttitle. in the &database. from &startdateformatted. to &enddateformatted.&tabletitle.)); %if &stratifybydp. = Y %then %do; %do dps = 1 %to %eval(&num_dp.); %let maskedID = %scan(&masked_dplist,&dps); %tableletter(); %addtotoc(tabnum=Table &tablenum.&tableletter., caption=%bquote(Summary of &reporttitle. in the &database. for &maskedID. from &startdateformatted. to &enddateformatted.&tabletitle.)); %end; %end; %let tablenum = %eval(&tablenum + 1); %end; /* z */ %leavet1t2conc: %end; /* td */ %end; /* %sysfunc(prxmatch(m/t1cida|t2cida|t2conc/i,&tdatasetlist.)) */ /*****************************************************************************************/ /* Type 1 and 2 censor tables */ /*****************************************************************************************/ %if %sysfunc(prxmatch(m/t1censor|t2censor|t2followuptime/i,&tdatasetlist.)) > 0 %then %do; %macro t1t2censortoc(tablename=, title=); %let tableidlist=; proc sql noprint; select distinct table into: tableidlist separated by ' ' from tablefile(where=(dataset in ("&tablename."))); quit; %if %str("&tableidlist") ne %str("") %then %do; %isdata(dataset=&tablename.); %if %eval(&nobs.>0) %then %do; %do t = 1 %to %sysfunc(countw(&tableidlist.)); %let tableid = %scan(&tableidlist., &t.); %let stratificationorder = 0; proc sql noprint; select max(stratificationorder) into: stratificationorder from tablefile(where=(dataset in ("&tablename.") and table = "&tableid")); quit; /*counter for determining table letter*/ %if %eval(&stratificationorder. = 1) & &stratifybydp. ne Y %then %let tablecount = 0; %else %let tablecount = 1; %do st = 1 %to &stratificationorder.; data _null_; set tablefile(where=(dataset in ("&tablename.") and table = "&tableid" and stratificationorder = &st.)); call symputx('tabletitle', tabletitle); run; %tableletter(); %if &tableid. = T1 %then %do; %addtotoc(tabnum=Table &tablenum.&tableletter., caption=%quote(Summary of Time to End of &title. for &reporttitle. in the &database. from &startdateformatted. to &enddateformatted.&tabletitle.)); %if &stratifybydp. = Y & %eval(&st.=1) %then %do; %tableletter(); %addtotoc(tabnum=Table &tablenum.&tableletter., caption=%quote(Summary of Time to End of &title. for &reporttitle. in the &database. from &startdateformatted. to &enddateformatted., by Data Partner)); %end; %end; %else %if &tableid. = T2 %then %do; %addtotoc(tabnum=Table &tablenum.&tableletter., caption=%quote(Summary of Reasons for End of &title. for &reporttitle. in the &database. from &startdateformatted. to &enddateformatted.&tabletitle.)); %if &stratifybydp. = Y & %eval(&st.=1) %then %do; %tableletter(); %addtotoc(tabnum=Table &tablenum.&tableletter., caption=%quote(Summary of Reasons for End of &title. for &reporttitle. in the &database. from &startdateformatted. to &enddateformatted., by Data Partner)); %end; %end; %else %if &tableid. = T3 & %eval(&st.=1) %then %do; /*loop through each reason for censoring and within that - loop through stratificationorder*/ %do c = 1 %to %sysfunc(countw(&defaultcensororder., ' ')); %let reason = %scan(&defaultcensororder., &c.); /*check if rows exist in table (censorreason parameter has already been applied in %censortables_createdata*/ data chktable; set &tablename.(where=(table_name="&reason.")); run; %isdata(dataset=chktable); %if %eval(&nobs.>0) %then %do; /*set table letter counter - need to check # of stratifications requested for censor reason*/ proc sql noprint; select count(distinct stratificationorder) into: reasonstratificationorder from tablefile where dataset in ("&tablename.") and table = "&tableid" and findw(censorreason, "&reason.")>0; quit; %if %eval(&reasonstratificationorder. = 1) %then %let tablecount = 0; %else %let tablecount = 1; /*loop through each stratification*/ %do t3st = 1 %to &stratificationorder.; %let censorreasontable = N; data _null_; set tablefile(where=(dataset in ("&tablename.") and table = "T3" and stratificationorder = &t3st.)); call symputx('tabletitle', tabletitle); /*check if censoring reason requested*/ if findw(censorreason, "&reason.")>0 then call symputx('censorreasontable', 'Y'); run; %if &censorreasontable. = Y %then %do; %tableletter(); %addtotoc(tabnum=Table &tablenum.&tableletter., caption=%quote(Summary of Time to End of &title. due to %bquote(&&&reason._label) for &reporttitle. in the &database. from &startdateformatted. to &enddateformatted.&tabletitle.)); %end; /*censor reason requested*/ %end; /*loop through stratification*/ proc datasets nowarn noprint lib=work; delete chktable; quit; /*after each censor reason upnumber table*/ %let tablenum = %eval(&tablenum + 1); %end; /*dataset exists*/ %end; /*censor reason loop*/ %end; /*T3*/ %end; /*loop through stratifications*/ /*if table = T1 or T2 - upnumber table*/ %if %sysfunc(prxmatch(m/T1|T2/i,&tableid.)) > 0 %then %do; %let tablenum = %eval(&tablenum + 1); %end; %end; /*underlying data exists*/ %end; /*loop through each table*/ %end; /*table requested*/ %mend; %t1t2censortoc(tablename=t2followuptime, title=At-Risk Period); %t1t2censortoc(tablename=t&typenum.censor, title=Observable Data); %end; /*t1censor and t2censor tables*/ /*********************************************************************************************/ /* Type 4 summary tables */ /*********************************************************************************************/ %if %str("&reporttype") = %str("T4L1") & %str("&tablelist") ne %str("") %then %do; %do tb = 1 %to %sysfunc(countw(&tablelist.)); %let table = %scan(&tablelist., &tb); /*determine if table includes non-pregnant section*/; %let nonpreg = %str( ); %let s=; data _null_; set tablefile(where=(table="&table")); if tablesubstrat in ("t4nopreg", "t4nopreggestwk") then do; call symput('nonpreg', " and Matched Non-Pregnant Episodes "); call symputx('s', "s"); end; run; %if &stratifybydp = Y %then %let tablecount=1; %else %let tablecount=0; /*Overall*/ %tableletter(); %addtotoc(tabnum=Table &tablenum.&tableletter., %if &table. = T1 %then %do; caption=%quote(Pregnant Episodes&nonpreg.with &reporttitle. in the &database. from &startdateformatted. to &enddateformatted.)); %end; %if &table. = T2 %then %do; caption=%quote(&reporttitle. Episodes Among Pregnant&nonpreg.Cohort&s. in the &database. from &startdateformatted. to &enddateformatted.)); %end; %if &table. = T3 %then %do; caption=%quote(&reporttitle. Codes Among Pregnant&nonpreg.Cohort&s. in the &database. from &startdateformatted. to &enddateformatted., without Adjusting for Same-Day Dispensings)); %end; %if &table. = T4 %then %do; caption=%quote(&reporttitle. Codes Among Pregnant&nonpreg.Cohort&s. in the &database. from &startdateformatted. to &enddateformatted., Adjusting for Same-Day Dispensings)); %end; %if &table. = T5 %then %do; caption=%quote(Pregnant Episodes&nonpreg.with &reporttitle. in the &database. from &startdateformatted. to &enddateformatted., by Gestational Week)); %end; %if &table. = T6 %then %do; caption=%quote(&reporttitle. Episodes Among Pregnant&nonpreg.Cohort&s. in the &database. from &startdateformatted. to &enddateformatted., by Gestational Week)); %end; /*By DP*/ %if &stratifybydp. = Y %then %do; %do dps = 1 %to %eval(&num_dp.); %let maskedID = %scan(&masked_dplist,&dps); %tableletter(); %addtotoc(tabnum=Table &tablenum.&tableletter., %if &table. = T1 %then %do; caption=%quote(Pregnant Episodes&nonpreg.with &reporttitle. in the &database. for &maskedid. from &startdateformatted. to &enddateformatted.)); %end; %if &table. = T2 %then %do; caption=%quote(&reporttitle. Episodes Among Pregnant&nonpreg.Cohort&s. in the &database. for &maskedid. from &startdateformatted. to &enddateformatted.)); %end; %if &table. = T3 %then %do; caption=%quote(&reporttitle. Codes Among Pregnant&nonpreg.Cohort&s. in the &database. for &maskedid. from &startdateformatted. to &enddateformatted., without Adjusting for Same-Day Dispensings)); %end; %if &table. = T4 %then %do; caption=%quote(&reporttitle. Codes Among Pregnant&nonpreg.Cohort&s. in the &database. for &maskedid. from &startdateformatted. to &enddateformatted., Adjusting for Same-Day Dispensings)); %end; %if &table. = T5 %then %do; caption=%quote(Pregnant Episodes&nonpreg.with &reporttitle. in the &database. for &maskedid. from &startdateformatted. to &enddateformatted., by Gestational Week)); %end; %if &table. = T6 %then %do; caption=%quote(&reporttitle. Episodes Among Pregnant&nonpreg.Cohort&s. in the &database. for &maskedid. from &startdateformatted. to &enddateformatted., by Gestational Week)); %end; %end; %end; %let tablenum = %eval(&tablenum + 1); %end; /*loop through each table*/ %end; /*type 4 summary tables*/ /*********************************************************************************************/ /* Type 5 summary tables */ /*********************************************************************************************/ %if %str("&reporttype") = %str("T5") %then %do; /*****************************************************************************************/ /* Type 5 Tables T1-T13, T18-T22 */ /*****************************************************************************************/ /*Example order of tables: 2a: Categorical - overall 2b: Continuous - all 2c: Categorical - by age group 2d: Continuous - by age group */ %macro t5toc(cattableid=, disttableid=, cattitle=, disttitle=); /*if both categorical and continuous tables specified, then need to ascertain master order between both tables*/ %if %str("&cattableid.") ne %str("") & %str("&disttableid.") ne %str("") %then %do; /*Because a user can select different stratifications for each table need to ascertain a master order from tablefile regardless of whether includeinreport = Y*/ /*if user excludes includeinreport = N from file, order may not match SOC standard order of operations */ proc sort data=input.&tablefile.(keep=table tablesub where=(table in ("&cattableid","&disttableid"))) out=_temptablefile; by table; run; data _temptablefile; set _temptablefile; by table; length order 3; if first.table then order = 1; else order = order+1; retain order; run; proc sort data=_temptablefile nodupkey; by tablesub; run; proc sort data=tablefile(keep=table tablesub stratificationorder tabletitle) out=_temptablefile1; by tablesub; run; *create dataset that maps categorical and continuous tables; data _tempmap; merge _temptablefile _temptablefile1(rename=table=cattable rename=stratificationorder=catstratificationorder where=(cattable="&cattableid")) _temptablefile1(rename=table=disttable rename=stratificationorder=diststratificationorder where=(disttable="&disttableid")); by tablesub; if missing(catstratificationorder) & missing(diststratificationorder) then delete; run; proc sort data=_tempmap; by order ; run; %end; %else %do; data _tempmap; set tablefile(keep=table tablesub stratificationorder tabletitle where=(table in (%if %str("&cattableid.") ne %str("") %then %do; "&cattableid" %end; %if %str("&disttableid.") ne %str("") %then %do; "&disttableid" %end;))); run; %end; /*Loop through each tablesub, determine whether to output categorical and/or continuous table*/ %isdata(dataset=_tempmap); %let t5tableobs = &nobs.; %let tablecount=1; %do i = 1 %to %eval(&t5tableobs.); %let cattablestratorder = 0; %let disttablestratorder = 0; data _null_; set _tempmap; if _n_ = &i. then do; if tablesub = 'overall' and "&stratifybydp." = "Y" then call symputx('tabletitle', ', by Data Partner'); else call symputx('tabletitle', tabletitle); /*Both requested - determine whether to print both tables*/ %if %str("&cattableid.") ne %str("") & %str("&disttableid.") ne %str("") %then %do; if missing(cattable) | missing(disttable) then call symputx('numtables', 1); else call symputx('numtables', 2); if missing(cattable)=0 then call symputx('cattablestratorder', catstratificationorder); if missing(disttable)=0 then call symputx('disttablestratorder', diststratificationorder); %end; %else %do; call symputx('numtables', 1); /*determine which table*/ if table = "&cattableid" then call symputx('cattablestratorder', stratificationorder); if table = "&disttableid" then call symputx('disttablestratorder', stratificationorder); %end; end; run; /*reset table letter counter if only 1 table*/ %if %eval(&t5tableobs.=1) & %eval(&numtables.=1) %then %let tablecount=0; %if %eval(&cattablestratorder.>0) %then %do; %tableletter(); %addtotoc(tabnum=Table &tablenum.&tableletter., caption=%bquote(&cattitle. for &reporttitle. in the &database. from &startdateformatted. to &enddateformatted.&tabletitle.)); %end; %if %eval(&disttablestratorder.>0) %then %do; %tableletter(); %addtotoc(tabnum=Table &tablenum.&tableletter., caption=%bquote(&disttitle. for &reporttitle. in the &database. from &startdateformatted. to &enddateformatted.&tabletitle.)); %end; %end; /*Save the table of contents datasets for use during the Type 5 report output process*/ %isdata(dataset=t5_tempmap); %if %eval(&nobs.>0) %then %do; data t5_tempmap; set t5_tempmap _tempmap (in=a); %if %varexist(_tempmap,stratificationorder) = 1 %then %do; if a and stratificationorder ne . then do; %if %str("&cattableid.") ne %str("") %then %do; cattable="&cattableid"; catstratificationorder = stratificationorder; %end; %else %if %str("&disttableid.") ne %str("") %then %do; disttable="&disttableid"; diststratificationorder = stratificationorder; %end; end; %end; run; %end; %else %do; /*dummy t5_tempmap file*/ data t5_tempmap; set _tempmap; %if %varexist(_tempmap,stratificationorder) = 1 %then %do; if stratificationorder ne . then do; %if %str("&cattableid.") ne %str("") %then %do; length cattable $3; cattable="&cattableid"; catstratificationorder = stratificationorder; %end; %else %if %str("&disttableid.") ne %str("") %then %do; length disttable $3; disttable="&disttableid"; diststratificationorder = stratificationorder; %end; end; %end; run; %end; proc datasets nowarn noprint lib=work; delete _temp:; quit; %let tablenum = %eval(&tablenum+1); %mend; /*T1/T2 - Days Supplied per Dispensing*/ %if %sysfunc(prxmatch(m/T1\b|T2\b/i,&tablelist.)) > 0 %then %do; %t5toc(cattableid=%if %sysfunc(prxmatch(m/T1\b/i,&tablelist.)) > 0 %then %do; T1 %end;, disttableid=%if %sysfunc(prxmatch(m/T2\b/i,&tablelist.)) > 0 %then %do; T2 %end;, cattitle=Categorical Summary of Days Supplied per Dispensing, disttitle=Continuous Summary of Days Supplied per Dispensing); %end; /*T3/T4 - Cumulative episode duration*/ %if %sysfunc(prxmatch(m/T3\b|T4\b/i,&tablelist.)) > 0 %then %do; %t5toc(cattableid=%if %sysfunc(prxmatch(m/T3\b/i,&tablelist.)) > 0 %then %do; T3 %end;, disttableid=%if %sysfunc(prxmatch(m/T4\b/i,&tablelist.)) > 0 %then %do; T4 %end;, cattitle=Categorical Summary of Patients%str(%') Cumulative Treatment Episode Durations, disttitle=Continuous Summary of Patients%str(%') Cumulative Treatment Episode Durations); %end; /*T5/T6 - Episode duration - all episodes*/ %if %sysfunc(prxmatch(m/T5\b|T6\b/i,&tablelist.)) > 0 %then %do; %t5toc(cattableid=%if %sysfunc(prxmatch(m/T5\b/i,&tablelist.)) > 0 %then %do; T5 %end;, disttableid=%if %sysfunc(prxmatch(m/T6\b/i,&tablelist.)) > 0 %then %do; T6 %end;, cattitle=Categorical Summary of All Treatment Episodes, disttitle=Continuous Summary of All Treatment Episodes); %end; /*T7/T8 - Episode duration - first episode*/ %if %sysfunc(prxmatch(m/T7\b|T8\b/i,&tablelist.)) > 0 %then %do; %t5toc(cattableid=%if %sysfunc(prxmatch(m/T7\b/i,&tablelist.)) > 0 %then %do; T7 %end;, disttableid=%if %sysfunc(prxmatch(m/T8\b/i,&tablelist.)) > 0 %then %do; T8 %end;, cattitle=Categorical Summary of First Treatment Episodes, disttitle=Continuous Summary of First Treatment Episodes); %end; /*T9/T10 - Episode duration - second and subsequent episodes*/ %if %sysfunc(prxmatch(m/T9\b|T10\b/i,&tablelist.)) > 0 %then %do; %t5toc(cattableid=%if %sysfunc(prxmatch(m/T9\b/i,&tablelist.)) > 0 %then %do; T9 %end;, disttableid=%if %sysfunc(prxmatch(m/T10\b/i,&tablelist.)) > 0 %then %do; T10 %end;, cattitle=Categorical Summary of Second and Subsequent Treatment Episodes, disttitle=Continuous Summary of Second and Subsequent Treatment Episodes); %end; /*T11 - All episode gaps*/ %if %sysfunc(prxmatch(m/T11\b/i,&tablelist.)) > 0 %then %do; %t5toc(cattableid=, disttableid=T11, cattitle=, disttitle=Continuous Summary of All Treatment Episode Gaps); %end; /*T12 - First episode gap*/ %if %sysfunc(prxmatch(m/T12\b/i,&tablelist.)) > 0 %then %do; %t5toc(cattableid=, disttableid=T12, cattitle=, disttitle=Continuous Summary of First Treatment Episode Gaps); %end; /*T13 - Second and subequent episode gaps*/ %if %sysfunc(prxmatch(m/T13\b/i,&tablelist.)) > 0 %then %do; %t5toc(cattableid=, disttableid=T13, cattitle=, disttitle=Continuous Summary of Second and Subsequent Treatment Episode Gaps); %end; /*T18 - Filled daily dose in each dispensing*/ %if %sysfunc(prxmatch(m/T18\b/i,&tablelist.)) > 0 %then %do; %t5toc(cattableid=T18, disttableid=, cattitle=Summary of Filled Daily Dose in Each Dispensing, disttitle=); %end; /*T19 - Average filled daily dose in each episode*/ %if %sysfunc(prxmatch(m/T19\b/i,&tablelist.)) > 0 %then %do; %t5toc(cattableid=T19, disttableid=, cattitle=Summary of Average Filled Daily Dose in Each Treatment Episode, disttitle=); %end; /*T20 - Average filled daily dose in first episode*/ %if %sysfunc(prxmatch(m/T20\b/i,&tablelist.)) > 0 %then %do; %t5toc(cattableid=T20, disttableid=, cattitle=Summary of Average Filled Daily Dose in Each Patient%str(%')s First Valid Episode, disttitle=); %end; /*T21 - Cumulative filled dose in each episode*/ %if %sysfunc(prxmatch(m/T21\b/i,&tablelist.)) > 0 %then %do; %t5toc(cattableid=T21, disttableid=, cattitle=Summary of Cumulative Filled Dose in All Treatment Episodes, disttitle=); %end; /*T22 - Cumulative filled dose in first episode*/ %if %sysfunc(prxmatch(m/T22\b/i,&tablelist.)) > 0 %then %do; %t5toc(cattableid=T22, disttableid=, cattitle=Summary of Cumulative Filled Dose in Each Patient%str(%')s First Treatment Episode, disttitle=); %end; /*Additional variables for ordering Type 5 table report output*/ %isdata(dataset=t5_tempmap); %if %eval(&nobs.>0) %then %do; data t5_tempmap; set t5_tempmap; length t5order 3; t5order=_n_; run; proc sort data = t5_tempmap; by table t5order; run; data t5_tempmap; set t5_tempmap; by table; length numtables tableorder 3; if first.table and last.table %if %varexist(t5_tempmap,cattable) = 1 & %varexist(t5_tempmap,disttable) = 1 %then %do; and (cattable = '' or disttable = '') %end; then numtables=1; else numtables=2; retain tableorder; if first.table then tableorder=0; tableorder+1; run; proc sort data = t5_tempmap; by t5order table; run; %end; /*****************************************************************************************/ /* Type 5 censor tables (T14-T17) */ /*****************************************************************************************/ %macro t5censortoc(tableid=, first=, dataset=); %if %sysfunc(prxmatch(m/T14\b|T16\b/i,&tableid.)) > 0 %then %do; /*counter for determining table letter*/ %if &stratifybydp. = Y %then %let tablecount = 1; %else %let tablecount = 0; %tableletter(); %addtotoc(tabnum=Table &tablenum.&tableletter., caption=%quote(Summary of Reasons &first.Treatment Episodes Ended for &reporttitle. in the &database. from &startdateformatted. to &enddateformatted.)); %if &stratifybydp. = Y %then %do; %tableletter(); %addtotoc(tabnum=Table &tablenum.&tableletter., caption=%quote(Summary of Reasons &first.Treatment Episodes Ended for &reporttitle. in the &database. from &startdateformatted. to &enddateformatted., by Data Partner)); %end; %let tablenum = %eval(&tablenum + 1); %end; %if %sysfunc(prxmatch(m/T15\b|T17\b/i,&tableid.)) > 0 %then %do; /*loop through each reason for censoring*/ %do c = 1 %to %sysfunc(countw(&defaultcensororder., ' ')); %let reason = %scan(&defaultcensororder., &c.); %let censorreasontable = N; data _null_; set tablefile(where=(dataset in ("t5censor") and table = "&tableid.")); /*check if censoring reason requested*/ if findw(censorreason, "&reason.")>0 then call symputx('censorreasontable', 'Y'); run; %if &censorreasontable. = Y %then %do; /*check if rows exist in table (censorreason parameter has already been applied in %censortables_createdata*/ data chktable; set &dataset.(where=(table_name="&reason.")); run; %isdata(dataset=chktable); %if %eval(&nobs.>0) %then %do; /*note - table is not stratified by DP*/ %addtotoc(tabnum=Table &tablenum., caption=%quote(Summary of Episode Duration for &first.Treatment Episodes Ended due to %bquote(&&&reason._label) for &reporttitle. in the &database. from &startdateformatted. to &enddateformatted.)); %let tablenum = %eval(&tablenum + 1); %end; proc datasets nowarn noprint lib=work; delete chktable; quit; %end; %end; %end; %mend; %if %sysfunc(prxmatch(m/T14\b/i,&tablelist.)) > 0 %then %do; %t5censortoc(tableid=T14, first=%str(First ), dataset=); %end; %if %sysfunc(prxmatch(m/T15\b/i,&tablelist.)) > 0 %then %do; %t5censortoc(tableid=T15, first=%str(First ), dataset=t5censor_first); %end; %if %sysfunc(prxmatch(m/T16\b/i,&tablelist.)) > 0 %then %do; %t5censortoc(tableid=T16, first=, dataset=); %end; %if %sysfunc(prxmatch(m/T17\b/i,&tablelist.)) > 0 %then %do; %t5censortoc(tableid=T17, first=, dataset=t5censor); %end; %end; /*type 5 tables*/ /*********************************************************************************************/ /* Type 6 tables */ /*********************************************************************************************/ %if %sysfunc(prxmatch(m/t6censor|t6plota|t6plotb/i,&tdatasetlist.)) > 0 %then %do; %macro t6toc(tableid=, title=); %isdata(dataset=table&tableid.); %if %eval(&nobs.>0) %then %do; %let stratificationorder = 0; proc sql noprint; select max(stratificationorder) into: stratificationorder from tablefile(where=(table = "&tableid")); quit; /*counter for determining table letter*/ %if %eval(&stratificationorder. = 1) & &stratifybydp. ne Y %then %let tablecount = 0; %else %let tablecount = 1; %do st = 1 %to &stratificationorder.; data _null_; set tablefile(where=(table = "&tableid" and stratificationorder = &st.)); call symputx('tabletitle', tabletitle); run; %tableletter(); %addtotoc(tabnum=Table &tablenum.&tableletter., %if &tableid. eq T8 or &tableid. eq T9 or &tableid. eq T10 %then %do; caption=%quote(Summary of &title. for &reporttitle. in the &database. from &startdateformatted. to &enddateformatted.&tabletitle.)); %end; %if &stratifybydp. = Y & %eval(&st.=1) %then %do; %tableletter(); %addtotoc(tabnum=Table &tablenum.&tableletter., %if &tableid. eq T8 or &tableid. eq T9 or &tableid. eq T10 %then %do; caption=%quote(Summary of &title. for &reporttitle. in the &database. from &startdateformatted. to &enddateformatted., by Data Partner)); %end; %end; %end; /*loop through stratifications*/ %let tablenum = %eval(&tablenum + 1); %end; /*underlying data exists*/ %mend t6toc; %t6toc(tableid=T8, title=Episode Duration by Reason Episodes Ended); %t6toc(tableid=T9, title=Time to First Switch or Episode End); %t6toc(tableid=T10, title=Time to Second Switch or Episode End); %end; /*type 6 tables*/ /*********************************************************************************************/ /* Code Distribution Tables */ /*********************************************************************************************/ %if &output_code_distribution. eq Y %then %do; /* This macros output a specific distribution type (EXP or HOI) entries in the table of content */ %macro codedistribution_type_toc(distindextype=); /* Compute group label to display in title */ %let grouplabel=; %isdata(dataset=labelfile); %if %eval(&nobs>0) %then %do; proc sql noprint; select label into :grouplabel trimmed from labelfile where lower(group)="&group." and runid = "&runid" and %if &distindextype. eq exp %then %do; lower(labeltype)="grouplabel"; %end; %else %do; lower(labeltype)="outcomelabel"; %end; quit; %end; %if %str("&grouplabel.") eq %str("") %then %let grouplabel = %trim(&group.); /* Full Code Distribution Table */ %tableletter(); %addtotoc(tabnum=Table &tablenum.&tableletter., caption=%quote(Full Code Distribution of &grouplabel. in the &database. from &startdateformatted. to &enddateformatted.)); /* Total Code Counts Table */ %tableletter(); %addtotoc(tabnum=Table &tablenum.&tableletter., caption=%quote(Total Code Counts of &grouplabel. in the &database. from &startdateformatted. to &enddateformatted.)); %mend codedistribution_type_toc; /* reset counter to reset table letter */ %let tablecount=1; proc sql noprint; select count(*) into :numgroupscodedist trimmed from GroupsDist; quit; %do loopcount = 1 %to &numgroupscodedist.; %let codedistexp = N; %let codedisthoi = N; data _null_; set GroupsDist; if &loopcount. = _N_; call symputx('runid', runid); call symputx('group', group); if index(upcase(codedist), "EXP") > 0 then call symputx('codedistexp', 'Y'); if index(upcase(codedist), "HOI") > 0 then call symputx('codedisthoi', 'Y'); run; %if &codedistexp. eq Y %then %do; * Defensive: make sure the requested data is available; %let obscount=0; proc sql noprint; select count(*) into :obscount from codedistdata where lower(group) = "&group." and runid = "&runid" and lower(distindextype) = "exp"; quit; %if %eval(&obscount. > 0) %then %codedistribution_type_toc(distindextype=exp); %end; %if &codedisthoi. eq Y %then %do; * Defensive: make sure the requested data is available; %let obscount=0; proc sql noprint; select count(*) into :obscount from codedistdata where lower(group) = "&group." and runid = "&runid" and lower(distindextype) = "hoi"; quit; %if %eval(&obscount. > 0) %then %codedistribution_type_toc(distindextype=hoi); %end; %end; *numgroupscodedist; %let tablenum = %eval(&tablenum + 1); %end; /*********************************************************************************************/ /* Attrition table */ /*********************************************************************************************/ /* reset counter to reset table letter */ %let tablecount=1; %do j = %eval(&look_start.) %to %eval(&look_end.); %if %index(&reporttype,L2) %then %let attrperiodid = _&j; %isdata(dataset=agg_patient_attrition&attrperiodid); %let attrition_patient = &nobs; %isdata(dataset=agg_episode_attrition&attrperiodid); %let attrition_episode = &nobs; %if &attrition_patient > 0 or &attrition_episode > 0 %then %do; %if (&attrition_patient > 0 and &attrition_episode = 0) or (&attrition_patient = 0 and &attrition_episode > 0) %then %do; %let tablecount = 0; %end; %if &attrition_episode > 0 %then %do; %tableletter(); %addtotoc(tabnum=Table &tablenum.&tableletter., caption=%quote(Summary of Episode Level Cohort Attrition in the &database. from &startdateformatted. to &&enddate&j.formatted.)); %end; %if &attrition_patient > 0 %then %do; %tableletter(); %addtotoc(tabnum=Table &tablenum.&tableletter., caption=%quote(Summary of Patient Level Cohort Attrition in the &database. from &startdateformatted. to &&enddate&j.formatted.)); %end; %let tablenum = %eval(&tablenum + 1); %end;/* attrition_groups file */ %end;/*periodid */ /*** END TABLES **/ /*********************************************************************************************/ /* Figures */ /*********************************************************************************************/ %isdata(dataset=figurefile); %if %eval(&nobs.>0) %then %do; %let figurenum = 1; /* Add +1 for additional figure types that are requested */ %let tablecount = 1; /***************************************************************************************/ /* L1 Figures */ /***************************************************************************************/ %if %sysfunc(prxmatch(m/T1|T2L1|T5|T6/i,&reporttype.)) > 0 %then %do; /*utility macro to loop through groups*/ %macro figuretoc(figure=, title =, dataset_name =); %isdata(dataset=&dataset_name.); %if %eval(&nobs.>0) %then %do; /*number of distinct groups in figure to loop through determine whether to add letter to figure #*/ proc sql noprint; select distinct order into :fgrouporderlist separated by ' ' from &dataset_name. order by order; select max(stratificationorder) into :fstrataorder from figurefile where figure="&figure"; quit; %if %sysfunc(countw(&fgrouporderlist.)) = 1 and &fstrataorder = 1 %then %let tablecount = 0; %else %let tablecount = 1; %do g = 1 %to %sysfunc(countw(&fgrouporderlist.)); %let order = %scan(&fgrouporderlist., &g.); %let grouplabel = ; %do f = 1 %to &fstrataorder; %let figuretitle = ""; data _null_; set figurefile(where=(figure = "&figure" and stratificationorder = &f)); call symputx('figuretitle', figuretitle); run; data _null_; set &dataset_name.(where=(order = &order.)); if _n_ = 1 then do; call symputx('grouplabel', grouplabel); /* Assign competing risk label */ %if &reporttype = T6 and %sysfunc(prxmatch(m/F8|F9/i,&figure)) %then %do; call symputx('competingrisklabel',vlabel(competingrisk)); %end; end; run; /* Unmask title for T6 F8 and F9 figures to resolve competing risk macro variable */ %if &reporttype = T6 and %sysfunc(prxmatch(m/F8|F9/i,&figure)) %then %let title = %unquote(&title); %tableletter(); %addtotoc(tabnum=Figure &figurenum.&tableletter., caption=%quote(&title. &grouplabel. in the &database. from &startdateformatted. to &enddateformatted.&figuretitle.)); %end; %end; /*loop through each figure*/ %let figurenum = %eval(&figurenum.+1); %end; /*figure dataset exists*/ %mend; /********************************************************************************************** T1: 1 figure: 1) F1: t1censor = Reasons for End of Observable Data by Group (1-CDF) - 1 figure per group /**********************************************************************************************/ %if &reporttype. = T1 %then %do; %figuretoc(figure=F1, title =Reasons for End of Observable Data Among, dataset_name = figureF1); %end; /*T1*/ /********************************************************************************************** T2L1: 3 figures: 1) F1: t2followuptime = Kaplan-Meier Estimate of Event of Interest Not Occurring 2) F2: t2followuptime = Reasons for End of Follow-Up by Group (1-CDF) 3) F3: t2censor = Reasons for End of Observable Data by Group (1-CDF) /**********************************************************************************************/ %if &reporttype. = T2L1 %then %do; /*F1: 1 figure per report*/ %isdata(dataset=figuref1); %if %eval(&nobs.>0) %then %do; %addtotoc(tabnum=Figure &figurenum., caption=%quote(Kaplan-Meier Estimate of Event of Interest Not Occurring in the &database. from &startdateformatted. to &enddateformatted.)); %let figurenum = %eval(&figurenum.+1); %end; /*F2: 1 figure per group*/ %if %sysfunc(prxmatch(m/F2/i,&figurelist.)) > 0 %then %do; %figuretoc(figure=F2, title =Reasons for End of Follow-Up Among, dataset_name = figureF2); %end; /*figuref2*/ /*F3: 1 figure per group*/ %if %sysfunc(prxmatch(m/F3/i,&figurelist.)) > 0 %then %do; %figuretoc(figure=F3, title =Reasons for End of Observable Data Among, dataset_name = figureF3); %end; /*figuref3*/ %end; /*T2L1*/ /********************************************************************************************** T5: 5 figures: 1) F1: Patient Entry into Study by Month 2) F2: Number of Prescription Dispensings in Patients First Episodes by Month 3) F3: Total Days Supply in Patients' First Episodes by Month 4) F4: t5censor = Reasons for End of First Treatment Episode by Group 5) F5: t5censor = End of First Treatment Episode due to [Censoring Reason] by Group /**********************************************************************************************/ %if &reporttype. = T5 %then %do; /* F1 */ %if %sysfunc(prxmatch(m/\bF1\b/i,&figurelist.)) > 0 %then %do; %figuretoc(figure=F1, title =Patient Entry into Study by Month for, dataset_name = figure123); %end; /* F2 */ %if %sysfunc(prxmatch(m/F2/i,&figurelist.)) > 0 %then %do; %figuretoc(figure=F2, title =Number of Prescription Dispensings in Patients%str(%') First Episodes by Month Patient Entered into Study for, dataset_name = figure123); %end; /* F3 */ %if %sysfunc(prxmatch(m/F3/i,&figurelist.)) > 0 %then %do; %figuretoc(figure=F3, title =Total Days Supply in Patients%str(%') First Episodes by Month Patient Entered into Study for, dataset_name = figure123); %end; /*F4: 1 figure per group*/ %if %sysfunc(prxmatch(m/F4/i,&figurelist.)) > 0 %then %do; %figuretoc(figure=F4, title =Reasons for End of First Treatment Episode Among, dataset_name = figureF4); %end; /*figuref4*/ /*F5: 1 figure per report*/ /*Censor reason*/ %if %sysfunc(prxmatch(m/F5/i,&figurelist.)) > 0 %then %do; %isdata(dataset= figuref5); %if %eval(&nobs.>0) %then %do; data _null_; set figurefile(where=(figure="F5")); call symputx('censordisplay', censordisplay); run; %addtotoc(tabnum=Figure &figurenum, caption =%quote(End of First Treatment Episode due to &&&censordisplay._label in the &database. from &startdateformatted. to &enddateformatted.)); %let figurenum = %eval(&figurenum.+1); %end; %end; %end; /*T5*/ /********************************************************************************************** T6: 9 figures: 1) F1 (not yet implemented) 2) F2 (not yet implemented) 3) F3 (not yet implemented) 4) F4: t6plota = Kaplan-Meier Estimate of First Switch Not Occurring 5) F5: t6plotb = Kaplan-Meier Estimate of Second Switch Not Occurring 6) F6: t6plota = Reasons for Censoring at First Switch Evaluation by Analysisgrp 7) F7: t6plotb = Reasons for Censoring at Second Switch Evaluation by Analysisgrp 8) F8: t6plota = Cumulative Incidence of First Switch Against Competing Risk by Analysisgrp 9) F9: t6plotb = Cumulative Incidence of Second Switch Against Competing Risk by Analysisgrp /***********************************************************************************************/ %if &reporttype. = T6 %then %do; /*F4 - F7: 1 figure per group*/ %if %sysfunc(prxmatch(m/F4/i,&figurelist.)) > 0 %then %do; %figuretoc(figure=F4, title =Kaplan-Meier Estimate of First Switch Not Occurring Among, dataset_name = figureF4); %end; /*figuref4*/ %if %sysfunc(prxmatch(m/F5/i,&figurelist.)) > 0 %then %do; %figuretoc(figure=F5, title =Kaplan-Meier Estimate of Second Switch Not Occurring Among, dataset_name = figureF5); %end; /*figuref5*/ %if %sysfunc(prxmatch(m/F6/i,&figurelist.)) > 0 %then %do; %figuretoc(figure=F6, title =Reasons for Censoring at First Switch Evaluation Among, dataset_name = figureF6); %end; /*figuref6*/ %if %sysfunc(prxmatch(m/F7/i,&figurelist.)) > 0 %then %do; %figuretoc(figure=F7, title =Reasons for Censoring at Second Switch Evaluation Among, dataset_name = figureF7); %end; /*figuref7*/ %if %sysfunc(prxmatch(m/F8/i,&figurelist.)) > 0 %then %do; %figuretoc(figure=F8, title =%nrstr(Cumulative Incidence of First Switch Accounting for &competingrisklabel. as a Competing Risk Among), dataset_name = figureF8); %end; /*figuref8*/ %if %sysfunc(prxmatch(m/F9/i,&figurelist.)) > 0 %then %do; %figuretoc(figure=F9, title =%nrstr(Cumulative Incidence of Second Switch Accounting for &competingrisklabel. as a Competing Risk Among), dataset_name = figureF9); %end; /*figuref9*/ %end; /*T6*/ %end; /*L1 figures*/ /***************************************************************************************/ /* L2 Figures */ /***************************************************************************************/ %if %sysfunc(prxmatch(m/T2L2|T4L2/i,&reporttype.)) > 0 %then %do; /*F1: PS distribution histograms*/ %if %sysfunc(prxmatch(m/F1/i,&figurelist.)) > 0 %then %do; %do j = %eval(&look_start) %to %eval(&look_end); %do loopcount = 1 %to &numl2comparisons.; data _NULL_; set l2comparisonfile(where=(order=&loopcount.)); call symputx('runid', runid); call symputx('analysisgrp', analysisgrp); call symputx('OutputPSDistribution', OutputPSDistribution); run; %if &OutputPSDistribution. = Y %then %do; %isdata(dataset=labelfile); %let grouplabel=&analysisgrp.; %if %eval(&nobs>0) %then %do; data _NULL_; set labelfile(where=(lowcase(labeltype)='grouplabel')); if group="&analysisgrp." and runid = "&runid"; call symputx('grouplabel', Label); run; %put &grouplabel.; %end; %let andafter=; proc sql noprint; select strip(file) into: psfile from pscs_masterinputs where analysisgrp = "&analysisgrp." and missing(subgroup); quit; %if &psfile. = psmatchfile | &psfile. = stratificationfile | &psfile. = iptwfile %then %do; data _null_; set pscs_masterinputs (where=(lowcase(analysisgrp)="&analysisgrp." and missing(subgroup))); call symputx("psestimategrp", lowcase(psestimategrp)); %if &psfile. = psmatchfile %then %do; if upcase(ratio) = "F" then do; call symput("andafter", " and After"); end; %end; %if &psfile = iptwfile %then %do; call symput("andafter", " and After"); %end; %else %if &psfile = stratificationfile %then %do; if upcase(strataweight) in ("ATE", "ATT") then do; call symput("andafter", " and After"); end; %end; run; %let numsubgroups=0; %let subgroup=; %let subgroupcat=; %let subgrouptitle=; proc sort nodupkey data=Pscs_masterinputs(where=(analysisgrp="&analysisgrp." and runid="&runid." and not missing(subgroup))) out=_subgroups(keep=subgroup subgroupcat subgrouporder subgroupcatorder combinedlabel); by subgrouporder subgroupcatorder; run; proc sql noprint; select count(*) into :numsubgroups from _subgroups; quit; %do sub=0 %to &numsubgroups.; %let max_eoi=0; %let max_ref=0; %if &sub. > 0 %then %do; data _null_; set _subgroups; if _N_=&sub.; call symputx("SubGroup",lowcase(strip(subgroup))); call symputx("SubgroupCat",upcase(strip(subgroupcat))); call symputx("subgrouptitle",combinedlabel); run; proc sql noprint; select max(_eoi), max(_ref) into :max_eoi, :max_ref from histogram_&j. (where=(runid="&runid." and order="&loopcount." and subgroup="&subgroup." and subgroupcat="&subgroupcat.")); quit; %end; %else %do; proc sql noprint; select max(_eoi), max(_ref) into :max_eoi, :max_ref from histogram_&j. (where=(runid="&runid." and order="&loopcount." and subgroup="" and subgroupcat="")); quit; %end; %if &max_eoi. > 0 and &max_ref. > 0 %then %do; %if &numsubgroups.=0 %then %let tablecount = 0; %tableletter(); %addtotoc(tabnum=Figure &figurenum.&tableletter., caption=%quote(Histograms Depicting Propensity Score Distributions Before&andafter Adjustment for &grouplabel. in the &database. from &startdateformatted. to &&enddate&j.formatted.&subgrouptitle.)) %end; %else %do; %put WARNING: (Sentinel) Insufficient data to produce histogram for analysisgrp=&analysisgrp., subgroup=&SubGroup., subgroupcat=&SubgroupCat.. Histogram will not be produced.; %end; %end; /* loop subgroups */ %let figurenum = %eval(&figurenum.+1); %let tablecount = 1; %let tableletter =a; %end; /*psfile*/ %end; /* OutputPSDistribution */ %end; /* loop comparisons */ %end; /* loop periods */ %end; /*Histograms*/ /*F2: Forest Plots*/ %if %sysfunc(prxmatch(m/F2/i,&figurelist.)) > 0 and %sysfunc(exist(input.&treeaggfile.)) eq 0 %then %do; %if %sysfunc(prxmatch(m/T2L2/i,&reporttype.)) > 0 %then %let ForestRatioTitle = Hazard Ratios (HR); %else %let ForestRatioTitle = Risk Ratios (RR); %let tableletter=a; %let tablecount = 1; %do j = %eval(&look_start) %to %eval(&look_end); %do plot = 1 %to 7; %let forest_title = ; data _null_; set forest_&j(where=(plotorder=&plot)); call symputx("forest_title",forest_title); run; %if %length(&forest_title) > 0 %then %do; %tableletter(); %addtotoc(tabnum=Figure &figurenum.&tableletter., caption=%quote(Forest Plot of &ForestRatioTitle and 95% Confidence Intervals (CI) for &forest_title in the &database. from &startdateformatted. to &&enddate&j.formatted.)) %end; /* Forest title exists */ %end; /* loop plots */ %end; /* loop periods */ %let figurenum = %eval(&figurenum.+1); %end; /*Forest plots */ /*F3-F5: KM Plots - Type 2 only*/ %if &reporttype. = T2L2 & %sysfunc(prxmatch(m/F3|F4|F5/i,&figurelist.)) > 0 and %sysfunc(exist(input.&treeaggfile.)) eq 0 %then %do; *reset tablecount; %let tablecount = 1; %let F3nobs = 0; %let F4nobs = 0; %let F5nobs = 0; %macro survivalcurvestoc(aggregated=, dpinparenthesis=, dpwhere=); /*assign labels*/ data _null_; set pscs_masterinputs(where=(analysisgrp="&analysisgrp." and missing(subgroup))); call symputx("psestimategrp", lowcase(psestimategrp)); run; data _null_; set infolder.&&&runid._psestimationfile(where=(lowcase(psestimategrp)="&psestimategrp.")); call symputx('GRP1', eoi); call symputx('GRP0', ref); run; %let outcomelabel = Event of Interest; %let eoilabel = &grp1.; %let reflabel = &grp0.; %let AnalysisGroupLabel = &analysisgrp.; %let PSEstimateGroupLabel = &psestimategrp.; %isdata(dataset=labelfile); %if %eval(&nobs.>0) %then %do; data _null_; set labelfile(in=a where=(group="&analysisgrp" and runid = "&runid" and labeltype = "outcomelabel")) labelfile(in=b where=(group="&grp1." and runid = "&runid." and labeltype = "grouplabel")) labelfile(in=c where=(group="&grp0." and runid = "&runid." and labeltype = "grouplabel")) labelfile(in=d where=(group="&analysisgrp" and runid = "&runid" and labeltype = "grouplabel")) labelfile(in=e where=(group="&psestimategrp" and runid = "&runid" and labeltype = "grouplabel")) ; if a then call symputx('outcomelabel', label); if b then call symputx('eoilabel', label); if c then call symputx('reflabel', label); if d then call symputx('AnalysisGroupLabel', label); if e then call symputx('PSEstimateGroupLabel', label); run; %end; %let numsubgroups=0; %let subgroup=; %let subgroupcat=; %let subgrouptitle=; proc sort nodupkey data=Pscs_masterinputs(where=(analysisgrp="&analysisgrp." and runid="&runid." and not missing(subgroup))) out=_subgroups(keep=subgroup subgroupcat subgrouporder subgroupcatorder combinedlabel); by subgrouporder subgroupcatorder; run; proc sql noprint; select count(*) into :numsubgroups from _subgroups; quit; %do sub=0 %to &numsubgroups.; %if &sub. > 0 %then %do; data _null_; set _subgroups; if _N_=&sub.; call symputx("SubGroup",lowcase(strip(subgroup))); call symputx("SubgroupCat",upcase(strip(subgroupcat))); call symputx("subgrouptitle",combinedlabel); run; %end; /*F3*/ %isdata(dataset=figureF3_analysis&loopcount._&j.); %let F3nobs = &nobs.; %if %eval(&nobs.>0) %then %do; %let max_day=0; proc sql noprint; select max(day) into :max_day from figureF3_analysis&loopcount._&j. where subgroup="&subgroup." and subgroupcat="&subgroupcat." and dpidsiteid="&dpwhere"; quit; %if &max_day. > 0 %then %do; %tableletter(); %addtotoc(tabnum=Figure &figurenum.&tableletter., caption=%quote(&aggregated.Unadjusted Kaplan-Meier Estimate and 95% Confidence Interval of &outcomelabel. Not Occurring Among &AnalysisGroupLabel.&dpinparenthesis. from the Whole Population in the &database. from &startdateformatted. to &&enddate&j.formatted.&subgrouptitle.)); %end; %else %do; %put WARNING: (Sentinel) Insufficient data to produce unadjusted Kaplan-Meier estimate for analysisgrp=&analysisgrp., subgroup=&SubGroup., subgroupcat=&SubgroupCat.. KM curves will not be produced.; %end; %end; /*F4*/ %isdata(dataset=figureF4_analysis&loopcount._&j.); %let F4nobs = &nobs.; %if %eval(&nobs.>0) %then %do; %let max_day=0; proc sql noprint; select max(day) into :max_day from figureF4_analysis&loopcount._&j. where subgroup="&subgroup." and subgroupcat="&subgroupcat." and dpidsiteid="&dpwhere"; quit; %if &pscsfile. = psmatchfile %then %let pop=Conditional Matched Population after; %else %if &pscsfile. = stratificationfile | &pscsfile. = iptwfile %then %let pop=Weighted Population after; %if &max_day. > 0 %then %do; %if &kmrefpop. = unweighted %then %let cititle=%str( and 95% Confidence Interval); %else %let cititle=; %tableletter(); %addtotoc(tabnum=Figure &figurenum.&tableletter., caption=%quote(&aggregated.Adjusted Kaplan-Meier Estimate&cititle. of &outcomelabel. Not Occurring Among &AnalysisGroupLabel.&dpinparenthesis. from the &pop. &PSEstimateGroupLabel. in the &database. from &startdateformatted. to &&enddate&j.formatted.&subgrouptitle.)); %end; %else %do; %put WARNING: (Sentinel) Insufficient data to produce conditional Kaplan-Meier estimate for analysisgrp=&analysisgrp., subgroup=&SubGroup., subgroupcat=&SubgroupCat.. KM curves will not be produced.; %end; %end; /*F5*/ %isdata(dataset=figureF5_analysis&loopcount._&j.); %let F5nobs = &nobs.; %if %eval(&nobs.>0) %then %do; %let max_day=0; proc sql noprint; select max(day) into :max_day from figureF5_analysis&loopcount._&j. where subgroup="&subgroup." and subgroupcat="&subgroupcat." and dpidsiteid="&dpwhere"; quit; %let pop=Unconditional Matched Population after; %if &max_day. > 0 %then %do; %tableletter(); %addtotoc(tabnum=Figure &figurenum.&tableletter., caption=%quote(&aggregated.Adjusted Kaplan-Meier Estimate and 95% Confidence Interval of &outcomelabel. Not Occurring Among &AnalysisGroupLabel.&dpinparenthesis. from the &pop. &PSEstimateGroupLabel. in the &database. from &startdateformatted. to &&enddate&j.formatted.&subgrouptitle.)); %end; %else %do; %put WARNING: (Sentinel) Insufficient data to produce unconditional Kaplan-Meier estimate for analysisgrp=&analysisgrp., subgroup=&SubGroup., subgroupcat=&SubgroupCat.. KM curves will not be produced.; %end; %end; %end; /*loop through numsubgroups */ %mend survivalcurvestoc; /*loop through each analysisgrp - dataset only exists if curve computed*/ %do loopcount = 1 %to &numl2comparisons.; data _null_; set l2comparisonfile(where=(order=&loopcount.)); call symputx('runid', runid); call symputx('analysisgrp', analysisgrp); call symputx('kmrefpop',kmrefpop); run; proc sql noprint; select distinct strip(file) into: pscsfile trimmed from pscs_masterinputs where analysisgrp = "&analysisgrp." and runid = "&runid" and missing(subgroup); quit; %if &pscsfile. = psmatchfile | &pscsfile. = stratificationfile | &pscsfile. = iptwfile %then %do; /*loop through periodid*/ %do j = %eval(&look_start) %to %eval(&look_end); %survivalcurvestoc( %if %eval(&num_dp.)=1 %then %do; aggregated=, %end; %else %do; aggregated=%str(Aggregated ), %end; dpinparenthesis=, dpwhere=ALL); * Save number of observations for the aggregated curves for figurenum increment below; %let F3nobsALL=&F3nobs.; %let F4nobsALL=&F3nobs.; %let F5nobsALL=&F3nobs.; *Output separate table for each Data Partner - loop through each DP; %if &stratifybydp. = Y %then %do; %do dps = 1 %to %eval(&num_dp.); %let maskedID = %scan(&masked_dplist,&dps); %survivalcurvestoc(aggregated=, dpinparenthesis=%str( (&maskedid.)), dpwhere=&maskedid.); %end; %end; /*DP stratification*/ %end; /*loop through periodid*/ /* if there is only 1 figure, rewrite figure # - this method is used instead of determining apriori b/c of the numerous permutations of situations that can lead to 1 figure */ %let countkm = 0; proc sql noprint; select count(caption) into: countkm from tableofcontents where index(tabnum, "Figure &figurenum.")>0; quit; %if %eval(&countkm.)=1 %then %do; data tableofcontents; set tableofcontents; if index(tabnum, "Figure &figurenum.")>0 then do; tabnum = "Figure &figurenum."; end; run; %end; %if %eval(&F3nobsALL.>0) | %eval(&F4nobsALL.>0) | %eval(&F5nobsALL.>0) %then %do; %let figurenum = %eval(&figurenum.+1); %let tablecount = 1; %let tableletter =a; %end; %end; /*PSmatch, stratification or IPTW*/ %end; /*loop through numl2comparisons */ %end; /*KM plots*/ %end; /*L2 figures*/ %end; /* Figure file */ /*****************/ /* Appendices */ /*****************/ /*Appendix A*/ %if &produceappendixfileonly. = N %then %do; %addtotoc(tabnum=Appendix A, caption=Dates of Available Data for Each Data Partner (DP) as of Request Distribution Date &datedistributed.); %end; /* The remaining appendices are created in appendix_driver.sas */ /*********************/ /* Remove empty rows */ /*********************/ data tableofcontents; set tableofcontents; if missing(tabnum)=0; run; %put =====> END MACRO: create_tableofcontents ; %mend create_tableofcontents;