You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2508 lines
80 KiB
Plaintext
2508 lines
80 KiB
Plaintext
8 months ago
|
*! version 1.86 1Apr2004
|
||
|
|
||
|
/*Revision list at end
|
||
|
|
||
|
Syntax:
|
||
|
a) binary data:
|
||
|
metan #events_group1 #nonevents_group1 #events_group2 #nonevents_group2 , ...
|
||
|
b) cts data:
|
||
|
metan #group1 mean1 sd1 #group2 mean2 sd2 , ...
|
||
|
c) generic effect size+st error:
|
||
|
metan theta se_theta , ...
|
||
|
d) generic effect size+ci:
|
||
|
metan theta lowerlimit upperlimit , ...
|
||
|
|
||
|
*/
|
||
|
|
||
|
program define metan7 , rclass
|
||
|
version 7.0
|
||
|
#delimit ;
|
||
|
syntax varlist(min=2 max=6 default=none numeric) [if] [in] [, BY(string)
|
||
|
ILevel(integer $S_level) OLevel(integer $S_level) CC(string)
|
||
|
OR RR RD FIXED FIXEDI RANDOM RANDOMI PETO COHEN HEDGES GLASS noSTANDARD
|
||
|
CHI2 CORNFIELD LOG BRESLOW EFORM noINTeger noOVERALL noSUBGROUP SGWEIGHT
|
||
|
SORTBY(passthru) noKEEP noGRAPH noTABLE LABEL(string) SAVING(passthru) noBOX
|
||
|
XLAbel(passthru) XTick(passthru) FORCE BOXSCA(real 1.0) BOXSHA(integer 4)
|
||
|
TEXTS(real 1.0) T1title(string) T2title(string) LEGEND(string)
|
||
|
B1title(string) B2title(string) noWT noSTATS COUNTS WGT(varlist numeric max=1)
|
||
|
GROUP1(string) GROUP2(string) EFFECT(string) ] ;
|
||
|
#delimit cr
|
||
|
|
||
|
|
||
|
global MA_FBSH=`boxsha'
|
||
|
global MA_FBSC=`boxsca'
|
||
|
global MA_ESLA="`effect'"
|
||
|
if "`legend'"!="" { global S_TX="`legend'" }
|
||
|
else { global S_TX "Study" }
|
||
|
|
||
|
*label groups
|
||
|
if "`group1'"=="" { global MA_G1L "Treatment" }
|
||
|
else { global MA_G1L "`group1'" }
|
||
|
if "`group2'"=="" { global MA_G2L "Control" }
|
||
|
else { global MA_G2L "`group2'" }
|
||
|
if "`legend'"!="" { global S_TX "`legend'" }
|
||
|
|
||
|
if (`texts'>2 | `texts'<0.1 ) {local texts=1}
|
||
|
global MA_FTSI=`texts'
|
||
|
if ("`by'"=="" & "`overall'"!="") {local wt "nowt"}
|
||
|
if `ilevel'<1 {local ilevel=`ilevel'*100 }
|
||
|
if `ilevel'>99 | `ilevel'<10 { local ilevel $S_level }
|
||
|
global ZIND= -invnorm((100-`ilevel')/200)
|
||
|
if `olevel'<1 {local olevel=`olevel'*100 }
|
||
|
if `olevel'>99 | `olevel'<10 { local olevel $S_level }
|
||
|
global ZOVE= -invnorm((100-`olevel')/200)
|
||
|
global IND=`ilevel'
|
||
|
global OVE=`olevel'
|
||
|
#delimit ;
|
||
|
global S_1 "."; global S_2 "."; global S_3 "."; global S_4 ".";
|
||
|
global S_5 "."; global S_6 "."; global S_7 "."; global S_8 ".";
|
||
|
global S_9 "."; global S_10 ".";global S_11 ".";global S_12 ".";
|
||
|
#delimit cr
|
||
|
|
||
|
*If not using own weights set fixed as default
|
||
|
if "`fixed'`random'`fixedi'`randomi'`peto'"=="" & ( "`wgt'"=="" ) { local fixed "fixed" }
|
||
|
*declare study labels for display
|
||
|
if "`label'"!="" {
|
||
|
parse "`label'", parse("=,")
|
||
|
while "`1'"!="" {
|
||
|
cap confirm var `3'
|
||
|
if _rc!=0 {
|
||
|
di in re "Variable `3' not defined"
|
||
|
exit _rc
|
||
|
}
|
||
|
local `1' "`3'"
|
||
|
mac shift 4
|
||
|
}
|
||
|
}
|
||
|
tempvar code
|
||
|
qui {
|
||
|
*put name/year variables into appropriate macros
|
||
|
if "`namevar'"!="" {
|
||
|
local lbnvl : value label `namevar'
|
||
|
if "`lbnvl'"!="" { quietly decode `namevar', gen(`code') }
|
||
|
else {
|
||
|
gen str10 `code'=""
|
||
|
cap confirm string variable `namevar'
|
||
|
if _rc==0 { replace `code'=`namevar' }
|
||
|
else if _rc==7 { replace `code'=string(`namevar') }
|
||
|
}
|
||
|
}
|
||
|
else { gen str3 `code'=string(_n) }
|
||
|
if "`yearvar'"!="" {
|
||
|
local yearvar "`yearvar'"
|
||
|
cap confirm string variable `yearvar'
|
||
|
if _rc==7 { local str "string" }
|
||
|
if "`namevar'"=="" { replace `code'=`str'(`yearvar') }
|
||
|
else { replace `code'=`code'+" ("+`str'(`yearvar')+")" }
|
||
|
}
|
||
|
if "`wgt'"!="" {
|
||
|
*User defined weights verification
|
||
|
if "`fixed'`random'`fixedi'`randomi'`peto'"!="" {
|
||
|
di in re "Option invalid with user-defined weights"
|
||
|
exit _rc
|
||
|
}
|
||
|
confirm numeric variable `wgt'
|
||
|
local wgt "wgt(`wgt')"
|
||
|
}
|
||
|
} /* End of quietly loop */
|
||
|
parse "`varlist'", parse(" ")
|
||
|
if "`6'"=="" {
|
||
|
if "`4'"=="" {
|
||
|
*Input is {theta setheta} or {theta lowerci upperci} => UDW, IV or D+L weighting
|
||
|
if "`3'"!="" {
|
||
|
*input is theta lci uci
|
||
|
cap assert ((`3'>=`1') & (`1'>=`2'))
|
||
|
if _rc!=0 {
|
||
|
di in bl "Effect size and confidence intervals invalid:"
|
||
|
di in bl "order should be {effect size, lower ci limit, upper ci limit}"
|
||
|
exit _rc
|
||
|
}
|
||
|
}
|
||
|
cap assert "`log'"==""
|
||
|
if _rc!=0 {
|
||
|
di in bl "Log option not available without raw data counts: if necessary, transform both"
|
||
|
di in bl "effect and standard error using " in wh "generate" in bl " and re-issue the metan command"
|
||
|
exit _rc
|
||
|
}
|
||
|
|
||
|
cap assert "`chi2'`cornfield'`peto'`breslow'`counts'`or'`rr'`rd'`standard'`hedges'`glass'`cohen'"==""
|
||
|
if _rc!=0 {
|
||
|
di in re "Option not available without raw data counts"
|
||
|
exit _rc
|
||
|
}
|
||
|
if "`wgt'"!="" { local method "*" }
|
||
|
else {
|
||
|
if "`random'`randomi'"!="" {
|
||
|
local randomi
|
||
|
local random "random"
|
||
|
local method "D+L"
|
||
|
}
|
||
|
if "`fixed'`fixedi'"!="" {
|
||
|
local fixedi
|
||
|
local fixed "fixed"
|
||
|
local method "I-V"
|
||
|
}
|
||
|
cap assert ("`random'"=="") + ("`fixed'"=="")==1
|
||
|
if _rc!=0 {
|
||
|
di in re "Specify fixed or random effect/s model"
|
||
|
exit _rc
|
||
|
}
|
||
|
}
|
||
|
cap assert "`cc'"==""
|
||
|
if _rc!=0 {
|
||
|
di in re "Continuity correction not valid with unless individual counts specified"
|
||
|
exit _rc
|
||
|
}
|
||
|
local callalg "iv_init"
|
||
|
local sumstat "ES"
|
||
|
} /*end of 2-variable set-up */
|
||
|
if "`4'"!="" {
|
||
|
*Input is 2x2 tables: MH, Peto, IV, D+L or user defined weighting allowed
|
||
|
cap assert "`5'"==""
|
||
|
if _rc!=0 {
|
||
|
di in re "Wrong number of variables specified"
|
||
|
exit _rc
|
||
|
}
|
||
|
if "`integer'"=="" {
|
||
|
cap {
|
||
|
assert int(`1')==`1'
|
||
|
assert int(`2')==`2'
|
||
|
assert int(`3')==`3'
|
||
|
assert int(`4')==`4'
|
||
|
}
|
||
|
if _rc!=0 {
|
||
|
di in re "Non integer cell counts found"
|
||
|
exit _rc
|
||
|
}
|
||
|
|
||
|
}
|
||
|
cap assert ( (`1'>=0) & (`2'>=0) & (`3'>=0) & (`4'>=0) )
|
||
|
if _rc!=0 {
|
||
|
di in re "Non-positive cell counts found"
|
||
|
exit _rc
|
||
|
}
|
||
|
if "`cc'"!="" {
|
||
|
*Ensure Continuity correction is valid
|
||
|
if "`peto'"!="" {
|
||
|
di in re "Peto method not valid with continuity correction"
|
||
|
exit
|
||
|
}
|
||
|
*Currently, allows user to define own constant [0,1) to add to all cells
|
||
|
cap confirm number `cc'
|
||
|
if _rc!=0 {
|
||
|
di in re "Invalid continuity correction: specify a constant number eg metan ... , cc(0.166667)"
|
||
|
exit
|
||
|
}
|
||
|
cap assert (`cc'>=0) & (`cc'<1)
|
||
|
if _rc!=0 {
|
||
|
di in re "Invalid continuity correction: must be in range [0,1)"
|
||
|
exit
|
||
|
}
|
||
|
}
|
||
|
else { local cc "0.5" }
|
||
|
if "`peto'"=="" { local cont "cc(`cc')" }
|
||
|
if "`peto'"!="" { local or "or" }
|
||
|
capture {
|
||
|
assert ( ("`or'"!="")+("`rr'"!="")+("`rd'"!="") <=1 )
|
||
|
assert ("`fixed'"!="")+("`fixedi'"!="")+("`random'"!="")+ /*
|
||
|
*/ ("`randomi'"!="")+("`peto'"!="")+("`wgt'"!="") <=1
|
||
|
assert "`standard'`hedges'`glass'`cohen'"==""
|
||
|
}
|
||
|
if _rc!=0 {
|
||
|
di in re "Invalid specifications for combining trials"
|
||
|
exit 198
|
||
|
}
|
||
|
*Default is set at pooling RRs.
|
||
|
if "`or'"!="" {local sumstat "OR" }
|
||
|
else if "`rd'"!="" {local sumstat "RD" }
|
||
|
else {local sumstat "RR" }
|
||
|
if "`wgt'"!="" { local method "*" }
|
||
|
else if "`random'`randomi'"!="" {local method "D+L" }
|
||
|
else if "`peto'"!="" {local method "Peto"}
|
||
|
else if "`fixedi'"!="" {local method "I-V"}
|
||
|
else {local method "M-H" }
|
||
|
if "`peto'"!="" {local callalg "Peto"}
|
||
|
else {local callalg "`sumstat'"}
|
||
|
if ("`sumstat'"!="OR" | "`method'"=="D+L") & "`chi2'"!="" {
|
||
|
di in re "Chi-squared option invalid for `method' `sumstat'"
|
||
|
exit
|
||
|
}
|
||
|
if ("`sumstat'"!="OR" | "`method'"=="D+L" | "`method'"=="Peto" ) & "`breslow'"!="" {
|
||
|
di in re "Breslow-Day heterogeneity option not available for `method' `sumstat'"
|
||
|
exit
|
||
|
}
|
||
|
if ("`sumstat'"!="OR" & "`sumstat'"!="RR") & "`log'"!="" {
|
||
|
di in re "Log option not appropriate for `sumstat'"
|
||
|
exit
|
||
|
}
|
||
|
if "`keep'"=="" {
|
||
|
cap drop _SS
|
||
|
qui gen _SS =`1'+`2'+`3'+`4'
|
||
|
}
|
||
|
} /* end of binary variable setup */
|
||
|
}
|
||
|
if "`6'"!="" {
|
||
|
*Input is form N mean SD for continuous data: IV, D+L or user defined weighting allowed
|
||
|
cap assert "`7'"==""
|
||
|
if _rc!=0 {
|
||
|
di in re "Wrong number of variables specified"
|
||
|
exit _rc
|
||
|
}
|
||
|
if "`integer'"=="" {
|
||
|
cap assert ((int(`1')==`1') & (int(`4')==`4'))
|
||
|
if _rc!=0 {
|
||
|
di in re "Non integer sample sizes found"
|
||
|
exit _rc
|
||
|
}
|
||
|
}
|
||
|
cap assert (`1'>0 & `4'>0)
|
||
|
if _rc!=0 {
|
||
|
di in re "Non positive sample sizes found"
|
||
|
exit _rc
|
||
|
}
|
||
|
if "`random'`randomi'"!="" {
|
||
|
local randomi
|
||
|
local random "random"
|
||
|
}
|
||
|
if "`fixed'`fixedi'"!="" {
|
||
|
local fixedi
|
||
|
local fixed "fixed"
|
||
|
}
|
||
|
cap{
|
||
|
assert ("`hedges'"!="")+ ("`glass'"!="")+ ("`cohen'"!="")+ ("`standard'"!="")<=1
|
||
|
assert ("`random'"!="")+ ("`fixed'"!="") <=1
|
||
|
assert "`or'`rr'`rd'`peto'`log'`cornfield'`chi2'`breslow'`eform'"==""
|
||
|
}
|
||
|
if _rc!=0 {
|
||
|
di in re "Invalid specifications for combining trials"
|
||
|
exit 198
|
||
|
}
|
||
|
if "`standard'"!="" {
|
||
|
local sumstat "WMD"
|
||
|
local stand "none"
|
||
|
}
|
||
|
else {
|
||
|
if "`hedges'"!="" {local stand "hedges"}
|
||
|
else if "`glass'"!="" {local stand "glass" }
|
||
|
else {local stand "cohen"}
|
||
|
local sumstat "SMD"
|
||
|
}
|
||
|
local stand "standard(`stand')"
|
||
|
if "`wgt'"!="" { local method "*" }
|
||
|
else if "`random'"!="" { local method "D+L" }
|
||
|
else { local method "I-V" }
|
||
|
if "`counts'"!="" {
|
||
|
di in bl "Data option counts not available with continuous data"
|
||
|
local counts
|
||
|
}
|
||
|
if "`cc'"!="" {
|
||
|
di in re "Continuity correction not available with continuous data"
|
||
|
exit
|
||
|
}
|
||
|
local callalg "MD"
|
||
|
if "`keep'"=="" {
|
||
|
cap drop _SS
|
||
|
qui gen _SS =`1'+`4'
|
||
|
}
|
||
|
} /*end of 6-var set-up*/
|
||
|
if "`by'"!="" {
|
||
|
cap confirm var `by'
|
||
|
if _rc!=0 {
|
||
|
di in red "Variable `by' does not exist"
|
||
|
exit _rc
|
||
|
}
|
||
|
local by "by(`by')"
|
||
|
local nextcall "nextcall(`callalg')"
|
||
|
local callalg "metanby"
|
||
|
local sstat "sumstat(`sumstat')"
|
||
|
}
|
||
|
|
||
|
`callalg' `varlist' `if' `in', `by' label(`code') `keep' `table' `graph' /*
|
||
|
*/ method(`method') `randomi' `cont' `stand' `chi2' `cornfield' /*
|
||
|
*/ `log' `breslow' `eform' `wgt' `overall' `subgroup' `sgweight' /*
|
||
|
*/ `sortby' `saving' `xlabel' `xtick' `force' `wt' `stats' `counts' `box' /*
|
||
|
*/ t1(".`t1title'") t2(".`t2title'") b1(".`b1title'") b2(".`b2title'") /*
|
||
|
*/ `groupla' `nextcall' `sstat'
|
||
|
|
||
|
if $S_8<0 {
|
||
|
di in re "Insufficient data to perform this meta-analysis"
|
||
|
}
|
||
|
|
||
|
/*Saved results: to keep compatible with v5 have retained $S_. macros.
|
||
|
Now assign also to r() macros
|
||
|
|
||
|
The macro names and descriptions in the prev version are
|
||
|
|
||
|
Name v7.0 v5.0
|
||
|
pooled ES r(ES) $S_1*
|
||
|
se(ES) - not if RR or OR r(seES) $S_2
|
||
|
se(logES) - only if RR/OR, non-logged
|
||
|
r(selogES) $S_2
|
||
|
lower CI of pooled ES r(ci_low) $S_3
|
||
|
upper CI of pooled ES r(ci_upp) $S_4
|
||
|
Z-value for ES r(z) $S_5
|
||
|
p(Z) r(p_z) $S_6
|
||
|
chi2 heterogeneity r(het) $S_7
|
||
|
df (#studies-1) r(df) $S_8
|
||
|
p(chi2 heterogeneity) r(p_het) $S_9
|
||
|
Chi2 for ES (OR only) r(chi2) $S_10
|
||
|
p(chi2) (OR only) r(p_chi2) $S_11
|
||
|
Estimated tau^2(D&L only) r(tau2) $S_12
|
||
|
Effect measure (RR,SMD..) r(measure) -
|
||
|
*unless log option used..
|
||
|
Overall event rate [binary data; group 1)
|
||
|
r(tger) -
|
||
|
Overall event rate [binary data; group 2)
|
||
|
r(cger) -
|
||
|
*/
|
||
|
|
||
|
|
||
|
*return log or eform as appropriate for OR/RR
|
||
|
return scalar ES=$S_1
|
||
|
if ("`sumstat'"=="OR" | "`sumstat'"=="RR") & ("`log'"=="") { return scalar selogES=$S_2 }
|
||
|
else if ("`sumstat'"=="ES" & "`eform'"!="") { return scalar selogES=$S_2 }
|
||
|
else { return scalar seES=$S_2 }
|
||
|
return scalar ci_low=$S_3
|
||
|
return scalar ci_upp=$S_4
|
||
|
return scalar z=$S_5
|
||
|
return scalar p_z=$S_6
|
||
|
return scalar het=$S_7
|
||
|
return scalar df=$S_8
|
||
|
return scalar p_het=$S_9
|
||
|
return scalar chi2=$S_10
|
||
|
return scalar p_chi2=$S_11
|
||
|
return scalar tau2=$S_12
|
||
|
return local measure "`log'`sumstat'"
|
||
|
if ("`sumstat'"=="RR" | "`sumstat'"=="OR" | "`sumstat'"=="RD") {
|
||
|
return scalar tger=$S_13
|
||
|
return scalar cger=$S_14
|
||
|
}
|
||
|
|
||
|
end
|
||
|
|
||
|
program define OR
|
||
|
version 7.0
|
||
|
#delimit ;
|
||
|
syntax varlist(min=4 max=4 default=none numeric) [if] [in] [,
|
||
|
LABEL(string) SORTBY(passthru) noGRAPH noTABLE CHI2 RANDOMI CC(string)
|
||
|
METHOD(string) XLAbel(passthru) XTICK(passthru) FORCE CORNFIELD noKEEP
|
||
|
SAVING(passthru) noBOX T1(string) T2(string) B1(string) B2(string) noOVERALL
|
||
|
noWT noSTATS LOG BRESLOW COUNTS WGT(varlist numeric max=1) noGROUPLA ] ;
|
||
|
#delimit cr
|
||
|
qui {
|
||
|
tempvar a b c d use zeros r1 r2 c1 c2 t or lnor es v se ill iul ea /*
|
||
|
*/ va weight qhet id rawdata cont_a cont_b cont_c cont_d
|
||
|
tempname ernum erden R S PR PS QR QS W OR lnOR selnOR A EA VA
|
||
|
parse "`varlist'", parse(" ")
|
||
|
if "`log'"!="" { local exp }
|
||
|
else { local exp "exp"}
|
||
|
gen double `a' =`1'
|
||
|
gen double `b' =`2'
|
||
|
gen double `c' =`3'
|
||
|
gen double `d' =`4'
|
||
|
gen double `r1'=`a'+`b'
|
||
|
gen double `r2'=`c'+`d'
|
||
|
gen double `c1'=`a'+`c'
|
||
|
gen double `c2'=`b'+`d'
|
||
|
gen byte `use'=1 `if' `in'
|
||
|
replace `use'=9 if `use'==.
|
||
|
replace `use'=9 if (`r1'==.) | (`r2'==.)
|
||
|
replace `use'=2 if (`use'==1) & (`r1'==0 | `r2'==0 )
|
||
|
replace `use'=2 if (`use'==1) & (`c1'==0 | `c2'==0 )
|
||
|
count if `use'==1
|
||
|
global S_8 =r(N)-1
|
||
|
if $S_8<0 { exit }
|
||
|
if "`counts'"!="" {
|
||
|
*Display raw counts
|
||
|
gen str20 `rawdata'= string(`a') + "/" + string(`r1') +";" + /*
|
||
|
*/ string(`c') + "/"+ string(`r2') if `use'!=9
|
||
|
replace `rawdata'= trim(`rawdata')
|
||
|
if "`overall'"=="" {
|
||
|
sum `a' if (`use'==1 | `use'==2)
|
||
|
local sum1=r(sum)
|
||
|
sum `r1' if (`use'==1 | `use'==2)
|
||
|
local sum2=r(sum)
|
||
|
sum `c' if (`use'==1 | `use'==2)
|
||
|
local sum3=r(sum)
|
||
|
sum `r2' if (`use'==1 | `use'==2)
|
||
|
local sum4=r(sum)
|
||
|
global MA_ODC = "`sum1'/`sum2';`sum3'/`sum4'"
|
||
|
}
|
||
|
}
|
||
|
else {gen str1 `rawdata'="."}
|
||
|
if "`method'"=="D+L" & ($S_8==0) { local method "M-H"}
|
||
|
*Get average event rate for each group (before any 0.5 adjustments or excluding 0-0 studies)
|
||
|
sum `a' if `use'<3
|
||
|
scalar `ernum'=r(sum)
|
||
|
sum `r1' if `use'<3
|
||
|
scalar `erden'=r(sum)
|
||
|
global S_13=`ernum'/`erden'
|
||
|
sum `c' if `use'<3
|
||
|
scalar `ernum'=r(sum)
|
||
|
sum `r2' if `use'<3
|
||
|
scalar `erden'=r(sum)
|
||
|
global S_14=`ernum'/`erden'
|
||
|
|
||
|
*Remove "uninformative" studies
|
||
|
replace `a'=. if `use'!=1
|
||
|
replace `b'=. if `use'!=1
|
||
|
replace `c'=. if `use'!=1
|
||
|
replace `d'=. if `use'!=1
|
||
|
replace `r1'=. if `use'!=1
|
||
|
replace `r2'=. if `use'!=1
|
||
|
gen double `t' =`r1'+`r2'
|
||
|
* Chi-squared test for effect
|
||
|
sum `a',meanonly
|
||
|
scalar `A'=r(sum)
|
||
|
gen double `ea'=(`r1'*`c1')/`t'
|
||
|
gen double `va'=`r1'*`r2'*`c1'*(`b'+`d')/(`t'*`t'*(`t'-1))
|
||
|
sum `ea',meanonly
|
||
|
scalar `EA'=r(sum)
|
||
|
sum `va',meanonly
|
||
|
scalar `VA'=r(sum)
|
||
|
global S_10=( (`A'-`EA')^2 )/`VA' /* chi2 effect value */
|
||
|
global S_11=chiprob(1,$S_10) /* p(chi2) */
|
||
|
|
||
|
if "`cornfield'"!="" {
|
||
|
*Compute Cornfield CI
|
||
|
gen `ill'=.
|
||
|
gen `iul'=.
|
||
|
local j=1
|
||
|
tempname i al aj c1j r1j r2j alold
|
||
|
while `j'<=_N {
|
||
|
if `use'[`j']==1 {
|
||
|
scalar `i' = 0
|
||
|
scalar `al' =`a'[`j']
|
||
|
scalar `aj' =`a'[`j']
|
||
|
scalar `c1j'=`c1'[`j']
|
||
|
scalar `r1j'=`r1'[`j']
|
||
|
scalar `r2j'=`r2'[`j']
|
||
|
scalar `alold'= .
|
||
|
while abs(`al'-`alold')>.001 & `al'!=. {
|
||
|
scalar `alold' = `al'
|
||
|
scalar `al'=`aj'-($ZIND)/sqrt( (1/`al') + 1/(`c1j'-`al') + /*
|
||
|
*/ 1/(`r1j'-`al') + 1/(`r2j'-`c1j'+`al') )
|
||
|
if `al'==. {
|
||
|
scalar `i'=`i'+1
|
||
|
scalar `al'=`aj'-`i'
|
||
|
if (`al'<0 | (`r2j'-`c1j'+`al')<0) {scalar `al'= . }
|
||
|
}
|
||
|
}
|
||
|
if `al'==. { scalar `al'= 0 }
|
||
|
replace `ill'=`log'( `al'*(`r2j'-`c1j'+`al')/((`c1j'-`al')*(`r1j'-`al')) ) in `j'
|
||
|
scalar `al'= `a'[`j']
|
||
|
scalar `alold'= .
|
||
|
scalar `i'= 0
|
||
|
while abs(`al'-`alold')>.001 & `al'!=. {
|
||
|
scalar `alold'= `al'
|
||
|
scalar `al'=`aj'+($ZIND)/sqrt( (1/`al')+ 1/(`c1j'-`al') + /*
|
||
|
*/ 1/(`r1j'-`al') + 1/(`r2j'-`c1j'+`al') )
|
||
|
if `al'==. {
|
||
|
scalar `i'=`i'+1
|
||
|
scalar `al'=`aj'+`i'
|
||
|
if (`al'>`r1j' | `al'>`c1j' ) { scalar `al' = . }
|
||
|
}
|
||
|
}
|
||
|
replace `iul'=`log'( `al'*(`r2j'-`c1j'+`al')/((`c1j'-`al')*(`r1j'-`al')) ) in `j'
|
||
|
}
|
||
|
local j=`j'+1
|
||
|
}
|
||
|
}
|
||
|
*Adjustment for zero cells in calcn of OR and var(OR)
|
||
|
gen `zeros'=1 if `use'==1 & (`a'==0 | `b'==0 | `c'==0 | `d'==0 )
|
||
|
gen `cont_a'=`cc'
|
||
|
gen `cont_b'=`cc'
|
||
|
gen `cont_c'=`cc'
|
||
|
gen `cont_d'=`cc'
|
||
|
replace `a'=`a'+`cont_a' if `zeros'==1
|
||
|
replace `b'=`b'+`cont_b' if `zeros'==1
|
||
|
replace `c'=`c'+`cont_c' if `zeros'==1
|
||
|
replace `d'=`d'+`cont_d' if `zeros'==1
|
||
|
replace `r1'=`r1'+(`cont_a'+`cont_b') if `zeros'==1
|
||
|
replace `r2'=`r2'+(`cont_c'+`cont_d') if `zeros'==1
|
||
|
replace `t' =`t' +(`cont_a'+`cont_b')+(`cont_c'+`cont_d') if `zeros'==1
|
||
|
gen double `or' =(`a'*`d')/(`b'*`c')
|
||
|
gen double `lnor'=log(`or')
|
||
|
gen double `v' =1/`a' +1/`b' +1/`c' + 1/`d'
|
||
|
gen double `es' =`log'(`or')
|
||
|
gen double `se' =sqrt(`v')
|
||
|
if "`cornfield'"=="" {
|
||
|
gen `ill' =`exp'(`lnor'-$ZIND*`se')
|
||
|
gen `iul' =`exp'(`lnor'+$ZIND*`se')
|
||
|
}
|
||
|
if "`method'"=="M-H" | ( "`method'"=="D+L" & "`randomi'"=="" ) {
|
||
|
tempname p q r s pr ps qr qs
|
||
|
gen double `r' =`a'*`d'/`t'
|
||
|
gen double `s' =`b'*`c'/`t'
|
||
|
sum `r', meanonly
|
||
|
scalar `R' =r(sum)
|
||
|
sum `s', meanonly
|
||
|
scalar `S' =r(sum)
|
||
|
*Calculate pooled MH- OR
|
||
|
scalar `OR' =`R'/`S'
|
||
|
scalar `lnOR'=log(`OR')
|
||
|
*Calculate variance/SE of lnOR and weights
|
||
|
gen double `p' =(`a'+`d')/`t'
|
||
|
gen double `q' =(`b'+`c')/`t'
|
||
|
gen double `pr' =`p'*`r'
|
||
|
gen double `ps' =`p'*`s'
|
||
|
gen double `qr' =`q'*`r'
|
||
|
gen double `qs' =`q'*`s'
|
||
|
sum `pr', meanonly
|
||
|
scalar `PR' =r(sum)
|
||
|
sum `ps', meanonly
|
||
|
scalar `PS' =r(sum)
|
||
|
sum `qr', meanonly
|
||
|
scalar `QR' =r(sum)
|
||
|
sum `qs', meanonly
|
||
|
scalar `QS' =r(sum)
|
||
|
scalar `selnOR'= sqrt( (`PR'/(`R'*`R') + (`PS'+`QR')/(`R'*`S') + /*
|
||
|
*/ `QS'/(`S'*`S'))/2 )
|
||
|
gen `weight'=100*`s'/`S'
|
||
|
*Store results in global macros, on log scale if requested
|
||
|
global S_1 =`log'(`OR')
|
||
|
global S_2 =`selnOR'
|
||
|
global S_3 =`exp'(`lnOR' -$ZOVE*`selnOR')
|
||
|
global S_4 =`exp'(`lnOR' +$ZOVE*`selnOR')
|
||
|
global S_5 =abs(`lnOR')/(`selnOR')
|
||
|
global S_6 =normprob(-abs($S_5))*2
|
||
|
drop `p' `q' `r' `pr' `ps' `qr' `qs'
|
||
|
*Calculate heterogeneity
|
||
|
if "`breslow'"=="" {
|
||
|
gen double `qhet' =( (`lnor'-`lnOR')^2 )/`v'
|
||
|
sum `qhet', meanonly
|
||
|
global S_7 =r(sum) /*Chi-squared */
|
||
|
global S_9 =chiprob($S_8,$S_7) /*p(chi2 het) */
|
||
|
}
|
||
|
}
|
||
|
if "`wgt'"!="" {
|
||
|
cap gen `weight'=.
|
||
|
udw `lnor' `v' , wgt(`wgt') `exp'
|
||
|
replace `weight'=`wgt'*100/$MA_W
|
||
|
local udwind "wgt(`wgt')"
|
||
|
}
|
||
|
else if "`method'"!="M-H" {
|
||
|
cap gen `weight'=.
|
||
|
iv `lnor' `v', method(`method') `randomi' `exp'
|
||
|
replace `weight'=100/( (`v'+$S_12)*($MA_W) )
|
||
|
}
|
||
|
|
||
|
if "`breslow'"!="" {
|
||
|
*Calculate heterogeneity by Breslow-Day test: need to reset zero cells and margins
|
||
|
|
||
|
if "`log'"=="" {local bexp }
|
||
|
else {local bexp "exp" }
|
||
|
replace `a'=`a'-`cont_a' if `zeros'==1
|
||
|
replace `b'=`b'-`cont_b' if `zeros'==1
|
||
|
replace `c'=`c'-`cont_c' if `zeros'==1
|
||
|
replace `d'=`d'-`cont_d' if `zeros'==1
|
||
|
replace `r1'=`r1'-(`cont_a'+`cont_b') if `zeros'==1
|
||
|
replace `r2'=`r2'-(`cont_c'+`cont_d') if `zeros'==1
|
||
|
replace `t' =`t' -(`cont_a'+`cont_b')-(`cont_c'+`cont_d') if `zeros'==1
|
||
|
if abs(`bexp'($S_1) - 1)<0.0001 {
|
||
|
gen afit = `r1'*`c1'/`t'
|
||
|
gen bfit = `r1'*`c2'/`t'
|
||
|
gen cfit = `r2'*`c1'/`t'
|
||
|
gen dfit = `r2'*`c2'/`t'
|
||
|
}
|
||
|
else {
|
||
|
tempvar sterm cterm root1 root2 afit bfit cfit dfit bresd_q
|
||
|
tempname qterm
|
||
|
scalar `qterm' = 1-`bexp'($S_1)
|
||
|
gen `sterm' = `r2' - `c1' + (`bexp'($S_1))*(`r1'+`c1')
|
||
|
gen `cterm' = -(`bexp'($S_1))*`c1'*`r1'
|
||
|
gen `root1' = (-`sterm' + sqrt(`sterm'*`sterm' - 4*`qterm'*`cterm'))/(2*`qterm')
|
||
|
gen `root2' = (-`sterm' - sqrt(`sterm'*`sterm' - 4*`qterm'*`cterm'))/(2*`qterm')
|
||
|
gen `afit' = `root1' if `root2'<0
|
||
|
replace `afit' = `root2' if `root1'<0
|
||
|
replace `afit' = `root1' if (`root2'>`c1') | (`root2'>`r1')
|
||
|
replace `afit' = `root2' if (`root1'>`c1') | (`root1'>`r1')
|
||
|
gen `bfit' = `r1' - `afit'
|
||
|
gen `cfit' = `c1' - `afit'
|
||
|
gen `dfit' = `r2' - `cfit'
|
||
|
}
|
||
|
gen `qhet' = ((`a'-`afit')^2)*((1/`afit')+(1/`bfit')+(1/`cfit')+(1/`dfit'))
|
||
|
sum `qhet', meanonly
|
||
|
global S_7 =r(sum) /*Het. Chi-squared */
|
||
|
global S_9 =chiprob($S_8,$S_7) /*p(chi2 het) */
|
||
|
}
|
||
|
replace `weight'=0 if `weight'==.
|
||
|
} /* End of "quietly" loop */
|
||
|
_disptab `es' `se' `ill' `iul' `weight' `use' `label' `rawdata', `keep' `sortby' /*
|
||
|
*/ `table' method(`method') sumstat(OR) `chi2' `xlabel' `xtick' `force' `graph' /*
|
||
|
*/ `box' `saving' t1("`t1'") t2("`t2'") b1("`b1'") b2("`b2'") `overall' `wt' /*
|
||
|
*/ `stats' `counts' `log' `groupla' `udwind' `cornfield'
|
||
|
end
|
||
|
|
||
|
program define Peto
|
||
|
version 7.0
|
||
|
#delimit ;
|
||
|
syntax varlist(min=4 max=4 default=none numeric) [if] [in] [,
|
||
|
LABEL(string) ORDER(string) noGRAPH METHOD(string) CHI2 XLAbel(passthru)
|
||
|
XTICK(passthru) FORCE noKEEP SAVING(passthru) noBOX noTABLE SORTBY(passthru) T1(string)
|
||
|
T2(string) B1(string) B2(string) noOVERALL noWT noSTATS LOG COUNTS ] ;
|
||
|
#delimit cr
|
||
|
qui {
|
||
|
tempvar a b c d use r1 r2 t ea olesse v lnor or es se /*
|
||
|
*/ ill iul p weight id rawdata
|
||
|
tempname ernum erden OLESSE V SE P lnOR A C R1 R2
|
||
|
parse "`varlist'", parse(" ")
|
||
|
if "`log'"!="" { local exp }
|
||
|
else { local exp "exp"}
|
||
|
gen double `a' =`1' `if' `in'
|
||
|
gen double `b' =`2' `if' `in'
|
||
|
gen double `c' =`3' `if' `in'
|
||
|
gen double `d' =`4' `if' `in'
|
||
|
gen double `r1' =`a'+`b'
|
||
|
gen double `r2' =`c'+`d'
|
||
|
gen byte `use'=1 `if' `in'
|
||
|
replace `use'=9 if `use'==.
|
||
|
replace `use'=9 if (`r1'==.) | (`r2'==.)
|
||
|
replace `use'=2 if (`use'==1) & (`r1'==0 | `r2'==0 )
|
||
|
replace `use'=2 if (`use'==1) & ((`a'==0 & `c'==0 ) | (`b'==0 & `d'==0))
|
||
|
count if `use'==1
|
||
|
global S_8 =r(N)-1
|
||
|
if $S_8<0 { exit }
|
||
|
if "`counts'"!="" {
|
||
|
*Display raw counts
|
||
|
gen str20 `rawdata'= string(`a') + "/" + string(`r1') +";" + /*
|
||
|
*/ string(`c') + "/"+ string(`r2') if `use'!=9
|
||
|
replace `rawdata'= trim(`rawdata')
|
||
|
if "`overall'"=="" {
|
||
|
sum `a' if (`use'==1 | `use'==2)
|
||
|
local sum1=r(sum)
|
||
|
sum `r1' if (`use'==1 | `use'==2)
|
||
|
local sum2=r(sum)
|
||
|
sum `c' if (`use'==1 | `use'==2)
|
||
|
local sum3=r(sum)
|
||
|
sum `r2' if (`use'==1 | `use'==2)
|
||
|
local sum4=r(sum)
|
||
|
global MA_ODC = "`sum1'/`sum2';`sum3'/`sum4'"
|
||
|
}
|
||
|
}
|
||
|
else {gen str1 `rawdata'="."}
|
||
|
*Get average event rate for each group (before any 0.5 adjustments or excluding 0-0 studies)
|
||
|
sum `a' if `use'<3
|
||
|
scalar `ernum'=r(sum)
|
||
|
sum `r1' if `use'<3
|
||
|
scalar `erden'=r(sum)
|
||
|
global S_13=`ernum'/`erden'
|
||
|
sum `c' if `use'<3
|
||
|
scalar `ernum'=r(sum)
|
||
|
sum `r2' if `use'<3
|
||
|
scalar `erden'=r(sum)
|
||
|
global S_14=`ernum'/`erden'
|
||
|
|
||
|
*Remove "uninformative" studies
|
||
|
replace `a'=. if `use'!=1
|
||
|
replace `b'=. if `use'!=1
|
||
|
replace `c'=. if `use'!=1
|
||
|
replace `d'=. if `use'!=1
|
||
|
replace `r1'=. if `use'!=1
|
||
|
replace `r2'=. if `use'!=1
|
||
|
gen double `t' =`r1'+`r2'
|
||
|
gen double `ea' =`r1'*(`a'+`c')/`t'
|
||
|
gen double `olesse'=`a'-`ea'
|
||
|
gen double `v' =`r1'*`r2'*(`a'+`c')*(`b'+`d')/( `t'*`t'*(`t'-1) )
|
||
|
gen double `lnor' =`olesse'/`v'
|
||
|
gen double `es' = `exp'(`lnor')
|
||
|
gen double `se' = 1/(sqrt(`v'))
|
||
|
gen double `ill' = `exp'(`lnor'-$ZIND*`se')
|
||
|
gen double `iul' = `exp'(`lnor'+$ZIND*`se')
|
||
|
gen double `p' =(`olesse')*(`olesse')/`v'
|
||
|
sum `olesse', meanonly
|
||
|
scalar `OLESSE'=r(sum)
|
||
|
sum `v', meanonly
|
||
|
scalar `V' =r(sum)
|
||
|
sum `p', meanonly
|
||
|
scalar `P' =r(sum)
|
||
|
scalar `lnOR' =`OLESSE'/`V'
|
||
|
global S_1 =`exp'(`lnOR')
|
||
|
global S_2 =1/sqrt(`V')
|
||
|
global S_3 =`exp'(`lnOR'-$ZOVE*($S_2))
|
||
|
global S_4 =`exp'(`lnOR'+$ZOVE*($S_2))
|
||
|
sum `a', meanonly
|
||
|
scalar `A' =r(sum)
|
||
|
sum `c', meanonly
|
||
|
scalar `C' =r(sum)
|
||
|
sum `r1', meanonly
|
||
|
scalar `R1' =r(sum)
|
||
|
sum `r2', meanonly
|
||
|
scalar `R2' =r(sum)
|
||
|
global S_10 =(`OLESSE'^2)/`V' /*Chi-squared effect*/
|
||
|
global S_11 =chiprob(1,$S_10)
|
||
|
global S_5 =abs(`lnOR')/($S_2)
|
||
|
global S_6 =normprob(-abs($S_5))*2
|
||
|
/*Heterogeneity */
|
||
|
global S_7=`P'-$S_10
|
||
|
global S_9 =chiprob($S_8,$S_7)
|
||
|
gen `weight' =100*`v'/`V'
|
||
|
replace `weight'=0 if `weight'==.
|
||
|
} /* End of quietly loop */
|
||
|
|
||
|
|
||
|
_disptab `es' `se' `ill' `iul' `weight' `use' `label' `rawdata' , `keep' `sortby' /*
|
||
|
*/ `table' method(`method') sumstat(OR) `chi2' `xlabel' `xtick' `force' `graph' `box' /*
|
||
|
*/ `saving' t1("`t1'") t2("`t2'") b1("`b1'") b2("`b2'") `overall' `wt' `stats' `counts' `log'
|
||
|
end
|
||
|
|
||
|
program define RR
|
||
|
version 7.0
|
||
|
#delimit ;
|
||
|
syntax varlist(min=4 max=4 default=none numeric) [if] [in] [,
|
||
|
LABEL(string) SORTBY(passthru) noGRAPH noTABLE RANDOMI METHOD(string) CC(string)
|
||
|
XLAbel(passthru) XTICK(passthru) FORCE noKEEP SAVING(passthru) noBOX T1(string)
|
||
|
T2(string) B1(string) B2(string) noOVERALL noWT noSTATS LOG COUNTS
|
||
|
WGT(varlist numeric max=1) ] ;
|
||
|
#delimit cr
|
||
|
qui {
|
||
|
tempvar a b c d use zeros r1 r2 t p r s rr lnrr es v se ill iul q /*
|
||
|
*/ weight id rawdata cont_a cont_b cont_c cont_d
|
||
|
tempname ernum erden P R S RR lnRR vlnRR
|
||
|
parse "`varlist'", parse(" ")
|
||
|
if "`log'"!="" { local exp }
|
||
|
else { local exp "exp"}
|
||
|
gen double `a' =`1'
|
||
|
gen double `b' =`2'
|
||
|
gen double `c' =`3'
|
||
|
gen double `d' =`4'
|
||
|
gen double `r1' =`a'+`b'
|
||
|
gen double `r2' =`c'+`d'
|
||
|
gen byte `use'=1 `if' `in'
|
||
|
replace `use'=9 if `use'==.
|
||
|
replace `use'=9 if (`r1'==.) | (`r2'==.)
|
||
|
replace `use'=2 if (`use'==1) & (`r1'==0 | `r2'==0 )
|
||
|
replace `use'=2 if (`use'==1) & ((`a'==0 & `c'==0 ) | (`b'==0 & `d'==0))
|
||
|
count if `use'==1
|
||
|
global S_8 =r(N)-1
|
||
|
if $S_8<0 { exit }
|
||
|
if "`counts'"!="" {
|
||
|
*Display raw counts
|
||
|
gen str20 `rawdata'= string(`a') + "/" + string(`r1') +";" + /*
|
||
|
*/ string(`c') + "/"+ string(`r2') if `use'!=9
|
||
|
replace `rawdata'= trim(`rawdata')
|
||
|
if "`overall'"=="" {
|
||
|
sum `a' if (`use'==1 | `use'==2)
|
||
|
local sum1=r(sum)
|
||
|
sum `r1' if (`use'==1 | `use'==2)
|
||
|
local sum2=r(sum)
|
||
|
sum `c' if (`use'==1 | `use'==2)
|
||
|
local sum3=r(sum)
|
||
|
sum `r2' if (`use'==1 | `use'==2)
|
||
|
local sum4=r(sum)
|
||
|
global MA_ODC = "`sum1'/`sum2';`sum3'/`sum4'"
|
||
|
}
|
||
|
}
|
||
|
else {gen str1 `rawdata'="."}
|
||
|
if "`method'"=="D+L" & ($S_8==0) { local method "M-H"}
|
||
|
*Get average event rate for each group (before any 0.5 adjustments or excluding 0-0 studies)
|
||
|
sum `a' if `use'<3
|
||
|
scalar `ernum'=r(sum)
|
||
|
sum `r1' if `use'<3
|
||
|
scalar `erden'=r(sum)
|
||
|
global S_13=`ernum'/`erden'
|
||
|
sum `c' if `use'<3
|
||
|
scalar `ernum'=r(sum)
|
||
|
sum `r2' if `use'<3
|
||
|
scalar `erden'=r(sum)
|
||
|
global S_14=`ernum'/`erden'
|
||
|
|
||
|
*Adjustment for zero cells in calcn of OR and var(OR)
|
||
|
gen `zeros'=1 if `use'==1 & (`a'==0 | `b'==0 | `c'==0 | `d'==0 )
|
||
|
gen `cont_a'=`cc'
|
||
|
gen `cont_b'=`cc'
|
||
|
gen `cont_c'=`cc'
|
||
|
gen `cont_d'=`cc'
|
||
|
replace `a'=`a'+`cont_a' if `zeros'==1
|
||
|
replace `b'=`b'+`cont_b' if `zeros'==1
|
||
|
replace `c'=`c'+`cont_c' if `zeros'==1
|
||
|
replace `d'=`d'+`cont_d' if `zeros'==1
|
||
|
replace `r1'=`r1'+(`cont_a'+`cont_b') if `zeros'==1
|
||
|
replace `r2'=`r2'+(`cont_c'+`cont_d') if `zeros'==1
|
||
|
|
||
|
*Remove "uninformative" studies
|
||
|
replace `a'=. if `use'!=1
|
||
|
replace `b'=. if `use'!=1
|
||
|
replace `c'=. if `use'!=1
|
||
|
replace `d'=. if `use'!=1
|
||
|
replace `r1'=. if `use'!=1
|
||
|
replace `r2'=. if `use'!=1
|
||
|
|
||
|
gen double `t' =`r1'+`r2'
|
||
|
gen double `r' =`a'*`r2'/`t'
|
||
|
gen double `s' =`c'*`r1'/`t'
|
||
|
gen double `rr' =`r'/`s'
|
||
|
gen double `lnrr'=log(`rr')
|
||
|
gen double `es' =`log'(`rr')
|
||
|
gen double `v' =1/`a' +1/`c' - 1/`r1' - 1/`r2'
|
||
|
gen double `se' =sqrt(`v')
|
||
|
gen double `ill' =`exp'(`lnrr'-$ZIND*`se')
|
||
|
gen double `iul' =`exp'(`lnrr'+$ZIND*`se')
|
||
|
if "`method'"=="M-H" | "`method'"=="D+L" & "`randomi'"=="" {
|
||
|
*MH method for pooling/calculating heterogeneity in DL method
|
||
|
gen double `p' =`r1'*`r2'*(`a'+`c')/(`t'*`t') - `a'*`c'/`t'
|
||
|
sum `p', meanonly
|
||
|
scalar `P' =r(sum)
|
||
|
sum `r', meanonly
|
||
|
scalar `R' =r(sum)
|
||
|
sum `s', meanonly
|
||
|
scalar `S' =r(sum)
|
||
|
scalar `RR'=`R'/`S'
|
||
|
scalar `lnRR'=log(`RR')
|
||
|
* Heterogeneity
|
||
|
gen double `q' =( (`lnrr'-`lnRR')^2 )/`v'
|
||
|
sum `q', meanonly
|
||
|
global S_7 =r(sum)
|
||
|
global S_9 =chiprob($S_8,$S_7)
|
||
|
gen `weight'=100*`s'/`S'
|
||
|
global S_1 =`log'(`RR')
|
||
|
global S_2 =sqrt( `P'/(`R'*`S') )
|
||
|
global S_3 =`exp'(`lnRR' -$ZOVE*($S_2))
|
||
|
global S_4 =`exp'(`lnRR' +$ZOVE*($S_2))
|
||
|
global S_5 =abs(`lnRR')/($S_2)
|
||
|
global S_6 =normprob(-abs($S_5))*2
|
||
|
}
|
||
|
if "`wgt'"!="" {
|
||
|
cap gen `weight'=.
|
||
|
udw `lnrr' `v' , wgt(`wgt') `exp'
|
||
|
replace `weight'=`wgt'*100/$MA_W
|
||
|
local udwind "wgt(`wgt')"
|
||
|
}
|
||
|
else if "`method'"!="M-H" {
|
||
|
cap gen `weight'=.
|
||
|
iv `lnrr' `v', method(`method') `randomi' `exp'
|
||
|
replace `weight'=100/( (`v'+$S_12)*($MA_W) )
|
||
|
}
|
||
|
replace `weight'=0 if `weight'==.
|
||
|
} /* End of "quietly" loop */
|
||
|
_disptab `es' `se' `ill' `iul' `weight' `use' `label' `rawdata' , `keep' `sortby' /*
|
||
|
*/ `table' method(`method') sumstat(RR) `xlabel' `xtick' `force' `graph' `box' /*
|
||
|
*/ `saving' t1("`t1'") t2("`t2'") b1("`b1'") b2("`b2'") `overall' `wt' /*
|
||
|
*/ `stats' `counts' `log' `udwind'
|
||
|
|
||
|
end
|
||
|
|
||
|
program define RD
|
||
|
version 7.0
|
||
|
#delimit ;
|
||
|
syntax varlist(min=4 max=4 default=none numeric) [if] [in] [,
|
||
|
LABEL(string) SORTBY(passthru) noGRAPH noTABLE RANDOMI METHOD(string) CC(string) noKEEP
|
||
|
SAVING(passthru) XLAbel(passthru) XTICK(passthru) noBOX FORCE T1(string) T2(string)
|
||
|
B1(string) B2(string) noOVERALL noWT noSTATS COUNTS WGT(varlist numeric max=1) ] ;
|
||
|
#delimit cr
|
||
|
qui {
|
||
|
tempvar a b c d use zeros r1 r2 t rd weight rdnum v se ill iul vnum q /*
|
||
|
*/ id w rawdata cont_a cont_b cont_c cont_d
|
||
|
tempname ernum erden RDNUM VNUM W
|
||
|
parse "`varlist'", parse(" ")
|
||
|
gen double `a' =`1'
|
||
|
gen double `b' =`2'
|
||
|
gen double `c' =`3'
|
||
|
gen double `d' =`4'
|
||
|
gen double `r1' =`a'+`b'
|
||
|
gen double `r2' =`c'+`d'
|
||
|
gen byte `use'=1 `if' `in'
|
||
|
replace `use'=9 if `use'==.
|
||
|
replace `use'=9 if (`r1'==.) | (`r2'==.)
|
||
|
replace `use'=2 if ( `use'==1) & (`r1'==0 | `r2'==0 )
|
||
|
count if `use'==1
|
||
|
global S_8 =r(N)-1
|
||
|
if $S_8<0 { exit }
|
||
|
if "`counts'"!="" {
|
||
|
*Display raw counts
|
||
|
gen str20 `rawdata'= string(`a') + "/" + string(`r1') +";" + /*
|
||
|
*/ string(`c') + "/"+ string(`r2') if `use'!=9
|
||
|
replace `rawdata'= trim(`rawdata')
|
||
|
if "`overall'"=="" {
|
||
|
sum `a' if (`use'==1 | `use'==2)
|
||
|
local sum1=r(sum)
|
||
|
sum `r1' if (`use'==1 | `use'==2)
|
||
|
local sum2=r(sum)
|
||
|
sum `c' if (`use'==1 | `use'==2)
|
||
|
local sum3=r(sum)
|
||
|
sum `r2' if (`use'==1 | `use'==2)
|
||
|
local sum4=r(sum)
|
||
|
global MA_ODC = "`sum1'/`sum2';`sum3'/`sum4'"
|
||
|
}
|
||
|
}
|
||
|
else {gen str1 `rawdata'="."}
|
||
|
if "`method'"=="D+L" & ($S_8==0) { local method "M-H"}
|
||
|
*Get average event rate for each group (before any cont adjustments or excluding 0-0 studies)
|
||
|
sum `a' if `use'<3
|
||
|
scalar `ernum'=r(sum)
|
||
|
sum `r1' if `use'<3
|
||
|
scalar `erden'=r(sum)
|
||
|
global S_13=`ernum'/`erden'
|
||
|
sum `c' if `use'<3
|
||
|
scalar `ernum'=r(sum)
|
||
|
sum `r2' if `use'<3
|
||
|
scalar `erden'=r(sum)
|
||
|
global S_14=`ernum'/`erden'
|
||
|
|
||
|
*Remove "uninformative" studies
|
||
|
replace `a'=. if `use'!=1
|
||
|
replace `b'=. if `use'!=1
|
||
|
replace `c'=. if `use'!=1
|
||
|
replace `d'=. if `use'!=1
|
||
|
replace `r1'=. if `use'!=1
|
||
|
replace `r2'=. if `use'!=1
|
||
|
gen double `t' =`r1'+`r2'
|
||
|
gen double `rd' =`a'/`r1' - `c'/`r2'
|
||
|
gen `weight'=`r1'*`r2'/`t'
|
||
|
sum `weight',meanonly
|
||
|
scalar `W' =r(sum)
|
||
|
gen double `rdnum'=( (`a'*`r2')-(`c'*`r1') )/`t'
|
||
|
* Zero cell adjustments, placed here to ensure 0/n1 v 0/n2 really IS RD=0
|
||
|
*Adjustment for zero cells in calcn of OR and var(OR)
|
||
|
gen `zeros'=1 if `use'==1 & (`a'==0 | `b'==0 | `c'==0 | `d'==0 )
|
||
|
gen `cont_a'=`cc'
|
||
|
gen `cont_b'=`cc'
|
||
|
gen `cont_c'=`cc'
|
||
|
gen `cont_d'=`cc'
|
||
|
replace `a'=`a'+`cont_a' if `zeros'==1
|
||
|
replace `b'=`b'+`cont_b' if `zeros'==1
|
||
|
replace `c'=`c'+`cont_c' if `zeros'==1
|
||
|
replace `d'=`d'+`cont_d' if `zeros'==1
|
||
|
replace `r1'=`r1'+(`cont_a'+`cont_b') if `zeros'==1
|
||
|
replace `r2'=`r2'+(`cont_c'+`cont_d') if `zeros'==1
|
||
|
replace `t' =`t' +(`cont_a'+`cont_b')+(`cont_c'+`cont_d') if `zeros'==1
|
||
|
|
||
|
gen double `v' =`a'*`b'/(`r1'^3)+`c'*`d'/(`r2'^3)
|
||
|
gen double `se' =sqrt(`v')
|
||
|
gen double `ill' = `rd'-$ZIND*`se'
|
||
|
gen double `iul' = `rd'+$ZIND*`se'
|
||
|
|
||
|
if "`method'"=="M-H" | ("`method'"=="D+L" & "`randomi'"=="" ) {
|
||
|
sum `rdnum',meanonly
|
||
|
scalar `RDNUM'=r(sum)
|
||
|
global S_1 =`RDNUM'/`W'
|
||
|
gen double `q' =( (`rd'-$S_1)^2 )/`v'
|
||
|
sum `q', meanonly
|
||
|
global S_7 =r(sum)
|
||
|
global S_9 =chiprob($S_8,$S_7)
|
||
|
gen double `vnum'=( (`a'*`b'*(`r2'^3) )+(`c'*`d'*(`r1'^3))) /*
|
||
|
*/ /(`r1'*`r2'*`t'*`t')
|
||
|
sum `vnum',meanonly
|
||
|
scalar `VNUM'=r(sum)
|
||
|
global S_2 =sqrt( `VNUM'/(`W'*`W') )
|
||
|
replace `weight'=`weight'*100/`W'
|
||
|
global S_3 =$S_1 -$ZOVE*($S_2)
|
||
|
global S_4 =$S_1 +$ZOVE*($S_2)
|
||
|
global S_5 =abs($S_1)/($S_2)
|
||
|
global S_6 =normprob(-abs($S_5))*2
|
||
|
}
|
||
|
if "`wgt'"!="" {
|
||
|
udw `rd' `v' ,wgt(`wgt')
|
||
|
replace `weight'=`wgt'*100/$MA_W
|
||
|
local udwind "wgt(`wgt')"
|
||
|
}
|
||
|
else if "`method'"!="M-H" {
|
||
|
iv `rd' `v', method(`method') `randomi'
|
||
|
replace `weight'=100/( (`v'+$S_12)*($MA_W) )
|
||
|
}
|
||
|
replace `weight'=0 if `weight'==.
|
||
|
} /* End of "quietly" loop */
|
||
|
_disptab `rd' `se' `ill' `iul' `weight' `use' `label' `rawdata', `keep' `sortby' /*
|
||
|
*/ `table' method(`method') sumstat(RD) `xlabel' `xtick'`force' `graph' `box' /*
|
||
|
*/ `saving' t1("`t1'") t2("`t2'") b1("`b1'") b2("`b2'") `overall' `wt' `stats' `counts' `udwind'
|
||
|
|
||
|
end
|
||
|
|
||
|
program define MD
|
||
|
version 7.0
|
||
|
#delimit ;
|
||
|
syntax varlist(min=6 max=6 default=none numeric) [if] [in] [,
|
||
|
LABEL(string) SORTBY(passthru) noGRAPH METHOD(string) noKEEP SAVING(passthru) noBOX
|
||
|
noTABLE STANDARD(string) XLAbel(passthru) XTICK(passthru) FORCE T1(string) T2(string)
|
||
|
B1(string) B2(string) noOVERALL noWT noSTATS COUNTS WGT(string) ] ;
|
||
|
#delimit cr
|
||
|
qui {
|
||
|
tempvar n1 x1 sd1 n2 x2 sd2 use n s md v se ill iul weight id qhet rawdata
|
||
|
parse "`varlist'", parse(" ")
|
||
|
gen double `n1' =`1'
|
||
|
gen double `x1' =`2'
|
||
|
gen double `sd1'=`3'
|
||
|
gen double `n2' =`4'
|
||
|
gen double `x2' =`5'
|
||
|
gen double `sd2'=`6'
|
||
|
|
||
|
gen `use'=1 `if' `in'
|
||
|
replace `use'=9 if `use'==.
|
||
|
replace `use'=9 if (`n1'==.) | (`n2'==.) | (`x1'==.) | (`x2'==.) | /*
|
||
|
*/ (`sd1'==.) | (`sd2'==.)
|
||
|
replace `use'=2 if ( `use'==1) & (`n1' <2 | `n2' <2 )
|
||
|
replace `use'=2 if ( `use'==1) & (`sd1'<=0 | `sd2'<=0 )
|
||
|
count if `use'==1
|
||
|
global S_8 =r(N)-1
|
||
|
if $S_8<0 { exit }
|
||
|
if "`counts'"!="" {
|
||
|
*Display raw counts instead of default
|
||
|
gen str40 `rawdata'= string(`n1') + " " + string(`x1') +" (" + string(`sd1') + /*
|
||
|
*/ ") ; " + string(`n2') + " " + string(`x2') +" (" + string(`sd2') +") "
|
||
|
replace `rawdata'= trim(`rawdata')
|
||
|
}
|
||
|
else {gen str1 `rawdata'="."}
|
||
|
if "`method'"=="D+L" & ($S_8==0) { local method "I-V"}
|
||
|
replace `n1' =. if `use'!=1
|
||
|
replace `x1' =. if `use'!=1
|
||
|
replace `sd1'=. if `use'!=1
|
||
|
replace `n2' =. if `use'!=1
|
||
|
replace `x2' =. if `use'!=1
|
||
|
replace `sd2'=. if `use'!=1
|
||
|
gen double `n' =`n1'+`n2'
|
||
|
if "`standard'"=="none" {
|
||
|
gen double `md' =`x1'-`x2'
|
||
|
gen double `v'=(`sd1'^2)/`n1' + (`sd2'^2)/`n2'
|
||
|
local prefix "W"
|
||
|
}
|
||
|
else {
|
||
|
gen double `s'=sqrt( ((`n1'-1)*(`sd1'^2)+(`n2'-1)*(`sd2'^2) )/(`n'-2) )
|
||
|
if "`standard'"=="cohen" {
|
||
|
gen double `md' = (`x1'-`x2')/`s'
|
||
|
gen double `v'= ( `n'/(`n1'*`n2') )+( (`md'^2)/(2*(`n'-2)) )
|
||
|
}
|
||
|
else if "`standard'"=="hedges" {
|
||
|
gen double `md' =( (`x1'-`x2')/`s' )*( 1- 3/(4*`n'-9) )
|
||
|
gen double `v'=( `n'/(`n1'*`n2') ) + ( (`md'^2)/(2*(`n'-3.94)) )
|
||
|
}
|
||
|
else if "`standard'"=="glass" {
|
||
|
gen double `md' = (`x1'-`x2')/`sd2'
|
||
|
gen double `v'= (`n'/(`n1'*`n2')) + ( (`md'^2)/(2*(`n2'-1)) )
|
||
|
}
|
||
|
local prefix "S"
|
||
|
}
|
||
|
gen double `se' =sqrt(`v')
|
||
|
gen double `ill' =`md'-$ZIND*`se'
|
||
|
gen double `iul' =`md'+$ZIND*`se'
|
||
|
if "`wgt'"!="" {
|
||
|
udw `md' `v' , wgt(`wgt')
|
||
|
gen `weight'=`wgt'*100/$MA_W
|
||
|
local udwind "wgt(`wgt')"
|
||
|
}
|
||
|
else {
|
||
|
iv `md' `v', method(`method') randomi
|
||
|
gen `weight'=100/( (`v'+$S_12)*($MA_W) )
|
||
|
}
|
||
|
replace `weight'=0 if `weight'==.
|
||
|
} /* End of quietly loop */
|
||
|
_disptab `md' `se' `ill' `iul' `weight' `use' `label' `rawdata', `keep' `sortby' /*
|
||
|
*/ `table' method(`method') sumstat(`prefix'MD) `xlabel' `xtick' `force' `graph' /*
|
||
|
*/ `box' `saving' t1("`t1'") t2("`t2'") b1("`b1'") b2("`b2'") `overall' `wt' `stats' `udwind'
|
||
|
end
|
||
|
|
||
|
program define iv_init
|
||
|
version 7.0
|
||
|
#delimit ;
|
||
|
syntax varlist(min=2 max=3 default=none numeric) [if] [in] [,
|
||
|
LABEL(string) SORTBY(passthru) noGRAPH METHOD(string) noKEEP SAVING(passthru) noBOX
|
||
|
noTABLE XLAbel(passthru) XTICK(passthru) FORCE T1(string) T2(string) B1(string)
|
||
|
B2(string) noOVERALL noWT noSTATS EFORM WGT(string) ] ;
|
||
|
#delimit cr
|
||
|
qui {
|
||
|
tempvar es se use v ill iul weight id rawdata
|
||
|
parse "`varlist'", parse(" ")
|
||
|
gen `es'=`1'
|
||
|
if "`eform'"!="" { local exp "exp" }
|
||
|
if "`3'"=="" {
|
||
|
gen double `se'=`2'
|
||
|
gen double `ill' =`exp'(`es'-$ZIND*`se' )
|
||
|
gen double `iul' =`exp'(`es'+$ZIND*`se' )
|
||
|
}
|
||
|
if "`3'"!="" {
|
||
|
gen double `se'=(`3'-`2')/($ZIND*2)
|
||
|
gen double `ill' =`exp'(`2')
|
||
|
gen double `iul' =`exp'(`3')
|
||
|
local var3 "var3"
|
||
|
}
|
||
|
gen double `use'=1 `if' `in'
|
||
|
replace `use'=9 if `use'==.
|
||
|
replace `use'=9 if (`es'==. | `se'==.)
|
||
|
replace `use'=2 if (`use'==1 & `se'<=0 )
|
||
|
count if `use'==1
|
||
|
global S_8 =r(N)-1
|
||
|
if $S_8<0 { exit }
|
||
|
|
||
|
if "`method'"=="D+L" & ($S_8==0) { local method "I-V"}
|
||
|
replace `es' =. if `use'!=1
|
||
|
replace `se' =. if `use'!=1
|
||
|
gen double `v'=(`se')^2
|
||
|
gen str1 `rawdata'="."
|
||
|
if "`wgt'"!="" {
|
||
|
gen `weight' = `wgt' if `use'==1
|
||
|
udw `es' `v', wgt(`weight') `exp'
|
||
|
replace `weight'=100*`wgt'/($MA_W)
|
||
|
local udwind "wgt(`wgt')"
|
||
|
}
|
||
|
else {
|
||
|
iv `es' `v', method(`method') `exp' randomi
|
||
|
*NB randomi necc to calculate heterogeneity
|
||
|
gen `weight'=100/( (`v'+$S_12)*($MA_W) )
|
||
|
}
|
||
|
replace `weight'=0 if `weight'==.
|
||
|
replace `es'=`exp'(`es')
|
||
|
|
||
|
} /* End of quietly loop */
|
||
|
_disptab `es' `se' `ill' `iul' `weight' `use' `label' `rawdata', `keep' `sortby' /*
|
||
|
*/`table' method(`method') sumstat(ES) `xlabel' `xtick' `force' `graph' `box' /*
|
||
|
*/ `saving' t1("`t1'") t2("`t2'") b1("`b1'") b2("`b2'") `overall' `wt' `stats' `eform' /*
|
||
|
*/ `var3' `udwind'
|
||
|
|
||
|
end
|
||
|
|
||
|
|
||
|
program define iv
|
||
|
version 7.0
|
||
|
#delimit ;
|
||
|
syntax varlist(min=2 max=2 default=none numeric) [if] [in] [,
|
||
|
METHOD(string) RANDOMI EXP ] ;
|
||
|
#delimit cr
|
||
|
tempvar stat v w qhet w2 wnew e_w e_wnew
|
||
|
tempname W W2 C T2 E_W E_WNEW OV vOV QHET
|
||
|
parse "`varlist'", parse(" ")
|
||
|
gen `stat'=`1'
|
||
|
gen `v' =`2'
|
||
|
gen `w' =1/`v'
|
||
|
sum `w',meanonly
|
||
|
scalar `W'=r(sum)
|
||
|
global S_12=0
|
||
|
global MA_W =`W'
|
||
|
if ("`randomi'"=="" & "`method'"=="D+L") { scalar `QHET'=$S_7 }
|
||
|
else {
|
||
|
gen `e_w' =`stat'*`w'
|
||
|
sum `e_w',meanonly
|
||
|
scalar `E_W'=r(sum)
|
||
|
scalar `OV' =`E_W'/`W'
|
||
|
* Heterogeneity
|
||
|
gen `qhet' =( (`stat'-`OV')^2 )/`v'
|
||
|
sum `qhet', meanonly
|
||
|
scalar `QHET'=r(sum)
|
||
|
global S_7=`QHET'
|
||
|
}
|
||
|
if "`method'"=="D+L" {
|
||
|
gen `w2' =`w'*`w'
|
||
|
sum `w2',meanonly
|
||
|
scalar `W2' =r(sum)
|
||
|
scalar `C' =`W' - `W2'/`W'
|
||
|
global S_12 =max(0, ((`QHET'-$S_8)/`C') )
|
||
|
gen `wnew' =1/(`v'+$S_12)
|
||
|
gen `e_wnew'=`stat'*`wnew'
|
||
|
sum `wnew',meanonly
|
||
|
global MA_W =r(sum)
|
||
|
sum `e_wnew',meanonly
|
||
|
scalar `E_WNEW'=r(sum)
|
||
|
scalar `OV' =`E_WNEW'/$MA_W
|
||
|
}
|
||
|
global S_1 =`exp'(`OV')
|
||
|
global S_2 =sqrt( 1/$MA_W )
|
||
|
global S_3 =`exp'(`OV' -$ZOVE*($S_2))
|
||
|
global S_4 =`exp'(`OV' +$ZOVE*($S_2))
|
||
|
global S_5 =abs(`OV')/($S_2)
|
||
|
global S_6 =normprob(-abs($S_5))*2
|
||
|
global S_9 =chiprob($S_8,$S_7)
|
||
|
end
|
||
|
|
||
|
|
||
|
program define udw
|
||
|
* user defined weights to combine trials
|
||
|
version 7.0
|
||
|
#delimit ;
|
||
|
syntax varlist(min=2 max=2 default=none numeric) [if] [in] [,
|
||
|
METHOD(string) EXP WGT(varlist numeric max=1) ] ;
|
||
|
#delimit cr
|
||
|
tempvar stat v w e_w varcomp qhet
|
||
|
|
||
|
tempname W E_W OV W2 Vnum V QHET
|
||
|
parse "`varlist'", parse(" ")
|
||
|
gen `stat'=`1'
|
||
|
gen `v' =`2'
|
||
|
gen `w' =`wgt' if `stat'!=.
|
||
|
sum `w',meanonly
|
||
|
scalar `W'=r(sum)
|
||
|
if `W'==0 {
|
||
|
di in re "Usable weights sum to zero: the table below will probably be nonsense"
|
||
|
}
|
||
|
global MA_W =`W'
|
||
|
*eff size = SIGMA(wi * thetai)/SIGMA(wi)
|
||
|
gen `e_w' =`stat'*`w'
|
||
|
sum `e_w',meanonly
|
||
|
scalar `E_W'=r(sum)
|
||
|
scalar `OV' =`E_W'/`W'
|
||
|
*VAR = SIGMA{wi^2 * var(thetai) }/[SIGMA(wi)]^2
|
||
|
sum `w',meanonly
|
||
|
scalar `W2'=(r(sum))^2
|
||
|
gen `varcomp' = `w'*`w'*`v'
|
||
|
sum `varcomp' ,meanonly
|
||
|
scalar `Vnum'=r(sum)
|
||
|
scalar `V' =`Vnum'/`W2'
|
||
|
|
||
|
*Heterogeneity (need to use variance weights here - BUT use ES=wgt*es/wgt, not necc var wts)
|
||
|
gen `qhet' =( (`stat'-`OV')^2 )/`v'
|
||
|
sum `qhet', meanonly
|
||
|
scalar `QHET'=r(sum)
|
||
|
|
||
|
global S_1 =`exp'(`OV')
|
||
|
global S_2 =sqrt( `V' )
|
||
|
global S_3 =`exp'(`OV' -$ZOVE*($S_2))
|
||
|
global S_4 =`exp'(`OV' +$ZOVE*($S_2))
|
||
|
global S_5 =abs(`OV')/($S_2)
|
||
|
global S_6 =normprob(-abs($S_5))*2
|
||
|
global S_7=`QHET'
|
||
|
global S_9 =chiprob($S_8,$S_7)
|
||
|
end
|
||
|
|
||
|
|
||
|
program define _disptab
|
||
|
version 7.0
|
||
|
#delimit ;
|
||
|
syntax varlist(min=7 max=8 default=none) [if] [in] [,
|
||
|
XLAbel(passthru) XTICK(passthru) FORCE noKEEP SAVING(passthru) noBOX noTABLE
|
||
|
noGRAPH METHOD(string) SUMSTAT(string) CHI2 T1(string) T2(string) B1(string)
|
||
|
B2(string) noOVERALL noWT noSTATS COUNTS LOG EFORM noGROUPLA SORTBY(string)
|
||
|
WGT(string) VAR3 CORNFIELD ] ;
|
||
|
#delimit cr
|
||
|
tempvar effect se lci uci weight use label tlabel rawdata id
|
||
|
parse "`varlist'", parse(" ")
|
||
|
qui {
|
||
|
gen `effect'=`1'
|
||
|
gen `se' =`2'
|
||
|
gen `lci' =`3'
|
||
|
gen `uci' =`4'
|
||
|
gen `weight'=`5'
|
||
|
gen byte `use'=`6'
|
||
|
format `weight' %5.1f
|
||
|
gen str10 `label'=""
|
||
|
replace `label'=`7'
|
||
|
global IND: displ %2.0f $IND
|
||
|
gen str40 `rawdata' = `8'
|
||
|
if "`keep'"=="" {
|
||
|
if ("`sumstat'"=="OR" | "`sumstat'"=="RR") & ("`log'"=="") { local ln "log"}
|
||
|
else { local ln }
|
||
|
cap drop _ES
|
||
|
cap drop _seES
|
||
|
cap drop _selogES
|
||
|
if "`sumstat'"!="ES" {
|
||
|
#delimit ;
|
||
|
replace _SS =. if `use'!=1; label var _SS "Sample size";
|
||
|
gen _ES =`effect';label var _ES "`log'`sumstat'";
|
||
|
gen _se`ln'ES=`se';label var _se`ln'ES "se(`ln'`log'`sumstat')";
|
||
|
#delimit cr
|
||
|
}
|
||
|
#delimit ;
|
||
|
cap drop _LCI ; cap drop _UCI; cap drop _WT;
|
||
|
gen _LCI =`lci'; label var _LCI "Lower CI (`log'`sumstat')";
|
||
|
gen _UCI =`uci'; label var _UCI "Upper CI (`log'`sumstat')";
|
||
|
gen _WT=`weight';label var _WT "`method' weight";
|
||
|
#delimit cr
|
||
|
}
|
||
|
|
||
|
preserve
|
||
|
if "`overall'"=="" {
|
||
|
**If overall figure requested, add an extra line to contain overall stats
|
||
|
qui {
|
||
|
local nobs1=_N+1
|
||
|
set obs `nobs1'
|
||
|
replace `effect'= ($S_1) in `nobs1'
|
||
|
replace `lci'=($S_3) in `nobs1'
|
||
|
replace `uci'=($S_4) in `nobs1'
|
||
|
replace `weight'=100 in `nobs1'
|
||
|
replace `use'=5 in `nobs1'
|
||
|
replace `label' = "Overall" in `nobs1'
|
||
|
if "`counts'"!="" { replace `rawdata'="$MA_ODC" in `nobs1' }
|
||
|
replace `label' = "Overall" in `nobs1'
|
||
|
}
|
||
|
}
|
||
|
local usetot=$S_8+1
|
||
|
count if `use'==2
|
||
|
local alltot=r(N)+`usetot'
|
||
|
gen `id'=_n
|
||
|
sort `use' `sortby' `id'
|
||
|
|
||
|
|
||
|
} /* End of quietly loop */
|
||
|
if "`table'"=="" {
|
||
|
qui gen str20 `tlabel'=`7' /*needs to be own label so as not to overrun!*/
|
||
|
if "`overall'`wt'"=="" {
|
||
|
local ww "% Weight"
|
||
|
}
|
||
|
|
||
|
if $IND!=$OVE {
|
||
|
global OVE: displ %2.0f $OVE
|
||
|
local insert "[$OVE% Conf. Interval]"
|
||
|
}
|
||
|
else { local insert "--------------------" }
|
||
|
|
||
|
di _n in gr _col(12) "Study" _col(22) "|" _col(24) "`log'" _col(28) "`sumstat'" /*
|
||
|
*/ _col(34) "[$IND% Conf. Interval]" _col(59) "`ww'" _n _dup(21) "-" "+" _dup(51) "-"
|
||
|
local i=1
|
||
|
while `i'<=_N {
|
||
|
if "`overall'`wt'"=="" { local ww=`weight'[`i'] }
|
||
|
else { local ww }
|
||
|
|
||
|
if (`use'[`i'])==2 {
|
||
|
*excluded trial
|
||
|
di in gr `tlabel'[`i'] _col(22) "| (Excluded)"
|
||
|
}
|
||
|
if ( (`use'[`i']==1) | (`use'[`i']==5) ) {
|
||
|
if (`use'[`i'])==1 {
|
||
|
*trial results
|
||
|
di in gr `tlabel'[`i'] _cont
|
||
|
}
|
||
|
else {
|
||
|
*overall
|
||
|
di in gr _dup(21) "-" "+" _dup(11) "-" "`insert'" _dup(20) "-" _n /*
|
||
|
*/ "`method' pooled `log'`sumstat'" _cont
|
||
|
}
|
||
|
di in gr _col(22) "|" in ye %7.3f `effect'[`i'] /*
|
||
|
*/ _col(35) %7.3f `lci'[`i'] " " %7.3f `uci'[`i'] _col(60) %6.2f `ww'
|
||
|
}
|
||
|
local i=`i'+1
|
||
|
}
|
||
|
di in gr _dup(21) "-" "+" _dup(51) "-"
|
||
|
if "`overall'"=="" {
|
||
|
if ("`method'"=="*" | "`var3'"!="") {
|
||
|
if "`method'"=="*" {
|
||
|
di in gr "* note: trials pooled by user defined weight `wgt'"
|
||
|
}
|
||
|
di in gr " Heterogeneity calculated by formula" _n /*
|
||
|
*/ " Q = SIGMA_i{ (1/variance_i)*(effect_i - effect_pooled)^2 } "
|
||
|
if "`var3'"!="" {
|
||
|
di in gr "where variance_i = ((upper limit - lower limit)/(2*z))^2 "
|
||
|
}
|
||
|
}
|
||
|
*Heterogeneity etc
|
||
|
if ( ("`sumstat'"=="OR" | "`sumstat'"=="RR") & "`log'"=="") {local h0=1}
|
||
|
else if ("`sumstat'"=="ES" & "`eform'"!="") {local h0=1}
|
||
|
else {local h0=0}
|
||
|
di _n in gr " Heterogeneity chi-squared = " in ye %6.2f $S_7 in gr /*
|
||
|
*/ " (d.f. = " in ye $S_8 in gr ") p = " in ye %4.3f $S_9
|
||
|
local i2=max(0, (100*($S_7-$S_8)/($S_7)) )
|
||
|
if $S_8<1 { local i2=. }
|
||
|
di in gr " I-squared (variation in `sumstat' attributable to " /*
|
||
|
*/ "heterogeneity) =" in ye %6.1f `i2' "%"
|
||
|
if "`method'"=="D+L" { di in gr " Estimate of between-study variance " /*
|
||
|
*/ "Tau-squared = " in ye %7.4f $S_12 }
|
||
|
|
||
|
if "`chi2'"!="" { di _n in gr " Test of OR=1: chi-squared = " in ye %4.2f /*
|
||
|
*/ $S_10 in gr " (d.f. = 1) p = " in ye %4.3f $S_11 }
|
||
|
else { di _n in gr " Test of `log'`sumstat'=`h0' : z= " in ye %6.2f $S_5 /*
|
||
|
*/ in gr " p = " in ye %4.3f $S_6 }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*capture only 1 trial scenario
|
||
|
qui {
|
||
|
count
|
||
|
if r(N)==1 {
|
||
|
set obs 2
|
||
|
replace `use'=99 in 2
|
||
|
replace `weight'=0 if `use'==99
|
||
|
}
|
||
|
} /*end of qui. */
|
||
|
if "`graph'"=="" & `usetot'>0 {
|
||
|
_dispgby `effect' `lci' `uci' `weight' `use' `label' `rawdata', `log' /*
|
||
|
*/ `xlabel' `xtick' `force' sumstat(`sumstat') `saving' `box' t1("`t1'") /*
|
||
|
*/ t2("`t2'") b1("`b1'") b2("`b2'") `overall' `wt' `stats' `counts' `eform' /*
|
||
|
*/ `groupla' `cornfield'
|
||
|
}
|
||
|
restore
|
||
|
end
|
||
|
|
||
|
|
||
|
program define metanby
|
||
|
version 7.0
|
||
|
#delimit ;
|
||
|
syntax varlist(min=2 max=6 default=none numeric) [if] [in] [, BY(string)
|
||
|
LABEL(string) SORTBY(string) noGRAPH noTABLE noKEEP NEXTCALL(string)
|
||
|
METHOD(string) SUMSTAT(string) RANDOMI WGT(passthru) noSUBGROUP SGWEIGHT
|
||
|
CORNFIELD CHI2 CC(passthru) STANDARD(passthru) noOVERALL LOG EFORM BRESLOW
|
||
|
XLAbel(passthru) XTICK(passthru) FORCE SAVING(passthru) T1(string) T2(string)
|
||
|
B1(string) B2(string) noWT noSTATS COUNTS noBOX noGROUPLA ] ;
|
||
|
#delimit cr
|
||
|
if ("`subgroup'"!="" & "`overall'`sgweight'"!="") { local wt "nowt" }
|
||
|
tempvar use by2 newby r1 r2 rawdata effect se lci uci weight wtdisp /*
|
||
|
*/ hetc hetdf hetp i2 tau2 tsig psig expand tlabel id
|
||
|
|
||
|
qui {
|
||
|
gen `use'=1 `if' `in'
|
||
|
replace `use'=9 if `use'==.
|
||
|
gen str1 `rawdata'="."
|
||
|
|
||
|
tokenize `varlist'
|
||
|
if ("`nextcall'"=="RR" | "`nextcall'"=="OR" | "`nextcall'"=="RD" |"`nextcall'"=="Peto" ) {
|
||
|
*Sort out r1 & r2 for 2x2 table: might be needed in counts and mh/use
|
||
|
gen `r1' = `1'+`2'
|
||
|
gen `r2' = `3'+`4'
|
||
|
replace `use'=2 if ((`use'==1) & (`r1'==0 | `r2'==0 ))
|
||
|
replace `use'=2 if ((`use'==1) & ((`1'+`3'==0) | (`2'+`4'==0) ) & "`nextcall'"!="RD")
|
||
|
replace `use'=9 if (`r1'==.) | (`r2'==.)
|
||
|
if "`counts'"!="" {
|
||
|
*create new variable with count data (if requested)
|
||
|
replace `rawdata'= trim( string(`1') + "/" + string(`r1') +";" + /*
|
||
|
*/ string(`3') + "/"+ string(`r2') ) if `use'!=9
|
||
|
}
|
||
|
}
|
||
|
if "`nextcall'"=="MD" {
|
||
|
*Sort out n1 & n2
|
||
|
replace `use'=9 if (`1'==.) | (`2'==.) | (`3'==.) | (`4'==.) | (`5'==.) | (`6'==.)
|
||
|
replace `use'=2 if ( `use'==1) & (`1' <2 | `4' <2 )
|
||
|
replace `use'=2 if ( `use'==1) & (`3'<=0 | `6'<=0 )
|
||
|
}
|
||
|
if "`nextcall'"=="iv_init" {
|
||
|
replace `use'=9 if (`1'==. | `2'==.)
|
||
|
if "`3'"=="" {
|
||
|
replace `use'=2 if (`use'==1 & `2'<=0 )
|
||
|
}
|
||
|
else {
|
||
|
replace `use'=9 if (`3'==.)
|
||
|
replace `use'=2 if ( `2'>`1' | `3'<`1' | `3'<`2')
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (("`sumstat'"=="OR" | "`sumstat'"=="RR") & "`log'"=="" ) {local h0=1}
|
||
|
else if ("`sumstat'"=="ES" & "`eform'"!="") {local h0=1}
|
||
|
else {local h0=0}
|
||
|
if "`eform'"!="" { local exp "exp" }
|
||
|
|
||
|
*Get the individual trial stats
|
||
|
`nextcall' `varlist' if `use'==1, nograph notable method(`method') `randomi' /*
|
||
|
*/ label(`label') `wgt' `cornfield' `chi2' `cc' `standard' `log' `eform' `breslow'
|
||
|
if $S_8<0 {
|
||
|
*no trials - bomb out
|
||
|
exit
|
||
|
}
|
||
|
local nostud=$S_8
|
||
|
*need to calculate from variable itself if only 2 variables (ES, SE(ES) syntax used)
|
||
|
if "`sumstat'"=="ES" {
|
||
|
gen `effect'=`exp'(`1')
|
||
|
if "`3'"=="" {
|
||
|
gen `se'=`2'
|
||
|
}
|
||
|
else {
|
||
|
gen `se'=.
|
||
|
local var3 "var3"
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
gen `effect'=_ES
|
||
|
if `h0'<0.01 { gen `se'=_seES }
|
||
|
else { gen `se'=_selogES }
|
||
|
}
|
||
|
gen `lci'=_LCI
|
||
|
gen `uci'=_UCI
|
||
|
gen `weight'=_WT
|
||
|
*put overall weight into var if requested
|
||
|
if ("`sgweight'"=="" & "`overall'"=="" ) {
|
||
|
gen `wtdisp'=_WT
|
||
|
}
|
||
|
else {
|
||
|
gen `wtdisp'=.
|
||
|
}
|
||
|
gen `id'=_n
|
||
|
sort `by' `sortby' `id'
|
||
|
*Keep only neccesary data (have to put preserve here in order to keep _ES etc)
|
||
|
preserve
|
||
|
drop if `use'==9
|
||
|
*Can now forget about the if/in conditions specified: unnecc rows have been removed
|
||
|
|
||
|
*Keep tger and cger here (otherwise it ends up in last subgroup only)
|
||
|
if ("`sumstat'"=="OR" | "`sumstat'"=="RR" | "`sumstat'"=="RD" ) {
|
||
|
local tger=$S_13
|
||
|
local cger=$S_14
|
||
|
}
|
||
|
|
||
|
*subgroup component of heterogeneity
|
||
|
gen `hetc'=.
|
||
|
gen `hetdf'=.
|
||
|
gen `hetp'=.
|
||
|
gen `i2'=.
|
||
|
gen `tau2'=.
|
||
|
gen `tsig'=.
|
||
|
gen `psig'=.
|
||
|
*Convert "by" variable to numeric if its a string variable
|
||
|
cap confirm numeric var `by'
|
||
|
if _rc>0 {
|
||
|
encode `by', gen(`by2')
|
||
|
drop `by'
|
||
|
rename `by2' `by'
|
||
|
}
|
||
|
*Create new "by" variable to take on codes 1,2,3..
|
||
|
gen `newby'=(`by'>`by'[_n-1])
|
||
|
replace `newby'=1+sum(`newby')
|
||
|
local ngroups=`newby'[_N]
|
||
|
|
||
|
if "`overall'"=="" {
|
||
|
*If requested, add an extra line to contain overall stats
|
||
|
local nobs1=_N+1
|
||
|
set obs `nobs1'
|
||
|
|
||
|
replace `use'=5 in `nobs1'
|
||
|
replace `newby'=`ngroups'+1 in `nobs1'
|
||
|
replace `effect'= ($S_1) in `nobs1'
|
||
|
replace `lci'=($S_3) in `nobs1'
|
||
|
replace `uci'=($S_4) in `nobs1'
|
||
|
*Put cell counts in subtotal row
|
||
|
if ("`counts'"!="" & "`nextcall'"!="MD") {
|
||
|
*put up overall binary count data
|
||
|
sum `1' if (`use'==1 | `use'==2)
|
||
|
local sum1=r(sum)
|
||
|
sum `r1' if (`use'==1 | `use'==2)
|
||
|
local sum2=r(sum)
|
||
|
sum `3' if (`use'==1 | `use'==2)
|
||
|
local sum3=r(sum)
|
||
|
sum `r2' if (`use'==1 | `use'==2)
|
||
|
local sum4=r(sum)
|
||
|
replace `rawdata'= "`sum1'/`sum2';`sum3'/`sum4'" in `nobs1'
|
||
|
}
|
||
|
replace `hetc' =($S_7) in `nobs1'
|
||
|
replace `hetdf'=($S_8) in `nobs1'
|
||
|
replace `hetp' =($S_9) in `nobs1'
|
||
|
replace `i2'=max(0, ( 100*($S_7-$S_8))/($S_7) ) in `nobs1'
|
||
|
if $S_8<1 { replace `i2'=. in `nobs1' }
|
||
|
replace `tau2' =$S_12 in `nobs1'
|
||
|
replace `se'=$S_2 in `nobs1'
|
||
|
if "`chi2'"!="" {
|
||
|
replace `tsig'=$S_10 in `nobs1'
|
||
|
replace `psig'=$S_11 in `nobs1'
|
||
|
local z=$S_5
|
||
|
local pz=$S_6
|
||
|
}
|
||
|
else {
|
||
|
replace `tsig'=$S_5 in `nobs1'
|
||
|
replace `psig'=$S_6 in `nobs1'
|
||
|
local echi2 =$S_10
|
||
|
local pchi2=$S_11
|
||
|
}
|
||
|
replace `label' = "Overall" in `nobs1'
|
||
|
if "`sgweight'"=="" { replace `wtdisp'=100 in `nobs1' }
|
||
|
}
|
||
|
|
||
|
*Create extra 2 or 3 lines per bygroup: one to label, one for gap
|
||
|
*and one for overall effect size (unless no subgroup combining is done)
|
||
|
sort `newby' `use' `sortby' `id'
|
||
|
by `newby': gen `expand'=1 + 2*(_n==1) + (_n==1 & "`subgroup'"=="")
|
||
|
replace `expand'=1 if `use'==5
|
||
|
expand `expand'
|
||
|
gsort `newby' -`expand' `use' `sortby' `id'
|
||
|
by `newby': replace `use'=0 if `expand'>1 & _n==2 /* row for by label */
|
||
|
by `newby': replace `use'=4 if `expand'>1 & _n==3 /* row for blank line */
|
||
|
by `newby': replace `use'=3 if `expand'>1 & _n==4 /* (if specified) row to hold subgp effect sizes */
|
||
|
|
||
|
* blank out effect sizes in new rows
|
||
|
replace `effect'=. if `expand'>1 & `use'!=1
|
||
|
replace `lci'=. if `expand'>1 & `use'!=1
|
||
|
replace `uci'=. if `expand'>1 & `use'!=1
|
||
|
replace `weight' =. if `expand'>1 & `use'!=1
|
||
|
replace `rawdata' ="." if `expand'>1 & `use'!=1
|
||
|
*Perform subgroup analyses
|
||
|
local j=1
|
||
|
while `j'<=`ngroups' {
|
||
|
if "`subgroup'"=="" {
|
||
|
*First ensure the by() category has any data
|
||
|
count if (`newby'==`j' & `use'==1)
|
||
|
if r(N)==0 {
|
||
|
*No data in subgroup=> fill variables with missing and move on
|
||
|
replace `effect'=. if (`use'==3 & `newby'==`j')
|
||
|
replace `lci'=. if (`use'==3 & `newby'==`j')
|
||
|
replace `uci'=. if (`use'==3 & `newby'==`j')
|
||
|
replace `wtdisp'=0 if `newby'==`j'
|
||
|
replace `weight'=0 if `newby'==`j'
|
||
|
replace `hetc'=. if `newby'==`j'
|
||
|
replace `hetdf'=. if `newby'==`j'
|
||
|
replace `hetp'=. if `newby'==`j'
|
||
|
replace `i2'=. if `newby'==`j'
|
||
|
replace `tsig'=. if `newby'==`j'
|
||
|
replace `psig'=. if `newby'==`j'
|
||
|
replace `tau2'=. if `newby'==`j'
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
`nextcall' `varlist' if (`newby'==`j' & `use'==1) , nograph /*
|
||
|
*/ notable label(`label') method(`method') `randomi' `wgt' `cornfield' `chi2' /*
|
||
|
*/ `cc' `standard' `log' `eform' `breslow'
|
||
|
replace `effect'=($S_1) if `use'==3 & `newby'==`j'
|
||
|
replace `lci'=($S_3) if `use'==3 & `newby'==`j'
|
||
|
replace `uci'=($S_4) if `use'==3 & `newby'==`j'
|
||
|
*Put within-subg weights in if nooverall or sgweight options specified
|
||
|
if ("`overall'`sgweight'"!="" ) {
|
||
|
replace `wtdisp'=_WT if `newby'==`j'
|
||
|
replace `wtdisp'=100 if (`use'==3 & `newby'==`j')
|
||
|
}
|
||
|
else {
|
||
|
qui sum `wtdisp' if (`use'==1 & `newby'==`j')
|
||
|
replace `wtdisp'=r(sum) if (`use'==3 & `newby'==`j')
|
||
|
}
|
||
|
sum `weight' if `newby'==`j'
|
||
|
replace `weight'= r(sum) if `use'==3 & `newby'==`j'
|
||
|
replace `hetc' =($S_7) if `use'==3 & `newby'==`j'
|
||
|
replace `hetdf'=($S_8) if `use'==3 & `newby'==`j'
|
||
|
replace `hetp' =($S_9) if `use'==3 & `newby'==`j'
|
||
|
replace `i2'=max(0, ( 100*($S_7-$S_8))/($S_7) ) if `use'==3 & `newby'==`j'
|
||
|
if $S_8<1 { replace `i2'=. if `use'==3 & `newby'==`j' }
|
||
|
if "`chi2'"!="" {
|
||
|
replace `tsig'=($S_10) if `use'==3 & `newby'==`j'
|
||
|
replace `psig'=($S_11) if `use'==3 & `newby'==`j'
|
||
|
}
|
||
|
else {
|
||
|
replace `tsig'=($S_5) if `use'==3 & `newby'==`j'
|
||
|
replace `psig'=($S_6) if `use'==3 & `newby'==`j'
|
||
|
}
|
||
|
if "`method'"=="D+L" {
|
||
|
replace `tau2' =($S_12) if `use'==3 & `newby'==`j'
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*Whether data or not - put cell counts in subtotal row if requested (will be 0/n1;0/n2 or blank if all use>1)
|
||
|
if "`counts'"!="" {
|
||
|
*don't put up anything for MDs:
|
||
|
*1 Cochrane just put up N_gi. Not sure whether weighted mean should be in..
|
||
|
*2 justifying N_gi is tedious!
|
||
|
if "`nextcall'"!="MD" {
|
||
|
sum `1' if (`use'==1 | `use'==2) & (`newby'==`j')
|
||
|
local sum1=r(sum)
|
||
|
sum `r1' if (`use'==1 | `use'==2) & (`newby'==`j')
|
||
|
local sum2=r(sum)
|
||
|
sum `3' if (`use'==1 | `use'==2) & (`newby'==`j')
|
||
|
local sum3=r(sum)
|
||
|
sum `r2' if (`use'==1 | `use'==2) & (`newby'==`j')
|
||
|
local sum4=r(sum)
|
||
|
replace `rawdata'= "`sum1'/`sum2';`sum3'/`sum4'" if (`use'==3 & `newby'==`j')
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
*Label attatched (if any) to byvar
|
||
|
local lbl: value label `by'
|
||
|
sum `by' if `newby'==`j'
|
||
|
local byvlu=r(mean)
|
||
|
if "`lbl'"=="" { local lab "`by'==`byvlu'" }
|
||
|
else { local lab: label `lbl' `byvlu' }
|
||
|
replace `label' = "`lab'" if ( `use'==0 & `newby'==`j')
|
||
|
replace `label' = "Subtotal" if ( `use'==3 & `newby'==`j')
|
||
|
local j=`j'+1
|
||
|
}
|
||
|
|
||
|
} /*End of quietly loop*/
|
||
|
|
||
|
|
||
|
*Put table up (if requested)
|
||
|
sort `newby' `use' `sortby' `id'
|
||
|
|
||
|
if "`table'"=="" {
|
||
|
qui gen str20 `tlabel'=`label'
|
||
|
if "`overall'`wt'"=="" {
|
||
|
local ww "% Weight"
|
||
|
}
|
||
|
di _n in gr _col(12) "Study" _col(22) "|" _col(24) "`log'" _col(28) "`sumstat'" /*
|
||
|
*/ _col(34) "[$IND% Conf. Interval]" _col(59) "`ww'"
|
||
|
di _dup(21) "-" "+" _dup(51) "-"
|
||
|
*legend for pooled confidence intervals
|
||
|
|
||
|
local i=1
|
||
|
while `i'<= _N {
|
||
|
if (`use'[`i'])==0 {
|
||
|
*by label
|
||
|
di _col(6) in gr `tlabel'[`i']
|
||
|
}
|
||
|
if "`overall'`wt'"=="" { local ww=`wtdisp'[`i'] }
|
||
|
else { local ww }
|
||
|
if (`use'[`i'])==1 {
|
||
|
*trial results
|
||
|
di in gr `tlabel'[`i'] _col(22) "| " in ye %7.3f `effect'[`i'] /*
|
||
|
*/ _col(35) %7.3f `lci'[`i'] " " %7.3f `uci'[`i'] _col(60) %6.2f `ww'
|
||
|
}
|
||
|
if (`use'[`i'])==2 {
|
||
|
*excluded trial
|
||
|
di in gr `tlabel'[`i'] _col(22) "| (Excluded)"
|
||
|
}
|
||
|
if ((`use'[`i']==3) & "`subgroup'"=="") | (`use'[`i']==5) {
|
||
|
*Subgroup effect size or overall effect size
|
||
|
if (`use'[`i'])==3 {
|
||
|
di in gr " Sub-total" _col(22) "|"
|
||
|
}
|
||
|
if (`use'[`i'])==5 {
|
||
|
if $IND!=$OVE {
|
||
|
local insert "[$OVE% Conf. Interval]"
|
||
|
}
|
||
|
di in gr "Overall" _col(22) "|" _col(34) "`insert'"
|
||
|
}
|
||
|
if "`ww'"=="." { local ww }
|
||
|
di in gr " `method' pooled `log'`sumstat'" _col(22) "| " in ye %7.3f /*
|
||
|
*/ `effect'[`i'] _col(35) %7.3f `lci'[`i'] " " %7.3f `uci'[`i'] _col(60) %6.2f `ww'
|
||
|
if (`use'[`i'])==5 {
|
||
|
di in gr _dup(21) "-" "+" _dup(51) "-"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (`use'[`i'])==4 {
|
||
|
*blank line separator (need to put line here in case nosubgroup was selected)
|
||
|
di in gr _dup(21) "-" "+" _dup(51) "-"
|
||
|
}
|
||
|
|
||
|
local i=`i'+1
|
||
|
|
||
|
}
|
||
|
*Skip next bits if nooverall AND nosubgroup
|
||
|
if ("`subgroup'"=="" | "`overall'"=="") {
|
||
|
|
||
|
*part 2: user defined weight notes and heterogeneity
|
||
|
if ("`method'"=="*" | "`var3'"!="") {
|
||
|
if "`method'"=="*" {
|
||
|
di in gr "* note: trials pooled by user defined weight `wgt'"
|
||
|
}
|
||
|
di in bl " Heterogeneity calculated by formula" _n /*
|
||
|
*/ " Q = SIGMA_i{ (1/variance_i)*(effect_i - effect_pooled)^2 } "
|
||
|
if "`var3'"!="" {
|
||
|
di in bl "where variance_i = ((upper limit - lower limit)/(2*z))^2 "
|
||
|
}
|
||
|
}
|
||
|
|
||
|
di in gr _n "Test(s) of heterogeneity:" _n _col(16) "Heterogeneity degrees of"
|
||
|
di in gr _col(18) "statistic freedom P I-squared**" _cont
|
||
|
if "`method'"=="D+L" { di in gr " Tau-squared" }
|
||
|
di
|
||
|
local i=1
|
||
|
while `i'<= _N {
|
||
|
if ("`subgroup'"=="" & (`use'[`i'])==0) | ( (`use'[`i'])==5) {
|
||
|
di in gr _n `tlabel'[`i'] _cont
|
||
|
}
|
||
|
if ( ((`use'[`i'])==3) | ((`use'[`i'])==5) ) {
|
||
|
|
||
|
di in ye _col(20) %6.2f `hetc'[`i'] _col(35) %2.0f `hetdf'[`i'] /*
|
||
|
*/ _col(43) %4.3f `hetp'[`i'] _col(51) %6.1f `i2'[`i'] "%" _cont
|
||
|
|
||
|
if "`method'"=="D+L" {
|
||
|
di in ye " " %7.4f `tau2'[`i'] _cont
|
||
|
}
|
||
|
|
||
|
if (`use'[`i']==5) & ("`subgroup'"=="") {
|
||
|
qui sum `hetc' if `use'==3
|
||
|
local btwghet = (`hetc'[`i']) -r(sum)
|
||
|
local df = `ngroups'-1
|
||
|
di _n in gr "Overall Test for heterogeneity between sub-groups : " _n /*
|
||
|
*/ in ye _col(20) %6.2f `btwghet' _col(35) %2.0f `df' _col(43) %4.3f (chiprob(`df',`btwghet'))
|
||
|
}
|
||
|
}
|
||
|
local i=`i'+1
|
||
|
}
|
||
|
di _n in gr "** I-squared: the variation in `sumstat' attributable to heterogeneity)" _n
|
||
|
|
||
|
*part 3: test statistics
|
||
|
di _n in gr "Significance test(s) of `log'`sumstat'=`h0'"
|
||
|
local i=1
|
||
|
while `i'<= _N {
|
||
|
if ("`subgroup'"=="" & (`use'[`i'])==0) | ( (`use'[`i'])==5) {
|
||
|
di _n in gr `tlabel'[`i'] _cont
|
||
|
}
|
||
|
if ( ((`use'[`i'])==3) | ((`use'[`i'])==5) ) {
|
||
|
|
||
|
if "`chi2'"!="" {
|
||
|
di in gr _col(20) "chi-squared = " in ye %5.2f `tsig'[`i'] /*
|
||
|
*/ in gr _col(35) " (d.f. = 1) p = " in ye %4.3f `psig'[`i'] _cont
|
||
|
}
|
||
|
else {
|
||
|
di in gr _col(23) "z= " in ye %5.2f `tsig'[`i'] _col(35) in gr /*
|
||
|
*/ " p = " in ye %4.3f `psig'[`i'] _cont
|
||
|
}
|
||
|
}
|
||
|
local i=`i'+1
|
||
|
}
|
||
|
di _n in gr _dup(73) "-"
|
||
|
}
|
||
|
|
||
|
} /* end of table display */
|
||
|
|
||
|
if "`overall'"=="" {
|
||
|
*need to return overall effect to $S_1 macros and so on...
|
||
|
global S_1=`effect'[_N]
|
||
|
global S_2=`se'[_N]
|
||
|
global S_3=`lci'[_N]
|
||
|
global S_4=`uci'[_N]
|
||
|
global S_7=`hetc'[_N]
|
||
|
global S_8=`hetdf'[_N]
|
||
|
global S_9=`hetp'[_N]
|
||
|
if "`chi2'"!="" {
|
||
|
global S_10=`tsig'[_N]
|
||
|
global S_11=`psig'[_N]
|
||
|
global S_5=`z'
|
||
|
global S_6=`pz'
|
||
|
}
|
||
|
else {
|
||
|
global S_5=`tsig'[_N]
|
||
|
global S_6=`psig'[_N]
|
||
|
global S_10=`echi2'
|
||
|
global S_11=`pchi2'
|
||
|
}
|
||
|
global S_12=`tau2'[_N]
|
||
|
if ("`sumstat'"=="OR" | "`sumstat'"=="RR" | "`sumstat'"=="RD" ) {
|
||
|
global S_13=`tger'
|
||
|
global S_14=`cger'
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
#delimit ;
|
||
|
global S_1 "."; global S_2 "."; global S_3 "."; global S_4 ".";
|
||
|
global S_5 "."; global S_6 "."; global S_7 "."; global S_8= `nostud';
|
||
|
global S_9 "."; global S_10 ".";global S_11 ".";global S_12 ".";
|
||
|
global S_13 ".";global S_14 ".";
|
||
|
#delimit cr
|
||
|
}
|
||
|
|
||
|
if "`graph'"=="" {
|
||
|
_dispgby `effect' `lci' `uci' `weight' `use' `label' `rawdata' `wtdisp', /*
|
||
|
*/ `log' `xlabel' `xtick' `force' sumstat(`sumstat') `saving' `box' t1("`t1'") /*
|
||
|
*/ t2("`t2'") b1("`b1'") b2("`b2'") `overall' `wt' `stats' `counts' `eform' /*
|
||
|
*/ `groupla' `cornfield'
|
||
|
}
|
||
|
|
||
|
restore
|
||
|
if "`keep'"=="" {
|
||
|
qui{
|
||
|
if ("`sumstat'"=="OR" | "`sumstat'"=="RR") & ("`log'"=="") { local ln "log"}
|
||
|
else { local ln }
|
||
|
cap drop _ES
|
||
|
cap drop _seES
|
||
|
cap drop _selogES
|
||
|
if "`sumstat'"!="ES" {
|
||
|
#delimit ;
|
||
|
replace _SS =. if `use'!=1; label var _SS "Sample size";
|
||
|
gen _ES =`effect';label var _ES "`log'`sumstat'";
|
||
|
gen _se`ln'ES=`se';label var _se`ln'ES "se(`ln'`log'`sumstat')";
|
||
|
#delimit cr
|
||
|
}
|
||
|
#delimit ;
|
||
|
cap drop _LCI ; cap drop _UCI; cap drop _WT;
|
||
|
gen _LCI =`lci'; label var _LCI "Lower CI (`log'`sumstat')";
|
||
|
gen _UCI =`uci'; label var _UCI "Upper CI (`log'`sumstat')";
|
||
|
#delimit cr
|
||
|
|
||
|
*correct weight if subgroup weights given
|
||
|
if ("`sgweight'"=="" & "`overall'"=="" ) { gen _WT=`weight' }
|
||
|
else if "`subgroup'"=="" & ("`overall'`sgweight'"!="" ) {
|
||
|
tempvar tempsum
|
||
|
by `by': gen `tempsum'=sum(`weight')
|
||
|
by `by': replace `tempsum'=`tempsum'[_N]
|
||
|
gen _WT=`weight'*100/`tempsum'
|
||
|
local sg "(subgroup) "
|
||
|
}
|
||
|
cap label var _WT "`method' `sg'% weight"
|
||
|
}
|
||
|
}
|
||
|
end
|
||
|
|
||
|
|
||
|
program define _dispgby
|
||
|
version 7.0
|
||
|
#delimit ;
|
||
|
syntax varlist(min=6 max=8 default=none ) [if] [in] [,
|
||
|
LOG XLAbel(string) XTICK(string) FORCE SAVING(string) noBOX SUMSTAT(string)
|
||
|
T1(string) T2(string) B1(string) B2(string) noOVERALL noWT noSTATS COUNTS EFORM
|
||
|
noGROUPLA CORNFIELD ];
|
||
|
#delimit cr
|
||
|
tempvar effect lci uci weight wtdisp use label tlabel id yrange xrange Ghsqrwt rawdata
|
||
|
parse "`varlist'", parse(" ")
|
||
|
qui {
|
||
|
gen `effect'=`1'
|
||
|
gen `lci' =`2'
|
||
|
gen `uci' =`3'
|
||
|
gen `weight'=`4'
|
||
|
gen byte `use'=`5'
|
||
|
*Use is now coded:
|
||
|
*0: blank line, except for text containing "by"
|
||
|
*1: trial
|
||
|
*2: excluded trial
|
||
|
*3. subgroup effect (in which case `label' or `6' is to contain the name of the subgroup)
|
||
|
*4. blank line
|
||
|
*5. overall effect
|
||
|
*9. missed/not considered
|
||
|
*As before effect sizes are held elsewhere
|
||
|
|
||
|
gen str10 `label'=""
|
||
|
replace `label'=`6'
|
||
|
count if (`use'==1 | `use'==2)
|
||
|
local ntrials=r(N)
|
||
|
count if (`use'>=0 & `use'<=5)
|
||
|
local ymax=r(N)
|
||
|
gen `id'=`ymax'-_n+1 if `use'<9
|
||
|
gen str40 `rawdata' = `7'
|
||
|
compress `rawdata'
|
||
|
if "`8'"!="" {
|
||
|
gen `wtdisp'=`8'
|
||
|
}
|
||
|
else {
|
||
|
gen `wtdisp'=`weight'
|
||
|
}
|
||
|
format `wtdisp' %5.1f
|
||
|
sum `lci'
|
||
|
local Gxlo=r(min) /* minimum of CIs*/
|
||
|
sum `uci'
|
||
|
local Gxhi=r(max) /* maximum of CIs*/
|
||
|
local h0=0
|
||
|
if (("`sumstat'"=="OR" | "`sumstat'"=="RR") & ("`log'"=="")) | ("`eform'"!="") {
|
||
|
local h0=1
|
||
|
local Glog "xlog"
|
||
|
local xlog "log"
|
||
|
local xexp "exp"
|
||
|
replace `lci'=1e-9 if `lci'<1e-8
|
||
|
replace `lci'=1e9 if `lci'>1e8 & `lci'!=.
|
||
|
replace `uci'=1e-9 if `uci'<1e-8
|
||
|
replace `uci'=1e9 if `uci'>1e8 & `uci'!=.
|
||
|
if `Gxlo'<1e-8 {local Gxlo=1e-8}
|
||
|
if `Gxhi'>1e8 {local Gxhi=1e8}
|
||
|
}
|
||
|
if "`cornfield'"!="" {
|
||
|
replace `lci'=`log'(1e-9) if ( (`lci'==. | `lci'==0) & (`effect'!=. & `use'==1) )
|
||
|
replace `uci'=`log'(1e9) if ( (`uci'==.) & (`effect'!=. & `use'==1) )
|
||
|
}
|
||
|
local flag1=0
|
||
|
if "`xtick'"!="" {
|
||
|
* capture inappropriate tick
|
||
|
cap assert ("`xlog'"=="" ) | /*
|
||
|
*/ ( ( min(`xtick' ,`Gxhi')>1e-8 ) & (max(`xtick' ,`Gxlo')<1e8) )
|
||
|
if _rc!=0 {
|
||
|
local flag1=10
|
||
|
local xtick "`h0'"
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
local xtick "`h0'"
|
||
|
}
|
||
|
if "`xlabel'"!="" {
|
||
|
* capture inappropriate label
|
||
|
cap {
|
||
|
assert ("`xlog'"=="" ) | /*
|
||
|
*/ ( ( min(`xlabel',`Gxhi')>1e-8 ) & (max(`xlabel',`Gxlo')<1e8) )
|
||
|
}
|
||
|
if _rc!=0 {
|
||
|
local flag1=10
|
||
|
local xlabel
|
||
|
}
|
||
|
else {
|
||
|
if "`force'"!="" {
|
||
|
parse "`xlabel'", parse(",")
|
||
|
if "`3'"!="" {
|
||
|
local Gxlo=`h0'
|
||
|
local Gxhi=`h0'
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if "`xlabel'"=="" | (`flag1'>1) {
|
||
|
local Gmodxhi=max( abs(`xlog'(`Gxlo')),abs(`xlog'(`Gxhi')))
|
||
|
if `Gmodxhi'==. {local Gmodxhi=2}
|
||
|
local Gxlo=`xexp'(-`Gmodxhi')
|
||
|
local Gxhi=`xexp'( `Gmodxhi')
|
||
|
local xlabel "`Gxlo',`h0',`Gxhi'"
|
||
|
}
|
||
|
local Gxlo=`xlog'(min(`xlabel',`xtick',`Gxlo'))
|
||
|
local Gxhi=`xlog'(max(`xlabel',`xtick',`Gxhi'))
|
||
|
|
||
|
local Gxlo1=`Gxlo'-0.1*`Gxhi'
|
||
|
local Gxrange=(`Gxhi'-`Gxlo')
|
||
|
local Gyhi=`id'[1]
|
||
|
|
||
|
local Gxh20=`Gxhi'+`Gxrange'*0.2
|
||
|
local Gxh40=`Gxhi'+`Gxrange'*0.4
|
||
|
local Gxh60=`Gxhi'+`Gxrange'*0.6
|
||
|
local Gxh80=`Gxhi'+`Gxrange'*0.8
|
||
|
local Gxh100=`Gxhi'+`Gxrange'*1.0
|
||
|
gen `xrange'=`xexp'(`Gxlo') in 1
|
||
|
|
||
|
*If user wants no counts (c), stats (s) or weights (w) , use entire window for trial plots
|
||
|
if ("`stats'"!="" & "`wt'"!="" & "`counts'"=="" ) {
|
||
|
local Txhi=`Gxhi'
|
||
|
}
|
||
|
*If user wants s&w (default) or c&w use Gxh60 (60% of graph range) for figures
|
||
|
if (("`stats'"=="" & "`wt'"=="" & "`counts'"=="" ) | ("`stats'"!="" & "`wt'"=="" & "`counts'"!="" )) {
|
||
|
local Txhi=`Gxh60'
|
||
|
}
|
||
|
*If user wants s or c alone use Gxh40 (40% of range)
|
||
|
if (("`stats'"=="" & "`wt'"!="" & "`counts'"=="" ) | ("`stats'"!="" & "`wt'"!="" & "`counts'"!="" )) {
|
||
|
local Txhi=`Gxh40'
|
||
|
}
|
||
|
*If user wants w alone use Gxh20
|
||
|
if ("`stats'"!="" & "`wt'"=="" & "`counts'"=="" ) {
|
||
|
local Txhi=`Gxh20'
|
||
|
}
|
||
|
*If user wants s&c use Gxh80
|
||
|
if ("`stats'"=="" & "`wt'"!="" & "`counts'"!="" ) {
|
||
|
local Txhi=`Gxh80'
|
||
|
}
|
||
|
*If user wants all 3 use Gxh100
|
||
|
if ("`stats'"=="" & "`wt'"=="" & "`counts'"!="" ) {
|
||
|
local Txhi=`Gxh100'
|
||
|
}
|
||
|
replace `xrange'=`xexp'(`Txhi') in 2
|
||
|
|
||
|
gen `yrange'=0 in 1
|
||
|
replace `yrange'=`Gyhi'+2 in 2
|
||
|
cap label drop tmpyl
|
||
|
cap label drop tmpxl
|
||
|
* Study legend now removed
|
||
|
label define tmpyl 0 " "
|
||
|
label define tmpxl `h0' " "
|
||
|
|
||
|
label values `yrange' tmpyl
|
||
|
label values `xrange' tmpxl
|
||
|
*Label x-axis and top right *if stats requested
|
||
|
if "`sumstat'"=="OR" {local sscale "`log' Odds ratio"}
|
||
|
else if "`sumstat'"=="RR" {local sscale "`log' Risk ratio"}
|
||
|
else if "`sumstat'"=="RD" {local sscale "Risk difference"}
|
||
|
else if "`sumstat'"=="WMD" {local sscale "Mean difference"}
|
||
|
else if "`sumstat'"=="SMD" {local sscale "Standardised mean difference"}
|
||
|
else if ("`sumstat'"=="ES" & "$MA_ESLA"!="") { local sscale "$MA_ESLA" }
|
||
|
else if "`sumstat'"=="ES" {local sscale "Effect size"}
|
||
|
|
||
|
if "`t1'"=="." {local t1 }
|
||
|
else {
|
||
|
local t1=substr("`t1'",2,.)
|
||
|
local t1 "t1(`t1')"
|
||
|
}
|
||
|
if "`t2'"=="." {local t2 }
|
||
|
else {
|
||
|
local t2=substr("`t2'",2,.)
|
||
|
local t2 "t2(`t2')"
|
||
|
}
|
||
|
if "`b2'"=="." { local b2 "`sscale'"}
|
||
|
else {
|
||
|
*Revise position of b2 title: graph command doesn't put it in the right place
|
||
|
local b2=substr("`b2'",2,.)
|
||
|
}
|
||
|
if "`saving'"!="" {local saving "saving(`saving')" }
|
||
|
else { local saving }
|
||
|
gph open, `saving'
|
||
|
|
||
|
if "`b1'"=="." {
|
||
|
graph `yrange' `xrange', s(i) xli(`h0') `Glog' xlabel(`h0') /*
|
||
|
*/ noaxis yla(0) gap(10) `t1' `t2' b2(" ")
|
||
|
}
|
||
|
else {
|
||
|
graph `yrange' `xrange', s(i) xli(`h0') `Glog' xlabel(`h0') /*
|
||
|
*/ noaxis yla(0) gap(10) `t1' `t2' b1(" ") b2(" ")
|
||
|
}
|
||
|
|
||
|
|
||
|
local r5=r(ay)
|
||
|
local r6=r(by)
|
||
|
local r7=r(ax)
|
||
|
local r8=r(bx)
|
||
|
local Aytexs=($MA_FTSI)*max(200, min(700, (600-20*(`ymax'-15)) ) )
|
||
|
local Axtexs=($MA_FTSI)*max(130, min(360,(0.6*`Aytexs')) )
|
||
|
|
||
|
gph font `Aytexs' `Axtexs'
|
||
|
gph pen 1
|
||
|
local Axh0 =`r7'*(`xlog'(`h0'))+`r8'
|
||
|
local Axlo =`r7'*(`Gxlo') +`r8'
|
||
|
local Axloe =`r7'*(`Gxlo') +`r8' -1500
|
||
|
local Axhi =`r7'*(`Gxhi') +`r8'
|
||
|
local Axhie =`r7'*(`Gxhi') +`r8' + 1500*( ("`wt'"=="") | ("`stats'"=="") | ("`counts'"!="") )
|
||
|
local Axh20 =`r7'*(`Gxh20') +`r8'
|
||
|
local Axh40 =`r7'*(`Gxh40') +`r8'
|
||
|
local Axh60 =`r7'*(`Gxh60') +`r8'
|
||
|
local Axh80 =`r7'*(`Gxh80') +`r8'
|
||
|
local Axh100=`r7'*(`Gxh100')+`r8'
|
||
|
*add x-axis line and label manually
|
||
|
|
||
|
gph line `r6' `Axloe' `r6' `Axhie'
|
||
|
local yb=`r6'+1000
|
||
|
gph line `r6' `Axh0' `yb' `Axh0'
|
||
|
local yb=`r6'+2400+max(0,(1000-100*`ymax'))
|
||
|
gph text `yb' `Axh0' 0 0 `b2'
|
||
|
if "`b1'"!="." {
|
||
|
local yb=`r6'+3000+max(0,(1200-100*`ymax'))
|
||
|
local b1=substr("`b1'",2,.)
|
||
|
if substr("`b1'",1,3)=="*I:" {
|
||
|
*"Favours ..." labels
|
||
|
local flab= substr("`b1'",4,.)
|
||
|
tokenize "`flab'" , parse(*)
|
||
|
if "`1'"!="" {
|
||
|
local xt=`Axh0'-1000
|
||
|
gph text `yb' `xt' 0 1 `1'
|
||
|
}
|
||
|
if "`3'"!="" {
|
||
|
local xt=`Axh0'+1000
|
||
|
gph text `yb' `xt' 0 -1 `3'
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
gph text `yb' `Axh0' 0 0 `b1'
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
*add xtick & xlabel manually
|
||
|
tokenize "`xlabel'", parse(,)
|
||
|
while "`1'"!="" {
|
||
|
local x=`1'
|
||
|
if (`x'>10e8) { local x="10e8" }
|
||
|
if ((`x'<=-10e8) & (`h0'==0)) { local x="-10e8" }
|
||
|
local Ax=`r7'*(`xlog'(`x')) +`r8'
|
||
|
local Ayh=`r6'
|
||
|
local Ayl=`r6'+400
|
||
|
local Ayt=`r6'+1700
|
||
|
gph line `Ayh' `Ax' `Ayl' `Ax'
|
||
|
if ((`x'<10e-6) & (`h0'==1)) { local x="0.00000" }
|
||
|
if ((`x'<10e4) & (`x'>-10e4)) { local x=substr("`x'",1,7) }
|
||
|
gph text `Ayt' `Ax' 0 0 `x'
|
||
|
mac shift 2
|
||
|
}
|
||
|
|
||
|
tokenize "`xtick'", parse(,)
|
||
|
while "`1'"!="" {
|
||
|
local Ax=`r7'*(`xlog'(`1')) + `r8'
|
||
|
local Ayh=`r6'
|
||
|
local Ayl=`r6'+400
|
||
|
local Ayt=`r6'+1000
|
||
|
gph line `Ayh' `Ax' `Ayl' `Ax'
|
||
|
mac shift 2
|
||
|
}
|
||
|
|
||
|
*Add legend
|
||
|
local Ayhi =(`Gyhi'+2)*`r5'+`r6'
|
||
|
local Axtexh=50+max(0,min(2400,`ymax'*60-700))
|
||
|
local Ayhead=`r5'*(`ymax'+1) + `r6'
|
||
|
|
||
|
gph text `Ayhead' `Axtexh' 0 -1 $S_TX
|
||
|
|
||
|
|
||
|
/*Order of text: stats, counts, weights (where selected)*/
|
||
|
*New positionings reduce gaps between stats/weight/counts
|
||
|
|
||
|
if ("`stats'"=="") {
|
||
|
*Align stats first at Axhi. Use prev line if `sscale' too long (>12chrs)
|
||
|
* local lenssc=length("`sscale'")
|
||
|
* if `lenssc'<13 {
|
||
|
* gph text `Ayhead' `Axhi' 0 -1 `sscale' ($IND% CI)
|
||
|
* }
|
||
|
* else {
|
||
|
local Ayhead2=`r5'*(`ymax'+1.65) + `r6'
|
||
|
gph text `Ayhead2' `Axhi' 0 -1 `sscale'
|
||
|
gph text `Ayhead' `Axhi' 0 -1 ($IND% CI)
|
||
|
* }
|
||
|
}
|
||
|
if ("`counts'"!="") {
|
||
|
*Align counts 2nd if both stats & weight
|
||
|
if ("`stats'"=="") {
|
||
|
local Ax2hi1=`Axh20'+(`Axh60'-`Axh20')*(max(0.4, min(1,2-0.07*`ymax') ) )
|
||
|
local Ax2hi2=`Axh20'+(`Axh80'-`Axh20')*(max(0.4, min(1,2-0.07*`ymax') ) )
|
||
|
*local Ax2hi1=`Axh60'
|
||
|
*local Ax2hi2=`Axh80'
|
||
|
}
|
||
|
else {
|
||
|
local Ax2hi1=`Axhi'+(`Axh20'-`Axhi')*(max(0.4, min(1,2-0.07*`ymax') ) )
|
||
|
local Ax2hi2=`Axhi'+(`Axh40'-`Axhi')*(max(0.4, min(1,2-0.07*`ymax') ) )
|
||
|
*local Ax2hi1=`Axh20'
|
||
|
*local Ax2hi2=`Axh40'
|
||
|
}
|
||
|
local yt=`Ayhead'-max(200, min(1000,1600-40*`ymax'))
|
||
|
gph text `yt' `Ax2hi1' 0 0 No. of events
|
||
|
* gph font 500 270
|
||
|
gph text `Ayhead' `Ax2hi1' 0 1 $MA_G1L
|
||
|
gph text `Ayhead' `Ax2hi2' 0 1 $MA_G2L
|
||
|
}
|
||
|
if ("`wt'"=="") {
|
||
|
if ("`stats'"=="" & "`counts'"!="") {
|
||
|
*local Ax3hi=`Axh100'
|
||
|
local Ax3hi=`Axh20'+(`Axh100'-`Axh20')*(max(0.4, min(1,2-0.07*`ymax') ) )
|
||
|
}
|
||
|
else if ("`stats'"!="" & "`counts'"=="") {
|
||
|
local Ax3hi=`Axh20'
|
||
|
}
|
||
|
else {
|
||
|
*local Ax3hi=`Axh60'
|
||
|
local Ax3hi=`Axh20'+(`Axh60'-`Axh20')*(max(0.4, min(1,2-0.07*`ymax') ) )
|
||
|
}
|
||
|
gph text `Ayhead' `Ax3hi' 0 1 % Weight
|
||
|
}
|
||
|
gen `Ghsqrwt'=0.5*sqrt(`weight')/2
|
||
|
local flag=0
|
||
|
while `flag'<1 {
|
||
|
cap assert `Ghsqrwt'<0.5 if (`use'==1)
|
||
|
if _rc!=0 { replace `Ghsqrwt'=0.9*`Ghsqrwt' }
|
||
|
else { local flag=10}
|
||
|
}
|
||
|
replace `Ghsqrwt'=($MA_FBSC)*`Ghsqrwt'
|
||
|
local flag2=0
|
||
|
local flag3=0
|
||
|
local i=1
|
||
|
gph pen 2
|
||
|
|
||
|
while `i'<=`ymax' {
|
||
|
local Aytcen= `r5'*(`id'[`i']-0.2)+`r6' /* text label centre */
|
||
|
local Aygcen= `r5'*(`id'[`i'])+`r6' /* graphics label centre */
|
||
|
|
||
|
|
||
|
*label to put on left hand
|
||
|
local tx = `label'[`i']
|
||
|
local Axtexs=`Axtexh'+400
|
||
|
|
||
|
*use=0 => blank line except for "by" legend
|
||
|
if (`use'[`i']==0) {
|
||
|
gph pen 3
|
||
|
gph text `Aytcen' `Axtexh' 0 -1 `tx'
|
||
|
}
|
||
|
|
||
|
*use=1 => individual trial
|
||
|
if `use'[`i']==1 {
|
||
|
gph pen 2
|
||
|
gph text `Aytcen' `Axtexs' 0 -1 `tx'
|
||
|
if `lci'[`i']==. | `uci'[`i']==. { local flag2=10 }
|
||
|
else {
|
||
|
* Define lower/upper points on x-line, and centre on y-axis
|
||
|
local Axlpt= `r7'*(`xlog'( `lci'[`i'] ))+`r8'
|
||
|
local Axupt= `r7'*(`xlog'( `uci'[`i'] ))+`r8'
|
||
|
if (`Axupt' < `Axlo') | (`Axlpt' > `Axhi') {
|
||
|
* If CI is totally off scale draw (triangular) arrow
|
||
|
local Ayco1=`r5'*(`id'[`i']-0.2)+`r6'
|
||
|
local Ayco2=`r5'*(`id'[`i']+0.2)+`r6'
|
||
|
if `Axupt'<=`Axlo' {
|
||
|
local Axlpt =`Axlo'
|
||
|
local Axco1=`r7'*(`Gxlo')+`r8'
|
||
|
local Axco2=`r7'*(`Gxlo')+`r8'+450
|
||
|
}
|
||
|
if `Axlpt'>=`Axhi' {
|
||
|
local Axupt =`Axhi'
|
||
|
local Axco1=`r7'*(`Gxhi')+`r8'
|
||
|
local Axco2=`r7'*(`Gxhi')+`r8'-450
|
||
|
}
|
||
|
gph line `Aygcen' `Axco1' `Ayco2' `Axco2'
|
||
|
gph line `Ayco2' `Axco2' `Ayco1' `Axco2'
|
||
|
gph line `Ayco1' `Axco2' `Aygcen' `Axco1'
|
||
|
}
|
||
|
else {
|
||
|
local Axcen =`r7'*`xlog'(`effect'[`i'])+`r8'
|
||
|
* Define box size
|
||
|
local Ahboxl =abs(`r5'*( `Ghsqrwt'[`i'] ))
|
||
|
local Ay1cord=`Aygcen'+`Ahboxl'
|
||
|
local Ax1cord=`Axcen' -`Ahboxl'
|
||
|
local Ay2cord=`Aygcen'-`Ahboxl'
|
||
|
local Ax2cord=`Axcen' +`Ahboxl'
|
||
|
if (`Axlpt' < `Axlo') | (`Axupt' > `Axhi') {
|
||
|
* CI is on but not totaly on scale: draw arrow at end of CI
|
||
|
local Ayco1=`r5'*(`id'[`i']-0.1)+`r6'
|
||
|
local Ayco2=`r5'*(`id'[`i']+0.1)+`r6'
|
||
|
if `Axlpt' < `Axlo' {
|
||
|
local Axlpt =`Axlo'
|
||
|
local Axco1=`r7'*(`Gxlo')+`r8'
|
||
|
local Axco2=`r7'*(`Gxlo')+`r8'+350
|
||
|
gph line `Aygcen' `Axco1' `Ayco1' `Axco2'
|
||
|
gph line `Aygcen' `Axco1' `Ayco2' `Axco2'
|
||
|
}
|
||
|
if `Axupt' > `Axhi' {
|
||
|
local Axupt =`Axhi'
|
||
|
local Axco1=`r7'*(`Gxhi')+`r8'
|
||
|
local Axco2=`r7'*(`Gxhi')+`r8'-350
|
||
|
gph line `Aygcen' `Axco1' `Ayco1' `Axco2'
|
||
|
gph line `Aygcen' `Axco1' `Ayco2' `Axco2'
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*draw line for CI
|
||
|
gph line `Aygcen' `Axlpt' `Aygcen' `Axupt'
|
||
|
*either draw box for ES/weight...
|
||
|
if "`box'"=="" {
|
||
|
if (`Ax1cord' >=`Axlo') & (`Ax2cord'<=`Axhi') {
|
||
|
gph box `Ay1cord' `Ax1cord' `Ay2cord' `Ax2cord' $MA_FBSH
|
||
|
}
|
||
|
else {local flag2=10}
|
||
|
}
|
||
|
*...or simply plot ES (as circle) instead of box
|
||
|
if "`box'"!="" {
|
||
|
if (`Ax1cord' >=`Axlo') & (`Ax2cord'<=`Axhi') {
|
||
|
local ptsize=250*$MA_FBSC
|
||
|
gph point `Aygcen' `Axcen' `ptsize' 1
|
||
|
}
|
||
|
else {local flag2=10}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*use=2 => Excluded trial
|
||
|
if (`use'[`i']==2) {
|
||
|
gph text `Aytcen' `Axtexs' 0 -1 `tx'
|
||
|
}
|
||
|
*use=4 => blank line: no text or graphic necessary (above displays nothing)
|
||
|
|
||
|
*use=3 => subgroup effect size (display by default), or...
|
||
|
*use=5 => overall effect size (display by default)
|
||
|
|
||
|
if ( ((`use'[`i']==3) & ("`subgrp'"=="")) | /*
|
||
|
*/ ((`use'[`i']==5) & ("`overall'"=="")) ) {
|
||
|
|
||
|
if (`use'[`i']==3) { gph pen 3 }
|
||
|
if (`use'[`i']==5) { gph pen 5 }
|
||
|
gph text `Aytcen' `Axtexh' 0 -1 `tx'
|
||
|
local Aycol=`r5'*((`id'[`i'])-0.2)+`r6'
|
||
|
local Aycoh= `r5'*((`id'[`i'])+0.2)+`r6'
|
||
|
*the following 4 are necc in case diamond is chopped off
|
||
|
local Aycenl1=`Aygcen'
|
||
|
local Aycenl2=`Aygcen'
|
||
|
local Aycenh1=`Aygcen'
|
||
|
local Aycenh2=`Aygcen'
|
||
|
local Axcol=`r7'*(`xlog'(`lci'[`i']))+`r8'
|
||
|
local Axcen=`r7'*(`xlog'(`effect'[`i']))+`r8'
|
||
|
local Axcoh=`r7'*(`xlog'(`uci'[`i']))+`r8'
|
||
|
if (`Axcen'<`Axlo') | (`Axcen'>`Axhi') {
|
||
|
*diamond is off the scale!
|
||
|
local flag3=10
|
||
|
}
|
||
|
else {
|
||
|
* phi is angle between diamond slope and y=id in right angle triangle; use this
|
||
|
* fact to get y where diamond is chopped off at
|
||
|
if `Axcol'<`Axlo' {
|
||
|
local flag3=10
|
||
|
local tanphi=(0.2*`r5')/(`Axcen'-`Axcol')
|
||
|
local Aydiff=(`Axlo'-`Axcol')*`tanphi'
|
||
|
local Aycenl1=`Aygcen'-`Aydiff'
|
||
|
local Aycenl2=`Aygcen'+`Aydiff'
|
||
|
local Axcol=`Axlo'
|
||
|
gph line `Aycenl1' `Axcol' `Aycenl2' `Axcol'
|
||
|
}
|
||
|
if `Axcoh'>`Axhi' {
|
||
|
local flag3=10
|
||
|
local tanphi=(0.2*`r5')/(`Axcoh'-`Axcen')
|
||
|
local Aydiff=(`Axcoh'-`Axhi')*`tanphi'
|
||
|
local Aycenh1=`Aygcen'-`Aydiff'
|
||
|
local Aycenh2=`Aygcen'+`Aydiff'
|
||
|
local Axcoh=`Axhi'
|
||
|
gph line `Aycenh1' `Axcoh' `Aycenh2' `Axcoh'
|
||
|
}
|
||
|
gph line `Aycoh' `Axcen' `Aycenh2' `Axcoh'
|
||
|
gph line `Aycenh1' `Axcoh' `Aycol' `Axcen'
|
||
|
gph line `Aycol' `Axcen' `Aycenl1' `Axcol'
|
||
|
gph line `Aycenl2' `Axcol' `Aycoh' `Axcen'
|
||
|
*Overall line (if specified)
|
||
|
if ((`use'[`i'])==5 & "`line'"=="" ) {
|
||
|
gph pen 5
|
||
|
local Adashl=`r5'*(`Gyhi'-1)/100
|
||
|
local Ayhi =`r5'*`Gyhi'+`r6'
|
||
|
local j =`r5'+`r6'
|
||
|
while `j'>`Ayhi' {
|
||
|
local Aycol=`j'
|
||
|
local Aycoh=`j'+`Adashl'
|
||
|
gph line `Aycol' `Axcen' `Aycoh' `Axcen'
|
||
|
local j=`j'+2*`Adashl'
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
*use=9 => excluded, ignore (will have been sorted to bottom of data)
|
||
|
|
||
|
*Diamonds or boxes&lines are now drawn - put text on the end
|
||
|
|
||
|
if ( `use'[`i']==1 | `use'[`i']==3 | `use'[`i']==5 ) {
|
||
|
*put text at end of graph (if requested)
|
||
|
if "`stats'"=="" {
|
||
|
*effect sizes
|
||
|
local e1=`effect'[`i']
|
||
|
local e2=`lci'[`i']
|
||
|
local e3=`uci'[`i']
|
||
|
*Make allowance for alignment where es<0
|
||
|
local sp1
|
||
|
if (`e1'>0) {local sp1 " "}
|
||
|
local sp2
|
||
|
if (`e2'>0) {local sp2 " "}
|
||
|
local sp3
|
||
|
if (`e3'>0) {local sp3 " "}
|
||
|
if (`e1'<1e-8) & "`Glog'"!="" {local e1 "<10^-8"}
|
||
|
else if (`e1'>1e8) & (`e1'!=.) & "`Glog'"!="" {
|
||
|
local e1 ">10^8"
|
||
|
}
|
||
|
else { local e1: displ %4.2f `e1' }
|
||
|
if (`e2'<1e-8) & ("`Glog'"!="" | "`cornfield'"!="") {
|
||
|
local e2 "<10^-8"
|
||
|
}
|
||
|
else if (`e2'>1e8) & (`e2'!=.) & "`Glog'"!="" {local e2 ">10^8"}
|
||
|
else { local e2: displ %4.2f `e2' }
|
||
|
if (`e3'<1e-8) & "`Glog'"!="" {local e3 "<10^-8"}
|
||
|
else if (`e3'>1e8) & (`e3'!=.) & ("`Glog'"!="" | "`cornfield'"!="") {
|
||
|
local e3 ">10^8"
|
||
|
}
|
||
|
else { local e3: displ %4.2f `e3' }
|
||
|
|
||
|
local esize "`sp1'`e1' (`sp2'`e2',`sp3'`e3')"
|
||
|
gph text `Aytcen' `Axhi' 0 -1 `esize'
|
||
|
}
|
||
|
if "`wt'"=="" {
|
||
|
local weit: displ %4.1f `wtdisp'[`i']
|
||
|
if `weit'!=. { gph text `Aytcen' `Ax3hi' 0 1 `weit' }
|
||
|
}
|
||
|
if "`counts'"!="" {
|
||
|
local nm: displ `rawdata'[`i']
|
||
|
if "`nm'"!="." {
|
||
|
parse "`nm'" , parse(";")
|
||
|
local nm1 "`1'"
|
||
|
local nm2 "`3'"
|
||
|
gph text `Aytcen' `Ax2hi1' 0 1 `nm1'
|
||
|
gph text `Aytcen' `Ax2hi2' 0 1 `nm2'
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if `use'[`i']==2 & "`stats'"=="" {
|
||
|
gph text `Aytcen' `Axhi' 0 -1 (Excluded)
|
||
|
if "`counts'"!="" {
|
||
|
local nm: displ `rawdata'[`i']
|
||
|
if "`nm'"!="." {
|
||
|
parse "`nm'" , parse(";")
|
||
|
local nm1 "`1'"
|
||
|
local nm2 "`3'"
|
||
|
gph text `Aytcen' `Ax2hi1' 0 1 `nm1'
|
||
|
gph text `Aytcen' `Ax2hi2' 0 1 `nm2'
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
local i=`i'+1
|
||
|
}
|
||
|
|
||
|
|
||
|
gph close
|
||
|
} /* end of qui section*/
|
||
|
#delimit ;
|
||
|
if `flag1'>1 { di in bl _n "Note: invalid xlabel() or xtick(): graph has been rescaled"};
|
||
|
if `flag2'>1 { di in bl _n "Warning: Some trials cannot be
|
||
|
represented graphically (may contain" _n "inestimable effects). Consider using different xlabel()"};
|
||
|
if `flag3'>1 { di in bl _n "Warning: Overall (or subgroup) effect size not fully
|
||
|
represented graphically." _n "Consider using xlabel()"};
|
||
|
#delimit cr
|
||
|
end
|
||
|
|
||
|
exit
|
||
|
|
||
|
Revision history
|
||
|
*Log option added: can display Log(ORs,RRs) on table & graph.
|
||
|
*graph appears when only 1 trial.
|
||
|
*nomenclature for _ES changed when OR or RR selected
|
||
|
*std err of estimate /log est in saved results is renamed and labelled so is clearer
|
||
|
*can handle 2 variables (ES, seES), exponentiated with new eform option
|
||
|
*Max label length for table is now 20chrs
|
||
|
*Breslow Day test for OR heterogeneity added
|
||
|
*Can display n/N for trials, with and without weights/stats.
|
||
|
*Groups can be labelled when using the counts option with group1() and group2()
|
||
|
*User-defined weight option:
|
||
|
* - can't use with fixed or random option
|
||
|
* - metan ... , wgt(wt) == metan ... , fixedi if wt=1/v ie IV weighting
|
||
|
* - however will differ if RE:
|
||
|
* - no tau2 involved in weights ie variance est differs: wgt=1/(vi+t) but SIGMA{1/wgt}=/= SIGMA{wi^2 * var(thetai) }/[SIGMA(wi)]^2
|
||
|
* - pooled ES=SIGMA{wgt*es}/SIGMA{wgt} =/= FE weighted, so heterogeneity not equal
|
||
|
*Can plot the ES alone without the box (nobox)
|
||
|
*Option to change "Study" label to something else
|
||
|
*Display a bit nicer (table formatted 3 rather than up to 7 dec places)
|
||
|
*Removed "Study -" from top; by default is replaced with "Study". Annoying "tick" next to it removed
|
||
|
*Added I^2 (Higgins & Thompson 2003)
|
||
|
*xtick, b2title options added can add "Favours ..." below graph with prefix and asterisk in b1(*I: .... * ....)
|
||
|
*Nointeger: allows sample size to be non-integer if requested.
|
||
|
*Adding continuity correction: currently allows user-defined const (or via nointeger opt)
|
||
|
*r(sumstat) now displays with S/WMD (did forget to was long standing bug)
|
||
|
*by() option added!!!
|
||
|
*r(tger) and r(cger) corrected from prev by() versions; overall ERs were not reported (were from the last by subg)
|
||
|
*bug with by() removed: wasn't keeping _ES etc
|
||
|
*allows CI syntax (3 variables: theta lower_limit upper_limit), BUT:
|
||
|
* - does not follow "meta" syntax, ie does not assume log transform needed
|
||
|
* - default is to assume symmetry and calculate se=0.5*(upper-lower)/z, but allows asymmetry
|
||
|
* - bug with udw and missing data fixed 1Mar04
|
||
|
*minor bugs fixed: trial labelling with by() on table, extended line to right of graph with
|
||
|
*options nostats nowt counts all specified, sortby w/o by() option, junk text if by() subgroup
|
||
|
*contained no informative trials, erroneous lack of overall summary on graph in some situations.
|
||
|
|