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;