Chapter 2: SAS Code
Sample dataset codebook:
treat = Binary indicator of treatment versus control group
x1-x5 = continuous confounders associated with Treat
cont_out = Continuous outcome of interest
bin_out = Binary outcome of interest
Estimating the propensity score in SAS with logistic regression
SAS> proc logistic data=mydata descending;
model treat=x1 x2 x3 x4 x5;
output out=mydata_ps prob=pscore;
run;
MATCHING
K:1 matching without replacement using GMATCH macro
/* Include %GMATCH
SAS> %include ‘gmatch.sas’;
/* Have previously estimated propensity score “pscore”
/* 1:1 nearest neighbor matching, matching on propensity score
SAS> %gmatch(data=mydata_ps, group=treat, id=id, mvars=pscore, seedca=111222, seedco=111333, out=matched);
/* 2:1 matching, matching on PS, caliper=0.10 units
SAS> %gmatch(data=mydata_ps, group=treat, id=id; mvars=pscore, dmaxk=0.10, ncontls=2, seedca=111222, seedco=111333, out=matched);
Caliper matching using GMATCH macro
/* Want to specify caliper in terms of SD of PS logit
/* Calculate standard deviation of logit of propensity score
SAS> proc means std data=mydata_ps;
var logit_ps;
output out=std_ps (keep=std) std=std;
run;
/* Create new variable that is 0.20 SD of logit of PS
SAS> data std_ps; set std_ps;
std=0.2*std;
run;
/*Create macro variable that specifies caliper size for matching
SAS> data _null_;
set std_ps;
call symput(‘stdcal’,std);
run;
/* 2:1 caliper matching, caliper=0.20 SD of logit of PS
SAS> %gmatch(data=mydata_ps, group=treat, id=id, mvars=logit_ps, dmaxk=&stdcal, ncontls=2, seedca=111222, seedco=111333, out=matched);
K:1 matching, with and without replacement using PSMatching macro
/* Include %PSMatching macro
SAS> %include ‘PSMatching.sas’;
/* Have previously estimated propensity score “pscore”
/* Separate datasets for treated and control, only contain ID and propensity score (or alternatively logit of PS)
SAS> data mydataC mydataT;
set mydata_ps;
if treat=0 then do;
idC=id; pscoreC=pscore; output mydataC;
end;
if treat=1 then do;
idT=id; pscoreT=pscore; output mydataT;
end;
/* 1:1 Nearest neighbor matching without replacement
SAS> %PSMatching(datatreatment=mydataT, datacontrol= mydataC, method=nn, numberofcontrols=1, replacement=no, out=matches);
Caliper matching using PSMatching macro
/* 2:1 caliper matching with replacement, 0.10 caliper
SAS> %PSMatching(datatreatment= mydataT, datacontrol= mydataC, method=caliper, caliper=0.10, numberofcontrols=2, replacement=yes, out=matches);
Radius matching using PSMatching macro
SAS> %PSMatching(datatreatment= mydataT, datacontrol= mydataC, method=radius, caliper=0.20, replacement=no, out=matches);
Optimal matching using DIST and VMATCH macros
/* Include %DIST and %VMATCH macro
SAS> %include ‘dist.sas’;
SAS> %include ‘vmatch.sas’;
/* If have already calculated distance matrix, can run vmatch
SAS> %vmatch(dist=mydata_dist, idca=id, a=2, b=2,lilm=num_of_control, n=num_of_treat, firstco=C_first,lastco=C_last);
/* Can run %VMATCH through %DIST macro
/* Does 1:1 optimal matching
SAS> %dist(data=mydata, group=treat, id=id, mvars=pscore,
wts=1, vmatch=Y, a=1, b=1, lilm= num_of_control, dmax=0.1,
outm=mp1_b, summatch=n, mergeout=mpropen);
/* Optimal matching; each treated matched with 1-3 controls
SAS> %dist(data=mydata, group=treat, id=id, mvars=pscore,
wts=1, vmatch=Y, a=1, b=3, lilm= num_of_control, dmax=0.1,
outm=mp1_b, summatch=n, mergeout=mpropen);
Balance diagnostics
/* Sort data by treatment groups
SAS> proc sort data = mydata_ps;
by treat;
/* Boxplot of PS by treatment group
SAS> proc boxplot data= mydata_ps;
symbol width = 2;
plot pscore*treat;
run;
/* Stacked histogram of PS by treatment group
SAS> ODS graphics on;
proc univariate noprint data= mydata_ps;
var pscore;
class treat;
histogram pscore / nrows=2 ;
run;
ODS graphics off;
PROPENSITY SCORE WEIGHTING, PARAMETRIC PS ESTIMATION
/* Estimate the propensity score with logistic regression
SAS> proc logistic data=mydata descending;
model treat = x1 x2 x3 x4 x5;
output out=mydata_ps prob=pscore;
run;
/* Calculate ATE and ATT propensity score weights
SAS> data mydata_ps.w;
set mydata_ps;
w_ate= treat/pscore + (1-treat)/(1-pscore);
w_att= treat + (1-treat)*(pscore/(1-pscore));
run;
/* Use ATE weights as probability weights in final analysis
SAS> proc genmod data=mydata_ps.w,
class id;
model cont_out = treat x1 x2 x3 x4 x5 / error=B;
weight w_ate;
run;
/* Use ATT weights as probability weights in final analysis
SAS> proc genmod data=mydata_ps.w,
class id;
model cont_out = treat x1 x2 x3 x4 x5 / error=B;
weight w_att;
run;
SUBCLASSIFICATION
Creating 5 propensity score subclasses
/* After generating propensity scores, create quintiles
SAS> proc rank data = mydata_ps groups=5 out= ps_strataranks;
var pscore;
ranks ps_quint;
run;
/* Sort data by quintiles
SAS> proc sort data = ps_strataranks;
by ps_quint;
Estimating subclass-specific and overall effect estimates
/* Binary outcome: Mantel-Haenszel stratified analysis
SAS> proc freq data= ps_strataranks;
table ps_quint*treat*bin_out / nocol cmh;
run;
/* Continuous outcome: can combine subclass-specific t-tests
SAS> proc ttest data=ps_strataranks;
by ps_quint;
class treat;
var cont_out;
ods output statistics = strata_out;
/* Calculate weights (weight is inverse of square of SE of group difference); weight group differences
SAS> data weights;
set strata_out;
if class = 'Diff (1-2)';
wt_i = 1/(StdErr**2);
wt_diff = wt_i*Mean;
/* Sum weighted means
SAS> proc means noprint data = weights;
var wt_i wt_diff;
output out = total sum = sum_wt sum_diff;
/* Calculate and output overall treatment difference, SE
SAS> data overall;
set total;
diff_mean = sum_diff/sum_wt;
diff_SE = SQRT(1/sum_wt);
SAS> proc print data = overall;
run;