%meta_rr
%meta_or
%meta_rd
%my_meta_norm
%ForestMacro
%PubBias
%macro pseudo_rsq(log_lh=, log_lh_i=, k=, obs=);
proc iml;
value = 1-((&log_lh.)/&log_lh_i.);
/* cox_snell_rsq = 1 - (&log_lh_i./&log_lh.)##(2/&obs.);*/
title "McFadden's R-Squared";
print value;
title;
quit;
%mend;
%macro rsq(data = , p=);
%let open_d = %sysfunc (open(&data.));
%let obs = %sysfunc(attrn(&open_d., nobs));
%let close_d = %sysfunc(close(&open_d.));
proc iml;
use &data.;
read all var _all_;
close &data.;
SSres = sum((rri-pred)##2);
/* SSt = sum(rri##2) - &obs.#(mean(rri)##2);*/
sst = sum((rri-mean(rri))##2);
ADJ_R_SQ = 1 - ((ssres/(&obs.-&p.)) / (sst/(&obs.-1)));
print ADJ_R_SQ;
quit;
%mend;
%macro meta_rr(data = , ai = , bi = , ci = , di = ,references=);
data new_data dorp_data;
set &data.;
if &ai. = 0 or &bi. = 0 or &ci. = 0 or &di. = 0 then do;
output dorp_data;
delete;
end;
if ai = bi and ci = di then do;
output dorp_data;
delete;
end;
output new_data;
run;
proc iml;
use new_data;
read all var _all_;
close new_data;
%let open_d = %sysfunc (open(new_data));
%let obs_ = %sysfunc(attrn(&open_d., nobs));
%let close_d = %sysfunc(close(&open_d.));
%let obs_1 = %sysevalf(&obs_ - 1);
n1 = &ai.+&ci.;
n2 = &bi.+&di.;
m1 = &ai.+&bi.;
m2 = &ci.+&di.;
N_total = &ai. +&bi.+&ci.+&di.;
/*99% 2.576*/
eer = &ai. / n1; /*98% 2.326*/
cer = &bi. / n2;
nnt = 1/abs(eer-cer);
nnt_ci_l = 1/((eer-cer)+1.96*sqrt((eer#(1-eer)/n1)+(cer#(1-cer)/n2)));
nnt_ci_u = 1/((eer-cer)-1.96*sqrt((eer#(1-eer)/n1)+(cer#(1-cer)/n2)));
nnh = 1/abs(eer-cer);
nnh_ci_l = 1/((eer-cer)+1.96*sqrt((eer#(1-eer)/n1)+(cer#(1-cer)/n2)));
nnh_ci_u = 1/((eer-cer)-1.96*sqrt((eer#(1-eer)/n1)+(cer#(1-cer)/n2)));
/*RR*/
pti = &ai./(&ai.+&ci.);
pci = &bi./(&bi.+&di.);
rri = pti/pci;
ln_rri = log(rri);
var_ln_rri = (1-pti)/(n1#pti) + (1-pci)/(n2#pci);
wi = 1/var_ln_rri;
rr_pooled = exp(sum(wi#log(rri))/sum(wi));
se_ln_rr_pooled = sqrt(1/(sum(wi)));
se_ln_rr=((1-pti)/(&ai.+&ci.)/pti+(1-pci)/(&bi.+&di.)/pci)##0.5;
rri_ci_l = exp(log(rri)-(1.96#se_ln_rr));
rri_ci_u = exp(log(rri)+(1.96#se_ln_rr));
rr_pooled_ci_l = exp(log(rr_pooled)-(1.96#se_ln_rr_pooled));
rr_pooled_ci_u = exp(log(rr_pooled)+(1.96#se_ln_rr_pooled));
print rri rri_ci_l rri_ci_u rr_pooled rr_pooled_ci_l rr_pooled_ci_u se_ln_rr_pooled ;
Zrr = log(rr_pooled)/se_ln_rr_pooled; /*ho: rr=1 reject h0 if |zrr|>=1.96*/
/* zrr_pv2 = 1 - cdf("Normal", Zrr); */
zrr_pv = (1-probnorm(abs(Zrr)))#2;
print zrr zrr_pv;
Qrr = sum(wi#((log(rri)-log(rr_pooled))##2));/*ho: rr1=rr2....=rr reject h0 qrr_pv>qrr_pv_005*/
/* qrr_pv_005 = cinv(0.95, 8);*/
qrr_pv = (1-probchi(Qrr, &obs_1.));
print Qrr qrr_pv;
i2 = (Qrr - &obs_1.)/ qrr;
print i2;
/* Random Effect */
t_2 = max(0, (Qrr-(&obs_1.)) / (sum(wi)- (sum(wi##2)) /sum(wi))) ;
wi_ = 1/(var_ln_rri+t_2);
theta_ = sum(wi_#rri) / sum(wi_);
se_theta_ = 1 / sqrt(sum(wi_));
theta__ci_l = theta_ - 1.96 # se_theta_;
theta__ci_u = theta_ + 1.96 # se_theta_;
r_pooled_rr = exp(sum(wi_#log(rri))/sum(wi_));
se_r_pooled_rr = 1 / sqrt(sum(wi_));
r_pooled_rr_ci_l = r_pooled_rr - 1.96#se_r_pooled_rr;
r_pooled_rr_ci_u = r_pooled_rr + 1.96#se_r_pooled_rr;
print t_2 wi_ theta_ se_theta_ theta__ci_l theta__ci_u ;
print r_pooled_rr se_r_pooled_rr r_pooled_rr_ci_l r_pooled_rr_ci_u;
/*theta_的有效性檢驗, h0 : theta_ =0 */
utheta_ = ((sum(wi_ # rri))##2 ) / sum(wi_);
utheta__pv = (1-probchi(utheta_, 1));
zutheta_ = theta_ / se_theta_;
zutheta__pv = (1-probnorm(abs(zutheta_)))#2;
print utheta_ utheta__pv zutheta_ zutheta__pv;
median_ln_rri = median(ln_rri);
wi_pct = wi/sum(wi);
sum_w = sum(wi_pct);
create output var {rri rri_ci_l rri_ci_u rr_pooled rr_pooled_ci_l rr_pooled_ci_u se_ln_rr zrr Qrr
qrr_pv ln_rri wi n1 n2 median_ln_rri i2 wi_pct wi_};
append;
close output;
GroupId =3;
create pooled var {rr_pooled rr_pooled_ci_l rr_pooled_ci_u GroupId};
append;
close output;
create ran_pooled var {r_pooled_rr r_pooled_rr_ci_l r_pooled_rr_ci_u GroupId};
append;
close output;
quit;title;
data output2;
merge output new_data;
obs = _n_;
GroupId = 1;
run;
data output3;
set output2
pooled (rename=(RR_POOLED=rri rr_pooled_ci_l=rri_ci_l rr_pooled_ci_u=rri_ci_u))
ran_pooled (rename=(r_pooled_rr=rri r_pooled_rr_ci_l=rri_ci_l r_pooled_rr_ci_u=rri_ci_u));
if _n_ = &obs_.+1 then &references. = "Pooled_F";
if _n_ = &obs_.+2 then &references. = "Pooled_R";
run;
proc sgplot data = output2;
HIGHLOW y=obs HIGH=rri_ci_u LOW=rri_ci_l / close = rri ;
REFLINE 1 / label = "1" AXIS= X;
yaxis REVERSE;
quit;
proc print data = output2(keep = &references. rri rri_ci_u rri_ci_l);
run;
/*
proc sgplot data = output2;
STEP x = obs y= wi / DATALABEL = wi;
quit;
*/
proc sgplot data = output2;
scatter y = se_ln_rr x = ln_rri / datalabel = obs;
yaxis REVERSE;
refline median_ln_rri / axis = x;
quit;
%mend;
%macro meta_or(data = , ai = , bi = , ci = , di = ,references=);
data new_data dorp_data;
set &data.;
if &ai. = 0 or &bi. = 0 or &ci. = 0 or &di. = 0 then do;
output dorp_data;
delete;
end;
if ai = bi and ci = di then do;
output dorp_data;
delete;
end;
output new_data;
run;
proc iml;
use new_data;
read all var _all_;
close new_data;
%let open_d = %sysfunc (open(new_data));
%let obs_ = %sysfunc(attrn(&open_d., nobs));
%let close_d = %sysfunc(close(&open_d.));
%let obs_1 = %sysevalf(&obs_ - 1);
n1 = &ai.+&ci.;
n2 = &bi.+&di.;
m1 = &ai.+&bi.;
m2 = &ci.+&di.;
N_total = &ai. +&bi.+&ci.+&di.;
/*99% 2.576*/
eer = &ai. / n1; /*98% 2.326*/
cer = &bi. / n2;
nnt = 1/abs(eer-cer);
nnt_ci_l = 1/((eer-cer)+1.96*sqrt((eer#(1-eer)/n1)+(cer#(1-cer)/n2)));
nnt_ci_u = 1/((eer-cer)-1.96*sqrt((eer#(1-eer)/n1)+(cer#(1-cer)/n2)));
nnh = 1/abs(eer-cer);
nnh_ci_l = 1/((eer-cer)+1.96*sqrt((eer#(1-eer)/n1)+(cer#(1-cer)/n2)));
nnh_ci_u = 1/((eer-cer)-1.96*sqrt((eer#(1-eer)/n1)+(cer#(1-cer)/n2)));
/*OR*/
oddsti = &ai./&ci.;
oddsci = &bi./&di.;
ori = oddsti/oddsci;
/*Woolf's Logit estimate of OR*/
var_ln_ori = 1/&ai. + 1/&bi. + 1/&ci. + 1/&di.;
wi = 1 / var_ln_ori;
ln_orw = sum(wi#log(ori))/sum(wi);
orw = exp(ln_orw);
ln_ori = log(ori);
se_ln_or = sqrt(var_ln_ori);
ln_ori_ci_l = ln_ori - 1.96#sqrt(var_ln_ori);
ln_ori_ci_u = ln_ori + 1.96#sqrt(var_ln_ori);
ori_ci_l = exp(ln_ori_ci_l);
ori_ci_u = exp(ln_ori_ci_u);
orw_ci_l = exp(ln_orw-1.96#sqrt(1/sum(wi)));
orw_ci_u = exp(ln_orw+1.96#sqrt(1/sum(wi)));
print ori ori_ci_l ori_ci_u ln_ori ln_ori_ci_l ln_ori_ci_u orw orw_ci_l orw_ci_u;
/* orw顯著性 h0:ORw=OR0 (e.g. or0=1)*/
or0=1;
Uw = sum(wi)#((ln_orw-log(or0))##2);
Uw_pv = (1-probchi(Uw, 1));
print Uw Uw_pv;
/*ori同質性檢定 H0: or1=or2=...=or*/
Qw = sum(wi#(log(ori)##2)) - sum(wi)#(ln_orw##2);
Qw_pv = (1-probchi(Qw, &obs_1.));
print Qw Qw_pv;
i2 = (qw - &obs_1. )/ qw;
print i2;
/* Random Effect */
t_2 = max(0, (Qw-(&obs_1.)) / (sum(wi)- (sum(wi##2)) /sum(wi))) ;
wi_ = 1/(var_ln_ori+t_2);
theta_ = sum(wi_#ori) / sum(wi_);
se_theta_ = 1 / sqrt(sum(wi_));
theta__ci_l = theta_ - 1.96 # se_theta_;
theta__ci_u = theta_ + 1.96 # se_theta_;
r_pooled_or = exp(sum(wi_#log(ori))/sum(wi_));
se_r_pooled_or = 1 / sqrt(sum(wi_));
r_pooled_or_ci_l = r_pooled_or - 1.96#se_r_pooled_or;
r_pooled_or_ci_u = r_pooled_or + 1.96#se_r_pooled_or;
print t_2 wi_ theta_ se_theta_ theta__ci_l theta__ci_u ;
print r_pooled_or se_r_pooled_or r_pooled_or_ci_l r_pooled_or_ci_u;
/*theta_的有效性檢驗, h0 : theta_ =0 */
utheta_ = ((sum(wi_ # ori))##2 ) / sum(wi_);
utheta__pv = (1-probchi(utheta_, 1));
zutheta_ = theta_ / se_theta_;
zutheta__pv = (1-probnorm(abs(zutheta_)))#2;
print utheta_ utheta__pv zutheta_ zutheta__pv;
median_ln_ori = median(ln_ori);
wi_pct = wi/sum(wi);
create output var { i2
ori ori_ci_l ori_ci_u ln_ori ln_ori_ci_l ln_ori_ci_u orw orw_ci_l orw_ci_u se_ln_or
Uw Uw_pv Qw
Qw_pv wi median_ln_ori n1 n2 wi_pct wi_};
append;
close output;
GroupId=3;
create pooled var {orw orw_ci_l orw_ci_u GroupId};
append;
close output;
create ran_pooled var {r_pooled_or r_pooled_or_ci_l r_pooled_or_ci_u GroupId};
append;
close output;
quit;title;
data output2;
merge output new_data;
obs = _n_;
GroupId = 1;
run;
data output3;
set output2
pooled (rename=(orw=ori orw_ci_l=ori_ci_l orw_ci_u=ori_ci_u))
ran_pooled (rename=(r_pooled_or=ori r_pooled_or_ci_l=ori_ci_l r_pooled_or_ci_u=ori_ci_u));
if _n_ = &obs_.+1 then &references. = "Pooled_F";
if _n_ = &obs_.+2 then &references. = "Pooled_R";
run;
proc sgplot data = output2;
HIGHLOW y=obs HIGH=ori_ci_u LOW=ori_ci_l / close = ori;
REFLINE 1 / label = "1" AXIS= X;
yaxis REVERSE;
quit;
proc print data = output2(keep = &references. ori ori_ci_u ori_ci_l);
run;
/*
proc sgplot data = output2;
STEP x = obs y= wi_or / DATALABEL = wi_or;
quit;
*/
proc sgplot data = output2;
scatter y = se_ln_or x = ln_ori / datalabel = obs;
yaxis REVERSE;
refline median_ln_ori / axis = x;
quit;
%mend;
%macro meta_rd(data = , ai = , bi = , ci = , di = ,references=);
data new_data dorp_data;
set &data.;
if ai = 0 and bi = 0 then do;
output dorp_data;
delete;
end;
if ai = bi and ci = di then do;
output dorp_data;
delete;
end;
output new_data;
run;
proc iml;
use new_data;
read all var _all_;
close new_data;
%let open_d = %sysfunc (open(new_data));
%let obs_ = %sysfunc(attrn(&open_d., nobs));
%let close_d = %sysfunc(close(&open_d.));
%let obs_1 = %sysevalf(&obs_ - 1);
n1 = &ai.+&ci.;
n2 = &bi.+&di.;
m1 = &ai.+&bi.;
m2 = &ci.+&di.;
N_total = &ai. +&bi.+&ci.+&di.;
/*99% 2.576*/
eer = &ai. / n1; /*98% 2.326*/
cer = &bi. / n2;
nnt = 1/abs(eer-cer);
nnt_ci_l = 1/((eer-cer)+1.96*sqrt((eer#(1-eer)/n1)+(cer#(1-cer)/n2)));
nnt_ci_u = 1/((eer-cer)-1.96*sqrt((eer#(1-eer)/n1)+(cer#(1-cer)/n2)));
nnh = 1/abs(eer-cer);
nnh_ci_l = 1/((eer-cer)+1.96*sqrt((eer#(1-eer)/n1)+(cer#(1-cer)/n2)));
nnh_ci_u = 1/((eer-cer)-1.96*sqrt((eer#(1-eer)/n1)+(cer#(1-cer)/n2)));
/*RD*/
pti = &ai./(&ai.+&ci.);
pci = &bi./(&bi.+&di.);
RDi = pti - pci;
var_rdi = (pti#(1-pti))/n1 + (pci#(1-pci))/n2;
se_rd = sqrt(var_rdi);
wi = 1/var_rdi;
RD_pooled = sum(wi#rdi) / sum(wi);
var_rd_p = 1/sum(wi);
se_rd_p = 1/sqrt(sum(wi));
RDi_ci_l = RDi - 1.96#sqrt(var_rdi);
RDi_ci_u = RDi + 1.96#sqrt(var_rdi);
RDp_ci_l = RD_pooled - 1.96#se_rd_p;
RDp_ci_u = RD_pooled + 1.96#se_rd_p;
print rdi RDi_ci_l RDi_ci_u RD_pooled RDp_ci_l RDp_ci_u;
/* rd顯著性 h0:rd=0 if |zrd|>1.96 reject ho*/
Zrd = RD_pooled/se_rd_p;
zrd_pv = (1-probnorm(abs(Zrd)))#2;
print zrd zrd_pv;
/* rd同質性 h0:rd1=rd2...*/
Qrd = sum(wi#((RDi-RD_pooled)##2));
Qrd_pv = (1-probchi(Qrd, &obs_1.));
print Qrd Qrd_pv;
i2 = (qrd - &obs_1.) / qrd;
print i2;
/* Random Effect */
t_2 = max(0, (Qrd-(&obs_1.)) / (sum(wi)- (sum(wi##2)) /sum(wi))) ;
wi_ = 1/(var_rdi+t_2);
theta_ = sum(wi#rdi) / sum(rdi);
se_theta_ = 1 / sqrt(sum(wi_));
theta__ci_l = theta_ - 1.96 # se_theta_;
theta__ci_u = theta_ + 1.96 # se_theta_;
r_pooled_rd = exp(sum(wi_#rdi)/sum(wi_));
se_r_pooled_rd = 1 / sqrt(sum(wi_));
r_pooled_rd_ci_l = r_pooled_rd - 1.96#se_r_pooled_rd;
r_pooled_rd_ci_u = r_pooled_rd + 1.96#se_r_pooled_rd;
print t_2 wi_ theta_ se_theta_ theta__ci_l theta__ci_u ;
print r_pooled_rd se_r_pooled_rd r_pooled_rd_ci_l r_pooled_rd_ci_u;
/*theta_的有效性檢驗, h0 : theta_ =0 */
utheta_ = ((sum(wi_ # rdi))##2 ) / sum(wi_);
utheta__pv = (1-probchi(utheta_, 1));
zutheta_ = theta_ / se_theta_;
zutheta__pv = (1-probnorm(abs(zutheta_)))#2;
print utheta_ utheta__pv zutheta_ zutheta__pv;
median_rdi = median(rdi);
wi_pct = wi/sum(wi);
create output var {
i2 Qw_pv rdi RDi_ci_l RDi_ci_u RD_pooled RDp_ci_l RDp_ci_u se_rd zrd Qrd Qrd_pv
wi median_rdi n1 n2 wi_pct};
append;
close output;
GroupId=3;
create pooled var {RD_pooled RDp_ci_l RDp_ci_u GroupId};
append;
close output;
create ran_pooled var { r_pooled_rd r_pooled_rd_ci_l r_pooled_rd_ci_u GroupId};
append;
close output;
quit;title;
data output2;
merge output new_data;
obs = _n_;
GroupId = 1;
run;
data output3;
set output2
pooled (rename=(RD_pooled=rdi RDp_ci_l=rdi_ci_l RDp_ci_u=rdi_ci_u))
ran_pooled (rename=(r_pooled_rd=rdi r_pooled_rd_ci_l=rdi_ci_l r_pooled_rd_ci_u=rdi_ci_u));
if _n_ = &obs_.+1 then &references. = "Pooled_F";
if _n_ = &obs_.+2 then &references. = "Pooled_R";
run;
proc sgplot data = output2;
HIGHLOW y=obs HIGH=rdi_ci_u LOW=rdi_ci_l / close = rdi;
REFLINE 0 / label = "0" AXIS= X;
yaxis REVERSE;
quit;
proc print data = output2(keep = &references. rdi rdi_ci_u rdi_ci_l);
run;
/*
proc sgplot data = output2;
STEP x = obs y= wi / DATALABEL = wi;
quit;
*/
proc sgplot data = output2;
scatter y = se_rd x = rdi / datalabel = obs;
yaxis REVERSE;
refline median_rdi / axis = x;
quit;
%mend;
%macro my_meta_norm(data = , NT = , YT = , ST = , NC = , YC = , SC = , references=);
%let open_d = %sysfunc (open(&data.));
%let obs_ = %sysfunc(attrn(&open_d., nobs));
%let close_d = %sysfunc(close(&open_d.));
%let obs_1 = %sysevalf(&obs_ - 1);
proc iml;
use &data.;
read all var _all_;
close &data.;
/* si = sqrt(st##2+sc##2-2*0.7*st*sc) /sqrt(2*(1-0.7)); */
/* si = sqrt(((nt-1)#((st)##2) + (nc-1)#((sc)##2))/(nt+nc-2));*/
/* esi = (yt - yc)/si;*/
/* proc iml;
yt = {54 48 40};
yc = {71 72 75};
st={14 15 11};
sc = {6 8 8};
n = {36 39 37};
nt = {36 39 37};
nc = {36 39 37};
si = sqrt(st##2+sc##2-2#0.7#st#sc) /sqrt(2#(1-0.7));
esi = (yt - yc)/si;
se_esi = sqrt((1/n)+(esi##2)/(2#n)) # sqrt(2#(1-0.7));
create d1 var {yt yc st sc n si esi se_esi nt nc};
append;
close d1;
quit;*/
/* m = nt+nc -2;*/
/* m = nt -1;*/
/* cm = 1 - (3/(4#m-1));*/
/* esui = cm#esi;*/
/* var_esui = (nt+nc)/(nt#nc) + ((esui)##2)/(2#(nt+nc));*/
/* se_esui = sqrt(var_esui);*/
esui_ci_l = esui - 1.96 #se_esui;
esui_ci_u = esui + 1.96 #se_esui;
print si esi esui var_esui esui_ci_l esui_ci_u;
wi = 1/var_esui;
pooled_es = sum(wi#esui) / sum(wi);
se_pooled_es = 1 / sqrt(sum(wi));
pooled_es_ci_l = pooled_es - 1.96#se_pooled_es;
pooled_es_ci_u = pooled_es + 1.96#se_pooled_es;
print wi pooled_es se_pooled_es pooled_es_ci_l pooled_es_ci_u;
/* print wi2 pooled_es2 se_pooled_es2;*/
/*ES的顯著性檢驗, */
/* i.e. Testing H0: Effect Size = 0*/
ues = ((sum(wi#esui))##2) / sum(wi);
ues_pv = (1-probchi(ues, 1));
print ues ues_pv;
median_esui = median(esui);
/*ES的同質性檢驗, */
/* i.e. Testing H0: = = = */
qes = sum(wi# ((esui-pooled_es)##2));
qes_pv = (1-probchi(qes, &obs_1.));
print qes qes_pv;
i2 = (qes -&obs_1.)/qes;
print i2;
wi_pct = wi/sum(wi);
/* Random Effect Model*/
t_2 = max(0, (qes-(&obs_1.)) / (sum(wi)- (sum(wi##2)) /sum(wi))) ;
wi_ = 1/(var_esui+t_2);
wi__ = (wi_/sum(wi_) )*100;
theta_ = sum(wi_#esui) / sum(wi_);
se_theta_ = 1 / sqrt(sum(wi_));
theta__ci_l = theta_ - 1.96 # se_theta_;
theta__ci_u = theta_ + 1.96 # se_theta_;
r_pooled_es = sum(wi_#esui) / sum(wi_);
se_r_pooled_es = 1 / sqrt(sum(wi_));
r_pooled_es_ci_l = pooled_es - 1.96#se_r_pooled_es;
r_pooled_es_ci_u = pooled_es + 1.96#se_r_pooled_es;
print t_2 wi_ wi__ theta_ se_theta_ theta__ci_l theta__ci_u ;
print r_pooled_es se_r_pooled_es r_pooled_es_ci_l r_pooled_es_ci_u;
/*r ES的顯著性檢驗, */
/* i.e. Testing H0: Effect Size = 0*/
ues = ((sum(wi_#esui))##2) / sum(wi);
ues_pv = (1-probchi(ues, 1));
print ues ues_pv;
/*r ES的同質性檢驗, */
/* i.e. Testing H0: = = = */
qes = sum(wi_# ((esui-pooled_es)##2));
qes_pv = (1-probchi(qes, &obs_1.));
print qes qes_pv;
i2 = (qes -&obs_1.)/qes;
print i2;
wi_pct = wi/sum(wi);
/*theta_的有效性檢驗, h0 : theta_ =0 */
utheta_ = ((sum(wi_ # esui))##2 ) / sum(wi_);
utheta__pv = (1-probchi(utheta_, 1));
zutheta_ = theta_ / se_theta_;
zutheta__pv = (1-probnorm(abs(zutheta_)))#2;
print utheta_ utheta__pv zutheta_ zutheta__pv;
GroupId=3;
create pooled var {pooled_es pooled_es_ci_l pooled_es_ci_u GroupId};
append;
close pooled;
create ran_pooled var {r_pooled_es r_pooled_es_ci_l r_pooled_es_ci_u GroupId};
append;
close ran_pooled;
;
create output var { i2 si esi esui var_esui se_esui esui_ci_l esui_ci_u pooled_es se_pooled_es pooled_es_ci_l pooled_es_ci_u ues ues_pv qes qes_pv
median_esui wi_ wi__ r_pooled_es se_r_pooled_es r_pooled_es_ci_l r_pooled_es_ci_u };
append;
close output;
quit;
data output2;
merge output (rename=(esui=smd esui_CI_L=lcl esui_CI_u=ucl WI__ = weigth_pct)) &data.;
obs = _n_;
GroupId = 1;
smd_ = Round(smd, 0.01);
run;/*
data output3;
set output2
pooled (rename=(pooled_es=esui pooled_es_ci_l=esui_ci_l pooled_es_ci_u=esui_ci_u))
ran_pooled (rename=(r_pooled_es=esui r_pooled_es_ci_l=esui_ci_l r_pooled_es_ci_u=esui_ci_u));
if _n_ = &obs_.+1 then &references. = "Pooled_F";
if _n_ = &obs_.+2 then &references. = "Pooled_R";
run;
*/
proc sgplot data = output2;
HIGHLOW y=obs HIGH=esui_ci_u LOW=esui_ci_l / close = esui ;
REFLINE 0 / label = "0" AXIS= X;
yaxis REVERSE;
quit;
proc print data = output2(keep = &references. esui esui_ci_l esui_ci_u wi);
run;
proc sgplot data = output2;
scatter y = se_esui x = esui / datalabel = obs;
refline median_esui / axis = x;
yaxis REVERSE;
quit;
%mend;
%macro symsize;
data _null_;
set metadat;
retain sizeh1-sizeh%eval(&n_stud+2)
fontv1-fontv%eval(&n_stud+2);
length fontv1-fontv%eval(&n_stud+2) $20.;
array sizes sizeh1-sizeh%eval(&n_stud+2);
array fvs $ fontv1-fontv%eval(&n_stud+2);
do i=1 to (&n_stud)+2;
if i=y then do;
sizes{i}=size*11/&maxsize;
fvs{i}='font=specialu v=K';
if i=&n_stud+2 then output;
end;
end;
%do i=1 %to (&n_stud)+2;
call symput("sh&i",trim(left(put(sizeh&i,6.2))));
call symput("fv&i",trim(left(put(fontv&i,20.))));
%global sh&i fv&i;
%end;
run;
%mend symsize;
%macro doall (study);
%do %while (&study le (&n_stud+2));
if y=&study then do;
xx&study=lower95;
yy&study=y+0.2; output;
yy&study=y-0.2; output;
yy&study=y;
output;
xx&study=upper95; output;
yy&study=y+0.2;output;
yy&study=y-0.2; output;
end;
%let study=%eval(&study+1);
%end;
%mend doall;
%macro syms;
%do i=1 %to (&n_stud)+2;
symbol&i &&fv&i l=1 interpol=none h=&&sh&i color=black;
%end;
%mend syms;
%macro plotpts;
%do i=1 %to (&n_stud + 2);
yy&i*xx&i=%eval(&n_stud+3)
%end;
%do i=1 %to (&n_stud + 2);
y&i*x&i=&i
%end;
%mend plotpts;
%Macro PubBias(N1=size1,N2=size2,di=effsize,studyID=study,ModelType=1,Dataset=MetaPub);
proc iml;
use &Dataset;
read all var{&N1} into n1_vec;
read all var{&N2} into n2_vec;
read all var{&di} into di_vec;
read all var{&n1 &n2} into n_vec;
k= nrow(di_vec);
start mean_d(di_vec,var_di,tau2,d_mean,resum_wt,vi_star,d_SE,upper95,lower95);
k = nrow(di_vec);
d_mean = 0;
resum_wt = 0;
vi_star = J(k,1,0);
do i = 1 to k;
d_mean = d_mean + di_vec[i,1]/(var_di[i,1]+tau2);
resum_wt = resum_wt + (var_di[i,1]+tau2)##-1;
vi_star[i,1] = var_di[i,1]+tau2;
end;
d_mean = d_mean/resum_wt;
d_SE = SQRT(resum_wt##-1);
upper95 = d_mean + 1.96#d_SE;
lower95 = d_mean - 1.96#d_SE;
finish;
start calcq(di_vec,n_vec,qq,prob_qq1,var_di);
k = nrow(di_vec);
var_di=J(k,1,0);
do i = 1 to k;
var_di[i,1] = ((n_vec[i,1]+n_vec[i,2])/(n_vec[i,1]#n_vec[i,2])) +
((di_vec[i,1]##2)/(2#(n_vec[i,1]+n_vec[i,2])));
end;
d_plus = 0;
fesum_wt = 0;
do i = 1 to k;
d_plus = d_plus + di_vec[i,1]/var_di[i,1];
fesum_wt = fesum_wt + var_di[i,1]##-1;
end;
d_plus = d_plus/fesum_wt;
QQ = 0;
do i = 1 to k;
QQ = QQ + ((di_vec[i,1] - d_plus)##2/var_di[i,1]);
end;
prob_qq1 = 1 - PROBCHI(QQ,k-1);
finish;
start calcreg(di_vec,n_vec,X_Matrix,vi,B_wls,SE_B,B_ols,SE_B_ols);
k = nrow(di_vec);
X = J(k,1,1)||X_Matrix;
B_wls = INV(X`*DIAG(vi)*X)*X`*DIAG(vi)*di_vec;
cov_b = INV(X`*DIAG(vi)*X);
SE_B = SQRT(vecdiag(cov_b));
B_ols =INV(X`*X)*X`*di_vec;
cov_b = INV(X`*X);
SE_B_ols = SQRT(vecdiag(cov_b));
finish;
START KENDALL(A,B,N,T_AB,untie_A,untie_B,VART_AB,Z_TEST);
DOM_MTXA = J(N,N,0);
ties_A = 0;
counts_A = 0;
do i = 1 to N;
do j = 1 to N;
if A[i,1] > A[j,1] then do;
DOM_MTXA[i,j] = 1;
end;
if A[i,1] < A[j,1] then do;
DOM_MTXA[i,j] = -1;
end;
if A[i,1] = A[j,1] then do;
ties_A = ties_A + 1;
end;
counts_A = counts_A + 1;
end;
end;
untie_A = 1 - (ties_A - N)/(counts_A - N);
DOM_MTXB = J(N,N,0);
ties_B = 0;
counts_B = 0;
do i = 1 to N;
do j = 1 to N;
if B[i,1] > B[j,1] then do;
DOM_MTXB[i,j] = 1;
end;
if B[i,1] < B[j,1] then do;
DOM_MTXB[i,j] = -1;
end;
if B[i,1] = B[j,1] then do;
ties_B = ties_B + 1;
end;
counts_B = counts_B + 1;
end;
end;
untie_B = 1 - (ties_B - N)/(counts_B - N);
DOM_MTXD = J(N,N,0);
do i = 1 to N;
do j = 1 to N;
DOM_MTXD[i,j] = DOM_MTXA[i,j]#DOM_MTXB[i,j];
end;
end;
MTXD_sum = DOM_MTXD[,+];
T_AB = MTXD_sum[+,] #(1/(n#(n-1)));
MTX_F = J(N,1,0);
do i = 1 to N;
MTX_F[i,1] = (MTXD_sum [i,1]/(n-1));
end;
MTX_G = J(N,1,0);
do i = 1 to N;
MTX_G[i,1] = (MTX_F[i,1] - T_AB)##2;
end;
MTXG_sum = 1/(n-1)#(MTX_G[+,]);
MTX_H = J(N,N,0);
do i = 1 to N;
do j = 1 to N;
MTX_H[i,j] = DOM_MTXD[i,j]#DOM_MTXD[i,j];
end;
end;
MTXH_sum = MTX_H[+,+];
NUMER_AB = MTXH_sum - ((n) # (n-1) # (T_AB#T_AB));
DENOM_AB = n#(n-1) -1;
VART_AB = NUMER_AB/DENOM_AB;
vart_ab = ((4#(n-2)#(MTXG_sum))+(2#VART_AB)) / (n#(n-1));
IF vart_ab > 0 THEN DO ;
Z_TEST = (T_AB /SQRT(vart_ab));
end;
if vart_ab =0 then do;
Z_TEST = 5.00;
END;
FINISH;
run calcq(di_vec,n_vec,qq,prob_qq1,var_di);
CC = (J(1,K,1)*var_di##-1) - ((J(1,K,1)*var_di##-2) /
(J(1,K,1)*var_di##-1));
Tau2 = (QQ - (K - 1)) / cc;
if tau2 < 0 then tau2 = 0;
if &modeltype=0 then tau2=0;
run mean_d(di_vec,var_di,tau2,d_mean,resum_wt,vi_star,d_SE,upper95,lower95);
vi = J(k,1,0);
vi_inv = j(k,1,0);
mean = 0;
weight = 0;
t_star = J(k,1,0);
Vi_stand = J(k,1,0);
dev_di = J(k,1,0);
Root_Vi=J(k,1,0);
Egger_z = J(k,1,0);
REJ_TRIM = J(3,1,0);
do i = 1 to k;
vi[i,1] = vi_star[i,1];
vi_inv[i,1] = vi[i,1]##-1;
mean = d_mean;
weight = resum_wt;
dev_di[i,1] = ABS(di_vec[i,1] - mean);
Vi_stand[i,1] = vi[i,1] - (weight##-1);
t_star[i,1] = (di_vec[i,1] - mean)/SQRT(Vi_stand[i,1]);
Root_Vi[i,1] = vi[i,1]##-0.5;
Egger_z[i,1] = di_vec[i,1]#Root_Vi[i,1];
end;
run calcreg(Egger_z,n_vec,Root_Vi,vi_inv,B_wls,SE_B,B_ols,SE_B_ols);
eggt_ols = B_ols[1,1]/SE_B_ols[1,1];
eggPR_tOLS =2#(1-probt(abs(eggt_ols),k-2));
run KENDALL(t_star,vi,k,T_X1Y,UT_A,UT_B,VART_X1Y,Z_TEST);
BeggV_z = Z_TEST;
BeggVpr_z =2#(1-probnorm(abs(Z_test)));
total_n = n_vec * J(2,1,1);
run KENDALL(t_star,total_n,k,T_X1Y,UT_A,UT_B,VART_X1Y,Z_TEST);
BeggN_z = Z_TEST;
BeggNpr_z =2#(1-probnorm(abs(Z_test)));
run calcreg(di_vec,n_vec,total_n,vi_inv,B_wls,SE_B,B_ols,SE_B_ols);
Funt_WLS = B_wls[2,1]/SE_B[2,1];
FunPR_tWLS =2#(1-probt(abs(Funt_WLS),k-2));
dev_rank = rank(dev_di);
do i = 1 to k;
if di_vec[i,1] < mean then dev_rank[i,1] = -1#dev_rank[i,1];
end;
r=-1#MIN(dev_rank);
gamma=k-r;
ro=gamma-1;
r2=MAX(dev_rank);
gamma2=k-r2;
ro2=gamma2-1;
if ro > 3 then REJ_TRIM[1,1] = REJ_TRIM[1,1] +1;
if ro2 > 3 then REJ_TRIM[2,1] = REJ_TRIM[2,1] +1;
if (ro > 3 | ro2 > 3) then REJ_TRIM[3,1] = REJ_TRIM[3,1] +1;
Right=REJ_TRIM[1,1]; If Right=1 then Right='Yes'; Else Right='No';
Left=REJ_TRIM[2,1];If Left=1 then Left='Yes'; Else Left='No';
Both=REJ_TRIM[3,1];If Both=1 then Both='Yes'; Else Both='No';
file print;
put
@1'-------------------------------------------------------------------------------------'//
@1 'Meta-Analysis: Descriptive Information'/
@1 '--------------------------------------'//
@5'Number of Studies'@60 k//
@5'Mean Effect Size' @60 d_mean 10.4/
@7'Confidence Band'/
@10 '95% Lower Limit' @60lower95 10.4/
@10'95% Upper Limit'@60 upper95 10.4//
@5 'Test for Homogeneity'@60'Chi Square'@75'Probability'/
@5 '--------------------' @60'----------'@75'-----------'/
@5'Q test'@62 QQ 8.4 @75 prob_qq1 10.4//
@5'Random Effects Variance Component'@62 Tau2 8.4///
@1'--------------------------------------------------------------------------------------'//
@1 'Meta-Analysis: Tests of Publication Bias'/
@1 '----------------------------------------'//
@5 'Egger Regression' @60't value'@75'Probability'/
@5 '----------------' @60'-------'@75'-----------'/
@5 'Egger OLS Regression' @60 eggt_OLS 7.4 @75 eggPR_tOLS 10.4///
@5 'Begg Rank Correlation' @60'z value'@75'Probability'/
@5 '---------------------' @60'-------'@75'-----------'/
@5 'Begg Rank Correlation (Predictor=Variance)' @60 beggV_z 7.4 @75 beggVpr_z 10.4/
@5 'Begg Rank Correlation (Predictor=Sample Size)' @60 beggN_z 7.4 @75 beggNpr_z 10.4///
@5 'Funnel Plot Regression' @60't value'@75'Probability'/
@5 '----------------------' @60'-------'@75'-----------'/
@5 'Funnel Plot WLS Regression' @60 Funt_WLS 7.4 @75 FunPR_tWLS 10.4///
@5 'Trim and Fill'@60'Publication Bias Present'/
@5 '-------------'@60'------------------------'/
@7 'Right Tail' @70 Right/
@7 'Left Tail' @70 Left/
@7 'Both Tails' @70 Both/
@1'--------------------------------------------------------------------------------------'//;
sum_n = n_vec*J(2,1,1);
total_n = sum(sum_n);
outvector = d_mean||lower95||upper95||total_n;
create j1 from outvector;
append from outvector;
quit;
data meta2;
set &Dataset;
SE = SQRT((&n1 + &n2) / (&n1 * &n2) + &di**2 / (2*(&n1 + &n2)));
lower95 = &di - 1.96*SE;
upper95 = &di + 1.96*SE;
size = &n1 + &n2;
y = _n_;
data total1;
set j1;
rename col1 = &di col2 = lower95 col3 = upper95 col4 = size;
length &StudyID $ 12;
y = 99;
&StudyID = 'Total';
data total2;
y = 98;
data total;
set total1 total2;
axis1 label=(height=2.9
font='Zapf' 'Effect Size')
minor=none
value=(height=2.5 font='Zapf');
axis2 label=(height=2.9 angle = 90
font='Zapf' 'Sample Size')
minor=none
value=(height=2.5 font='Zapf');
title1 font = 'Zapf' height = 2.9 'Funnel Plot' ;
symbol1 interpol=none
value=circle
height=3
cv=black
ci=black
co=black
width=2;
/*proc gplot data=meta2;*/
/* plot size*&di /haxis=axis1 vaxis=axis2 frame ;*/
/*run;*/
proc means data=meta2 noprint;
var size;
output out=meanout sum=sum max=max;
data _null_;
set meanout end=eof;
if eof then do;
call symput("n_stud",trim(left(put(_freq_,8.))));
call symput("n_subs",trim(left(put(sum,8.))));
call symput("maxsize",trim(left(put(max,8.))));
end;
proc sort data=meta2; by &di upper95;
run;
data metadat;
set total(in=a) meta2(in=b);
length metastr $ 200;
retain metastr studcnt;
metanum=y;
y=_n_;
if _n_=1 then do;
metastr="0=' ' %eval(&n_stud+3)=' '";
studcnt=&n_stud+2;
end;
else studcnt=studcnt-1;
do i=1 to %eval(&n_stud+2);
if i=studcnt then metastr=" " || trim(left(metastr)) || ' ' || trim(left(y))
||"='" || trim(left(&StudyID)) || "' ";
end;
if &StudyID=' ' then y=.;
yo=lower95; xo=upper95;
output metadat;
if _n_=%eval(&n_stud+2) then do;
call symput("metastr",trim(metastr));
end;
run;
proc format;
value stfmt &metastr;
run;
data alldata;
set metadat;
%doall (1)
%symsize
%syms
data final(drop=i);
set alldata;
array xarray{*} x1-x%eval(&n_stud+2);
array yarray{*} y1-y%eval(&n_stud+2);
do i=1 to (&n_stud)+2;
if i=y then do;
xarray{i}=&di;
yarray{i}=y;
end;
end;
format yy1-yy%eval(&n_stud+2) stfmt.;
run;
axis1 label=(height=2.9
font='Zapf' 'Effect Size')
minor=none
value=(height=2.5 font='Zapf');
axis2 label=(height=2.9 angle = 90
font='Zapf' 'Study')
minor=none
order=( 0 to %eval(&n_stud+3) by 1)
value=(height=2.5 font='Zapf');
/*title1 font = 'Zapf' height = 2.9 'Forest Plot' ;
proc gplot data=final;
plot
%plotpts / overlay haxis=axis1 vaxis=axis2 frame href=0;
symbol%eval(&n_stud+3) f=marker v=none l=1 w=1 i=join;
symbol1 font=marker v=P l=1 h=2.7 interpol=none;
run;*/
title;
quit;
%Mend PubBias;
%macro ForestMacro (
Data=, /*--Data Set Name (Required)--*/
Study=, /*--Variable name for Study (Required)--*/
OddsRatio=, /*--Variable name for Odds Ratio (Required)--*/
LCL=, /*--Variable name for Lower Confidence Limit (Required)--*/
UCL=, /*--Variable name for Upper Confidence Limit (Required)--*/
Group=GroupId, /*--Variable name for Study Type--*/
Weight=, /*--Variable name for Study Weight in %--*/
StatCol1=, /*--Variable name for Stat Column 1--*/
StatCol2=, /*--Variable name for Stat Column 2--*/
StatCol3=, /*--Variable name for Stat Column 3--*/
StatCol4=, /*--Variable name for Stat Column 4--*/
DisplayCols=YES, /*--Display the columns for OR, LCL, UCL & Weight--*/
WtFactor=, /*--Multiplier factor for Study Weights--*/
/*--If not provided WtFactor is computed internally--*/
Bands=YES, /*--Draw Horizontal Alternating Bands--*/
Borders=NO, /*--Draw Borders--*/
GraphWalls=NO, /*--Draw Filled Walls behind the Graph--*/
StatWalls=NO, /*--Draw Filled Walls behind the Statistics Tables--*/
Width=6.4in, /*--Default width of the graph in pixels--*/
Height=, /*--Default height of the graph is computed based on number of observations--*/
LabelColWidth=0.2, /*--Fractional width for Label Column--*/
Label1=Favors Treatment, /*--Favorable Label--*/
Label2=Favors Control, /*--Unfavorable Label--*/
PlotTitle=Odds Ratio and 95% CL, /*--Plot Title--*/
FootNote=, /*--Graph Footnote--*/
Title2=, /*--Graph title2--*/
Title=Impact of Treatment on Mortality by Study /*--Graph Title--*/
);
%local WeightVar MarkerSize GraphColWidth StatColWidth Border DisplaySecondary GraphWallDisplay StatWallDisplay;
%local OddsLabel LowerLabel UpperLabel WeightLabel SLabel1 SLabel2 SLabel3 SLabel4;
%local GraphHeight Ratio RowHeight HeaderHeight Nobs;
/*--Data, Study, OddsRatio, LCL and UCL are required --*/
/*--Group is optional --*/
/*--Terminatethese required parameters are not supplied--*/
%if %length(&Data) eq 0 %then %do;
%put The parameter 'Data' is required - Forest Macro Terminated.;
%goto finished;
%end;
%else %if %length(&Study) eq 0 %then %do;
%put The parameter 'Study' is required - Forest Macro Terminated.;
%goto finished;
%end;
%else %if %length(&LCL) eq 0 %then %do;
%put The parameter 'LCL' is required - Forest Macro Terminated.;
%goto finished;
%end;
%else %if %length(&UCL) eq 0 %then %do;
%put The parameter 'UCL' is required - Forest Macro Terminated.;
%goto finished;
%end;
%else %if %length(&OddsRatio) eq 0 %then %do;
%put The parameter 'OddsRatio' is required - Forest Macro Terminated.;
%goto finished;
%end;
/*--Initialize GraphHeight, Height per row and Height for other graph items--*/
%let GraphHeight=&Height;
%let RowHeight=22;
%let HeaderHeight=100;
%if %length(&Footnote) ne 0 %then %do;
%let HeaderHeight=115;
%end;
/*--If the Weight column is not provided, use equal weights, and suppress display of Weight stat--*/
%if &Weight eq %then %do;
%let WeightVar = _Weight;
%let MarkerSize = 7;
%end;
%else %do;
%let WeightVar=&Weight;
%let MarkerSize = 0;
%end;
/*--Set up GTL options for borders--*/
%let DisplaySecondary = displaysecondary=none;
%let Borders=%upcase(&Borders);
%if &Borders eq YES or &Borders eq Y %then %do;
%let Border = line;
%let DisplaySecondary = displaysecondary=(line);
%end;
/*--Set up GTL options for GraphWall Display--*/
%let GraphWallDisplay = walldisplay=none;
%let GraphWalls=%upcase(&GraphWalls);
%if &GraphWalls eq YES or &GraphWalls eq Y %then %do;
%let GraphWallDisplay = walldisplay=(fill);
%end;
/*--Set up GTL options for StatWall Display--*/
%let StatWallDisplay = walldisplay=none;
%let StatWalls=%upcase(&StatWalls);
%if &StatWalls eq YES or &StatWalls eq Y %then %do;
%let StatWallDisplay = walldisplay=(fill);
%end;
/*--Create Label Columns for standard and additional columns--*/
/*--Load Stat Column Label or name into macro for label column value--*/
%let dsid=%sysfunc(open(&Data));
%if &dsid %then %do;
%let Nobs=%sysfunc(attrn(&dsid, nlobs));
%if &Nobs eq 0 %then %do;
%put The Data Set &Data has no observations - Forest Macro Terminated.;
%let rc=%sysfunc(close(&dsid));
%goto finished;
%end;
%if &Nobs gt 150 %then %do;
%put The Data Set &Data has over 100 observations - Forest Macro Terminated.;
%let rc=%sysfunc(close(&dsid));
%goto finished;
%end;
/*--Count the number of stat columns--*/
%let idx=0;
/*--Column display information for the OddsRatio column--*/
%let DisplayCols=%upcase(&DisplayCols);
%if &DisplayCols eq YES or &DisplayCols eq Y %then %do;
%let OddsLabel=%sysfunc(varlabel(&dsid, %sysfunc(varnum(&dsid,&OddsRatio))));
%if %length(&OddsLabel) eq 0 %then %let OddsLabel=&OddsRatio;
%let idx= %eval(&idx+1);
%let LowerLabel=%sysfunc(varlabel(&dsid, %sysfunc(varnum(&dsid,&LCL))));
%if %length(&LowerLabel) eq 0 %then %let LowerLabel=&LCL;
%let idx= %eval(&idx+1);
%let UpperLabel=%sysfunc(varlabel(&dsid, %sysfunc(varnum(&dsid,&UCL))));
%if %length(&UpperLabel) eq 0 %then %let UpperLabel=&UCL;
%let idx= %eval(&idx+1);
%if &Weight ne %then %do;
%let WeightLabel=%sysfunc(varlabel(&dsid, %sysfunc(varnum(&dsid,&Weight))));
%if %length(&WeightLabel) eq 0 %then %let WeightLabel=&Weight;
%let idx= %eval(&idx+1);
%end;
%end;
/*--Additional columns to be displayed--*/
%if %length(&StatCol1) ne 0 %then %do;
%let SLabel1=%sysfunc(varlabel(&dsid, %sysfunc(varnum(&dsid,&StatCol1))));
%if %length(&SLabel1) eq 0 %then %let SLabel1=&StatCol1;
%let idx= %eval(&idx+1);
%end;
%if %length(&StatCol2) ne 0 %then %do;
%let SLabel2=%sysfunc(varlabel(&dsid, %sysfunc(varnum(&dsid,&StatCol2))));
%if %length(&SLabel2) eq 0 %then %let SLabel2=&StatCol2;
%let idx= %eval(&idx+1);
%end;
%if %length(&StatCol3) ne 0 %then %do;
%let SLabel3=%sysfunc(varlabel(&dsid, %sysfunc(varnum(&dsid,&StatCol3))));
%if %length(&SLabel3) eq 0 %then %let SLabel3=&StatCol3;
%let idx= %eval(&idx+1);
%end;
%if %length(&StatCol4) ne 0 %then %do;
%let SLabel4=%sysfunc(varlabel(&dsid, %sysfunc(varnum(&dsid,&StatCol4))));
%if %length(&SLabel4) eq 0 %then %let SLabel4=&StatCol4;
%let idx= %eval(&idx+1);
%end;
%let rc=%sysfunc(close(&dsid));
/*--Set column weights based on number of stat columns--*/
%let StatColWidth=%sysevalf(&idx * 0.075);
%let GraphColWidth= %sysevalf(1.0 - &LabelColWidth - &StatColWidth);
%end;
%else %do;
%put The data set &Data does not exist - Forest Macro Terminated.;
%goto finished;
%end;
/*--Compute Weight Factor if not provided --*/
/*--Estimate height of graph if not provided--*/
data _null_;
set &Data end=last;
retain totalweight 0;
totalweight+wt;
if last then do;
%if &wtFactor eq %then %do;
if totalweight <= 0 then totalweight=1;
call symput ('wtFactor', 1 / totalweight);
%end;
/*--Estimate Ratio of Plot height by Graph Height--*/
call symput ('Ratio', (_N_* &RowHeight)/(_N_* &RowHeight + &HeaderHeight));
/*--Estimate the optimal height of the graph based on obs count--*/
%if &Height eq %then %do;
call symput ('GraphHeight', _N_ * &RowHeight + &HeaderHeight);
%end;
end;
run;
/*--Append a PX only if this internally estimated--*/
%if &Height eq %then %do;
%let GraphHeight=&GraphHeight.px;
%end;
/*--Process Data--.3*/
data _forest;
set &Data;
format _wt ;
_ObsId=_N_;
%if &Weight eq %then %do;
&WeightVar=0;
%end;
label _wt=&WeightLabel;
/*--If Group column is provided--*/
%if %length(&Group) ne 0 %then %do;
/*--Group=1 (Study) values will be drawn without a group role--*/
if &group=1 then do;
_wt=&WeightVar / 100;
_grp=10;
_1 = &OddsRatio;
_lcl1=&LCL;
_ucl1=&UCL;
/*--Compute marker width--*/
_x1=&OddsRatio / (10 ** (&WeightVar*&WtFactor/2));
_x2=&OddsRatio * (10 ** (&WeightVar*&WtFactor/2));
end;
/*--Group=2 & 3 (SubGroup and Overall) values will be drawn with groupindex=2 & 3--*/
else if &group > 1 then do;
_grp=&group;
_2 = &OddsRatio;
end;
%end;
%else %do;
_wt=&WeightVar / 100;
_grp=10;
_1 = &OddsRatio;
_lcl1=&LCL;
_ucl1=&UCL;
/*--Compute marker width--*/
_x1=&OddsRatio / (10 ** (&WeightVar*&WtFactor/2));
_x2=&OddsRatio * (10 ** (&WeightVar*&WtFactor/2));
%end;
/*--Create label columns for standard and additional statistic--*/
%if %length(&OddsRatio) ne 0 %then %do;
_OddsRatioLabel = symget('OddsLabel');
%end;
%if %length(&LCL) ne 0 %then %do;
_LowerLabel = symget('LowerLabel');
%end;
%if %length(&UCL) ne 0 %then %do;
_UpperLabel = symget('UpperLabel');
%end;
%if %length(&Weight) ne 0 %then %do;
_WeightLabel = symget('WeightLabel');
%end;
%if %length(&StatCol1) ne 0 %then %do;
_StatColLabel1 = symget('SLabel1');
_StatCol1 = &StatCol1;
%end;
%if %length(&StatCol2) ne 0 %then %do;
_StatColLabel2 = symget('SLabel2');
_StatCol2 = &StatCol2;
%end;
%if %length(&StatCol3) ne 0 %then %do;
_StatColLabel3 = symget('SLabel3');
_StatCol3 = &StatCol3;
%end;
%if %length(&StatCol4) ne 0 %then %do;
_StatColLabel4 = symget('SLabel4');
_StatCol4 = &StatCol4;
%end;
run;
/*--Reverse the order to avoid putting axis reverse--*/
proc sort data=_forest out=_forest;
by descending _ObsId;
run;
/*--Add sequence numbers to each observation--*/
data _forest;
set _forest;
studyvalue=_n_;
run;
/*--Output values and formatted strings to data set--*/
data _forestFormat;
set _forest end=last;
keep label start end fmtname type hlo;
retain fmtname '_Study' type 'n';
label=&Study;
start=studyvalue;
end=studyvalue;
output;
if last then do;
hlo='O';
label='Other';
output;
end;
run;
/*--Create Format from data set--*/
proc format library=work cntlin=_forestFormat;
run;
/*--Apply format to study values--*/
/*--Compute width of box proportional to weight in log scale--*/
data _forest;
format studyvalue _study.;
set _forest;
%let Bands=%upcase(&Bands);
%if &Bands eq YES or &Bands eq Y %then %do;
if mod(studyvalue, 2) = 0 then _StudyRef=StudyValue;
%end;
run;
/*--Compute top and bottom offsets--*/
data _null_;
pct=&Ratio/nobs;
thk=pct* 0.9 *100;
call symputx("pct", pct);
call symputx("pct2", 2*pct);
call symputx("RefThickness", thk);
call symputx("count", nobs);
set _forest nobs=nobs;
run;
/*title;*/
/*options nodate nonumber;*/
/*--Define GTL template for graph--*/
proc template;
define statgraph ForestMacro;
begingraph / designwidth=&Width designheight=&GraphHeight;
entrytitle "&Title";
entryfootnote halign=left "&FootNote";
%if %length(&title2) ne 0 %then %do;
entrytitle "&title2" / textattrs=graphLabelText;
%end;
layout lattice / columns=3 columnweights=(&LabelColWidth &GraphColWidth &StatColWidth) columngutter=0
rowdatarange=union;
/*--Column # 1 contains the Study Labels using Secondary Y axis--*/
layout overlay / walldisplay=none x2axisopts=(display=none)
yaxisopts=(linearopts=(tickvaluesequence=(start=1 end=&count increment=1))
offsetmin=&pct2 offsetmax=&pct display=none
displaysecondary=(tickvalues &border));
scatterplot y=studyvalue x=_1 / yaxis=Y xaxis=X2 markerattrs=(size=0) includemissinggroup=true;
scatterplot y=studyvalue x=_1 / yaxis=Y xaxis=X2 markerattrs=(size=0) includemissinggroup=true;
endlayout;
/*--Column # 2 contains the graph--*/
layout overlay / &GraphWallDisplay border=false
yaxisopts=(linearopts=(tickvaluesequence=(start=1 end=&count increment=1))
offsetmin=&pct2 offsetmax=&pct display=none);
/*--Draw alternating bands using referenceline--*/
%if &Bands eq YES or &Bands eq Y %then %do;
referenceline y=_StudyRef / lineattrs=(thickness=&RefThickness.PCT) datatransparency=0.9;
%end;
/*--Draw Markers for SubGroup and Overall values--*/
%if %length(&Group) ne 0 %then %do;
scatterplot y=studyvalue x=_2 / markerattrs=(symbol=diamondfilled size=10) group=_grp
includemissinggroup=true index=_grp;
%end;
/*--Draw OddsRatio and Limits for Study Values--*/
scatterplot y=studyvalue x=_1 / xerrorupper=_ucl1 xerrorlower=_lcl1
markerattrs=graphdata1(symbol=squarefilled size=&MarkerSize);
/*--Draw box representing the weight of the study--*/
vectorplot y=studyvalue x=_x2 xorigin=_x1 yorigin=studyvalue / lineattrs=GraphData1(thickness=8)
arrowheads=false;
/*--Draw Reference lines and labels--*/
referenceline x=0;
entry halign=left "&Label1" / valign=bottom;
entry halign=right "&Label2" / valign=bottom;
endlayout;
/*--Column # 2 contains the statistics data--*/
layout overlay / &StatWallDisplay border=false
x2axisopts=(display=(tickvalues &border) displaysecondary=(line))
yaxisopts=(linearopts=(tickvaluesequence=(start=1 end=&count increment=1))
offsetmin=&pct2 offsetmax=&pct
display=none &DisplaySecondary.);
/*--Draw alternating bands using referenceline--*/
%if &Bands eq YES %then %do;
referenceline y=_StudyRef / lineattrs=(thickness=&RefThickness.PCT) datatransparency=0.9;
%end;
/*--Draw standard statistics columns--*/
%if &DisplayCols eq YES or &DisplayCols eq Y %then %do;
scatterplot y=studyvalue x=_OddsRatioLabel / markercharacter=&OddsRatio xaxis=x2;
scatterplot y=studyvalue x=_LowerLabel / markercharacter=&LCL xaxis=x2;
scatterplot y=studyvalue x=_UpperLabel / markercharacter=&UCL xaxis=x2;
%if &Weight ne %then %do;
scatterplot y=studyvalue x=_WeightLabel / markercharacter=_wt xaxis=x2;
%end;
%end;
/*--Draw additional statistics columns--*/
%if %length(&StatCol1) ne 0 %then %do;
scatterplot y=studyvalue x=_StatColLabel1 / markercharacter=&StatCol1 xaxis=x2;
%end;
%if %length(&StatCol2) ne 0 %then %do;
scatterplot y=studyvalue x=_StatColLabel2 / markercharacter=&StatCol2 xaxis=x2;
%end;
%if %length(&StatCol3) ne 0 %then %do;
scatterplot y=studyvalue x=_StatColLabel3 / markercharacter=&StatCol3 xaxis=x2;
%end;
%if %length(&StatCol4) ne 0 %then %do;
scatterplot y=studyvalue x=_StatColLabel4 / markercharacter=&StatCol4 xaxis=x2;
%end;
endlayout;
endlayout;
endgraph;
end;
run;
proc sgrender data=_forest template=ForestMacro description='Forest Plot';
run;
%finished:
title;
FootNote;
%mend ForestMacro;