**************************************************************************************************** * PROGRAM OVERVIEW **************************************************************************************************** * * PROGRAM: ms_util.sas * * Created (mm/dd/yyyy): 11/17/2014 * Last modified: 11/17/2014 * Version: 1.0 * *-------------------------------------------------------------------------------------------------- * PURPOSE: * This program uses encounter dataset from the MSDD to count the number of distinct visits from a * reference date and within a lookback period. Any encounter type (or group of) can be specified. * The macro will return the total number of encounters (both formatted and unformatted) and, if * requested, the number of encounters by encounter type (unformatted only). * * Program inputs: * - One dataset containing the Encounter data * - One dataset containing PatId and reference date from which the count will be computed. * Program outputs: * -SAS data file (.SAS7BDAT) with the PatId, reference date, the number of total visits * (both group and numeric), and if requested, the number of visits for each specified * encounter type (numeric only) * * Macro variable definitions: * * ENCOUNTERFILE= The name of the SAS dataset containing encounter data * INDEXFILE= The name of the SAS dataset containing the PatId and reference date to compute * the number of visits * INDEXDT= The name of the variable containing the reference date in INDEXFILE * PRIORDAYS= Number of days to define the lookback period to compute the counts * INCLINDEX= Whether to include the reference date in the lookback period (1=yes 0=no) * CARESETTINGS= The encounter type (caresetting) to be used in the count macro * BYENC= Type of output requested. "N" requests counts by all visits (with both the * formatted and unformatted version of counts). "Y" requests unformatted counts by * encounter type in addition to both the formatted and unformatted overall counts * returned under "N". Note - by encounter type and overall will calculate totals * differently (By encounter type allows one visit per day per encounter type. By * overall allows only one visit per day). So if you compare the sum of encounter * type counts to the overall counts, they may not match. * CSSTRAT= The grouping of counts that the user wishes to apply. Groups need to be separated * by a space, intervals defined using "-" and use "+" if the last group is open ended. * OUTFILE= Name of the output dataset containing the counts * * Programming Notes: * -The INDEXFILE input file must contains PatId and a reference date. * -As with all MS Programs, this macro assumes that the encounter table has been * defined according to the MSCDM * -Eligibility criteria are assumed to have been checked (i.e., that patients are * eligible for medical benefits in the PRIORDAYS-INCLINDEX interval) * * *-------------------------------------------------------------------------------------------------- * CONTACT INFO: * Mini-Sentinel Coordinating Center * info@mini-sentinel.org * *-------------------------------------------------------------------------------------------------- * CHANGE LOG: * * Version Date Initials Comment (reference external documentation when available) * ------- -------- -------- --------------------------------------------------------------- * mm/dd/yy * ***************************************************************************************************; %macro ms_util(ENCOUNTERFILE=,INDEXFILE=,INDEXDT=,PRIORDAYS=,INCLINDEX=,CARESETTINGS=,BYENC=,CSSTRAT=,OUTFILE=); %put =====> MACRO CALLED: ms_util v1.0; /*************************************/ /* Reduce data to selected patients */ /* and timeframe */ /*************************************/ ** Accept upper or lower case values for BYENC ** ; %if %UPCASE("&BYENC.") eq "Y" %then %let BYENC = Y ; %let EncCnt=%sysfunc(countw(&CARESETTINGS.)); %let EncString = ; %do i = 1 %to &EncCnt. ; %let enc = %scan(%sysfunc(compress("&CARESETTINGS.","'")),&i.) ; %let EncString = %trim(&EncString. NumVisits_&enc.) ; %end ; ** Defensive Coding - Make sure indexdt is never missing. ** ; data _null_; set &INDEXFILE. ; if missing(&INDEXDT.) then do ; put "Variable &INDEXDT. contains at least one missing value in dataset &INDEXFILE." ; ABORT ; end ; run; data CS(keep=PatId &INDEXDT. Adate EncType &EncString.); if 0 then set &INDEXFILE.; declare hash ht (hashexp:16, dataset:"&INDEXFILE.", MULTIDATA: "Y"); ht.definekey('PatId'); ht.definedata(ALL: 'YES'); ht.definedone(); ** Encounters; do until(eof2); set &ENCOUNTERFILE.(keep=PatId ADate EncType) end=eof2; if ht.find()=0 then do; if &INDEXDT.-&PRIORDAYS. <= ADate < &INDEXDT.-(-1*&INCLINDEX.) then do; %do i = 1 %to &EncCnt. ; %let enc = %scan(%sysfunc(compress("&CARESETTINGS.","'")),&i.) ; NumVisits_&enc. = (EncType eq "&enc.") ; %end ; if indexw(compress("&CARESETTINGS.","'"),EncType) then output; end; ht.has_next(RESULT: anotherCode); do while(anotherCode); if ht.find_next()=0 then do; if &INDEXDT.-&PRIORDAYS. <= ADate < &INDEXDT.-(-1*&INCLINDEX.) then do; %do i = 1 %to &EncCnt. ; %let enc = %scan(%sysfunc(compress("&CARESETTINGS.","'")),&i.) ; NumVisits_&enc. = (EncType eq "&enc.") ; %end ; if indexw(compress("&CARESETTINGS.","'"),EncType) then output; end; end ; ht.has_next(RESULT: anotherCode); end ; end; end; stop; run; ** Create format for strata; %let ALLVAR=; data _null; format FMTSTR $300.; NumGrp = countw("&CSSTRAT.", ' ') ; if (NumGrp eq 0) then FMTSTR='0-high = "0+"'; else do i=1 to NumGrp ; word=tranwrd(scan("&CSSTRAT.",i,' '), "+", "-high")||' = "'|| scan("&CSSTRAT.",i,' ')||'" '; FMTSTR=strip(FMTSTR)||" "||trim(left(word)); output; end; call symput("FMTSTR",FMTSTR); run; %put &FMTSTR.; PROC FORMAT; VALUE CSSTRAT &FMTSTR. ; run; ** Allow one encounter per day ** ; proc summary missing nway data = CS ; class PatId &INDEXDT. ADate ; var NumVisits_: ; output out = CS_SUMM (drop = _:) MAX = ; run ; ** Create output table; proc sql noprint; create table &OUTFILE. as select PatId, &INDEXDT., count(*) as ExactNumVisit from CS_SUMM group by PatId, &INDEXDT. order by PatId, &INDEXDT. ; quit; proc sort data = &INDEXFILE. out = indexfile ; by PatId &INDEXDT. ; run ; data &OUTFILE.; merge indexfile (in = a keep=PatId &INDEXDT.) &OUTFILE. (in = b) ; by PatId &INDEXDT.; if a; if ^b then ExactNumVisit=0; run; data &OUTFILE.; retain PatId &INDEXDT. NumVisit ExactNumVisit ; format ExactNumVisit best.; set &OUTFILE.; NumVisit=put(ExactNumVisit, CSSTRAT.); keep PatId &INDEXDT. NumVisit ExactNumVisit ; run; title 'Overall Utilization' ; proc freq data=&OUTFILE.; tables NumVisit ; run; proc means data=&OUTFILE. n mean stddev min p50 max ; var ExactNumVisit ; run ; title '' ; ** If also want output by encounter type ** ; %if ("&ByEnc." eq "Y") %then %do ; proc summary missing nway data = CS ; class PatId &INDEXDT. EncType ADate ; var NumVisits_: ; output out = CS_SUMM2 (drop = _:) MAX = ; run ; proc sql noprint; create table outputBYENC as select PatId, &INDEXDT., %do i = 1 %to &EncCnt. ; %let enc = %scan(%sysfunc(compress("&CARESETTINGS.","'")),&i.) ; sum(NumVisits_&enc.) as NumVisits_&enc. %if ("&i." ne "&EncCnt.") %then %do ; , %end ; %end ; from CS_SUMM2 group by PatId, &INDEXDT. order by PatId, &INDEXDT. ; quit; data &OUTFILE.; merge &OUTFILE. (in = a) outputBYENC (in = b) ; by PatId &INDEXDT. ; if a; if ^b then do ; %do i = 1 %to &EncCnt. ; %let enc = %scan(%sysfunc(compress("&CARESETTINGS.","'")),&i.) ; NumVisits_&enc.=0 ; %end ; end ; run; title 'By Encounter Type' ; proc means data=&OUTFILE. n mean stddev min p50 max ; var &EncString. ; run ; title '' ; %end ; proc datasets library = work nolist; delete CS CS_SUMM CS_SUMM2 outputBYENC ; quit; %put NOTE: ********END OF MACRO: ms_util v1.0********; %mend ms_util;