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.

4455 lines
125 KiB
Plaintext

*! 3.02 13Nov2008
* 13 Nov 2008- fixed counts option, was using str20 and truncating in `rawdata'
* decimal place option dp()
* Based on 2.34 11May2007
* Based on previous 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 metan, rclass
version 9.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) noBOX
XLAbel(passthru) XTick(passthru) FORCE BOXSCA(real 100.0) BOXSHA(integer 4)
TEXTSize(real 100.0) noWT noSTATS COUNTS WGT(varlist numeric max=1)
GROUP1(string) GROUP2(string) EFFECT(string)
/* new additions */
LCOLS(varlist) RCOLS(varlist) ASTEXT(integer 50) DOUBLE NOHET NULL(real 999) RFDIST SUMMARYONLY
SECOND(string) NOSECSUB SECONDSTATS(string) FAVOURS(string) FIRST(string) FIRSTSTATS(string)
BOXOPT(string) DIAMOPT(string) POINTOPT(string) CIOPT(string) OLINEOPT(string)
CLASSIC NOWARNING NULLOFF EFFICACY RFLevel(integer $S_level) DP(integer 2)
* ];
#delimit cr
global MA_OTHEROPTS `"`options'"'
global MA_BOXOPT `"`boxopt'"'
global MA_DIAMOPT `"`diamopt'"'
global MA_POINTOPT `"`pointopt'"'
global MA_CIOPT `"`ciopt'"'
global MA_OLINEOPT `"`olineopt'"'
global MA_DOUBLE "`double'"
global MA_FAVOURS "`favours'"
global MA_secondstats "`secondstats'"
global MA_firststats "`firststats'"
global MA_nohet "`nohet'"
global MA_rfdist "`rfdist'"
global MA_summaryonly "`summaryonly'"
global MA_classic "`classic'"
global MA_nowarning "`nowarning'"
global MA_nulloff "`nulloff'"
global MA_efficacy "`efficacy'"
global MA_dp = "`dp'"
if `null' != 999{
global MA_NULL = "`null'"
}
else{
global MA_NULL = ""
}
global MA_FBSC `boxsca'
global MA_ESLA "`effect'"
global MA_params = 0 // set as appropriate in variable set-up
if "`legend'"!="" {
global S_TX "`legend'"
}
else {
global S_TX "Study"
}
global MA_AS_TEXT `astext' // new option- percentage of graph as text
global MA_TEXT_SCA `textsize' // oops, that was in already
if `astext' > 90 | `astext' < 10 {
di as error "Percentage of graph as text (ASTEXT) must be within 10-90%"
di as error "Must have some space for text and graph"
exit
}
if `textsize' < 20 | `textsize' > 500 {
di as error "Text scale (TEXTSize) must be within 20-500"
di as error "Value is character size relative to graph"
di as error "Outside range will either be unreadable or too large"
exit
}
*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'"
}
global MA_FTSI `textsize'
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'
if `rflevel'<1 {
local rflevel `rflevel'*100
}
if `rflevel'>99 | `olevel'<10 {
local rflevel $S_level
}
global RFL `rflevel'
forvalues i = 1/12{
global S_`i' .
}
global MA_rjhby "`by'"
*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'"!="" {
tokenize "`label'", parse("=,")
while "`1'"!="" {
cap confirm var `3'
if _rc!=0 {
di as err "Variable `3' not defined"
exit
}
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')
}
}
}
if "`namevar'"=="" & "`lcols'" != ""{
local var1 = word("`lcols'",1)
cap confirm var `var1'
if _rc!=0 {
di in re "Variable `var1' not defined"
exit _rc
}
local namevar "`var1'"
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')
}
}
}
if "`namevar'"=="" & "`lcols'" == ""{
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 as err "Option invalid with user-defined weights"
exit
}
confirm numeric variable `wgt'
local wgt "wgt(`wgt')"
}
} /* End of quietly loop */
tokenize "`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
}
global MA_params = 3
}
else{
global MA_params = 2
}
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 as err "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 as err "Specify fixed or random effect/s model"
exit _rc
}
}
cap assert "`cc'"==""
if _rc!=0 {
di as err "Continuity correction not valid with unless individual counts specified"
exit _rc
}
local callalg "iv_init"
local sumstat "ES"
} /*end of 2&3-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 as err "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 as err "Non integer cell counts found"
exit _rc
}
}
cap assert ( (`1'>=0) & (`2'>=0) & (`3'>=0) & (`4'>=0) )
if _rc!=0 {
di as err "Non-positive cell counts found"
exit _rc
}
if "`cc'"!="" {
*Ensure Continuity correction is valid
if "`peto'"!="" {
di as err "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 as err "Invalid continuity correction: specify a constant number eg metan ... , cc(0.166667)"
exit
}
cap assert (`cc'>=0) & (`cc'<1)
if _rc!=0 {
di as err "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 as err "Invalid specifications for combining trials"
exit
}
*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 as err "Chi-squared option invalid for `method' `sumstat'"
exit
}
if ("`sumstat'"!="OR" | "`method'"=="D+L" | "`method'"=="Peto" ) & "`breslow'"!="" {
di as err "Breslow-Day heterogeneity option not available for `method' `sumstat'"
exit
}
if ("`sumstat'"!="OR" & "`sumstat'"!="RR") & "`log'"!="" {
di as err "Log option not appropriate for `sumstat'"
exit
}
if "`keep'"=="" {
cap drop _SS
qui gen _SS =`1'+`2'+`3'+`4'
}
global MA_params = 4
} /* end of binary variable setup */
} /* end of all non-6 variable set up */
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 as err "Wrong number of variables specified"
exit _rc
}
if "`integer'"=="" {
cap assert ((int(`1')==`1') & (int(`4')==`4'))
if _rc!=0 {
di as err "Non integer sample sizes found"
exit _rc
}
}
cap assert (`1'>0 & `4'>0)
if _rc!=0 {
di as err "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 as err "Invalid specifications for combining trials"
exit
}
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"
}
/* CAN NOW HAVE THIS
if "`counts'"!="" {
di in bl "Data option counts not available with continuous data"
local counts
}
*/
if "`cc'"!="" {
di as err "Continuity correction not available with continuous data"
exit
}
local callalg "MD"
if "`keep'"=="" {
cap drop _SS
qui gen _SS =`1'+`4'
}
global MA_params = 6
} /*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')"
}
if "$MA_efficacy" != ""{
cap assert "`sumstat'" == "RR" | "`sumstat'" == "OR"
if _rc!=0 {
di in red "Efficacy statistics only possible with odds ratios and risk ratios"
exit _rc
}
}
// RJH- code for second effect estimate
global MA_method1 ""
global MA_method2 "" // MAKE BLANK OR MAY BE KEPT FROM PREV
// CODE AND CONDITIONS FOR USER DEFINED "FIRST" ANALYSIS
// FIRST DITCH ANY MACROS WE DON'T WANT
foreach globule in MA_userES MA_userCIlow MA_userCIupp MA_userDesc MA_userESM ///
MA_userCIlowM MA_userCIuppM MA_userDescM MA_ODC {
global `globule' = .
}
if "`first'" != ""{
cap assert real(word("`first'",1)) != . & real(word("`first'",2)) != . ///
& real(word("`first'",3)) != .
if _rc != 0{
di as err "Must supply estimate with confidence intervals: ES CIlow CIupp"
di as err "with user-defined main analysis"
exit
}
cap assert "`wgt'" != ""
if _rc != 0{
di as err "Must supply weight variable with option WGT(varname)"
di as err "with user-defined main analysis"
exit
}
cap assert "`by'"==""
if _rc != 0{
di as err "Cannot use option BY() with user-defined main analysis"
exit
}
cap assert $MA_params == 2 | $MA_params == 3
if _rc != 0{
di as err "Variable input must be 2 or 3 parameters, i.e.,
di as err "{theta se_theta} or {ES CIupp CIlow}"
di as err "with user-defined main analysis"
exit
}
global MA_userESM = real(word("`first'",1))
global MA_userCIlowM = real(word("`first'",2))
global MA_userCIuppM = real(word("`first'",3))
if "`eform'" != ""{
foreach glob in MA_userESM MA_userCIlowM MA_userCIuppM{
global `glob' = exp($`glob')
}
}
if $MA_userESM < $MA_userCIlowM | $MA_userESM > $MA_userCIuppM{
di as err "Must supply estimate with confidence interval in the order: ES CIlow CIupp"
exit
}
global MA_userDescM = substr("`first'", (strpos("`first'",word("`first'",4))), ///
(length("`first'")-strpos("`first'",word("`first'",4))+1) )
if word("`first'",4) == "" {
global MA_userDescM = "USER DEFN"
}
local method "USER"
}
if "`second'" != ""{
// METHODS
// RANDOM- D+L
// FIXED- M-H only available with cell counts
// RANDOMI- D+L
// FIXEDI- I-V
if "`second'" == "random" | "`second'" == "randomi" {
local method_2 "D+L"
}
else if "`second'" == "fixed" & "`4'" != "" & "`6'" == ""{
local method_2 "M-H"
}
else if "`second'" == "peto" & "`4'" != "" & "`6'" == ""{
local method_2 "Peto"
}
else if "`second'" == "fixedi" | ( ("`4'" == "" | "`6'" != "") & "`second'" == "fixed" ) ///
| ( ("`4'" == "" | "`6'" != "") & "`second'" == "peto" ){
local method_2 "I-V"
}
else{
cap assert real(word("`second'",1)) != . & real(word("`second'",2)) != . ///
& real(word("`second'",3)) != .
if _rc != 0{
di as err "Choose appropriate method for second analysis, or supply user-"
di as err "defined estimate with confidence intervals: ES CIlow CIupp"
exit
}
global MA_userES = real(word("`second'",1))
global MA_userCIlow = real(word("`second'",2))
global MA_userCIupp = real(word("`second'",3))
if "`eform'" != ""{
foreach glob in MA_userES MA_userCIlow MA_userCIupp{
global `glob' = exp($`glob')
}
}
if $MA_userES < $MA_userCIlow | $MA_userES > $MA_userCIupp{
di as err "Must supply estimate with confidence interval in the order: ES CIlow CIupp"
}
global MA_userDesc = substr("`second'", (strpos("`second'",word("`second'",4))), ///
(length("`second'")-strpos("`second'",word("`second'",4))+1) )
if word("`second'",4) == ""{
global MA_userDesc = "USER DEFN"
}
local nosecsub "nosecsub"
local method_2 "USER"
}
}
if "`method'" == "USER" & "`method_2'" != "USER" & "`method_2'" != ""{
di as err "Cannot have user defined analysis as main analysis and standard analysis
di as err "as second analysis. You can do it the other way round, or have two user"
di as err "defined analyses, but you can't do this particular thing."
di as err "Sorry, that's just the way it is."
exit
}
global MA_method1 "`method'"
global MA_method2 "`method_2'"
global MA_SECOND_ES .
global MA_SECOND_LCI .
global MA_SECOND_UCI .
global MA_SECOND_SE_ES .
global MA_SECOND_TAU2 .
global MA_first_TAU2 .
global MA_SECOND_DF .
global MA_first_DF .
if "`second'" != ""{
if "`callalg'" != "metanby"{ // just run through twice
`callalg' `varlist' `if' `in', `by' label(`code') `keep' /*
*/ method(`method_2') `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'") lcols("`lcols'") rcols("`rcols'") /*
*/ `groupla' `nextcall' `sstat' notable nograph rjhsecond
global MA_second_ES = $S_1 // IF NO BY JUST KEEP ESTIMATES AND STICK IN SOMEWHERE LATER
global MA_second_SE_ES = $S_2
global MA_second_LCI = $S_3
global MA_second_UCI = $S_4
global MA_second_TAU2 = $S_12
global MA_second_DF = $S_8
`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'") lcols("`lcols'") rcols("`rcols'") /*
*/ `groupla' `nextcall' `sstat'
}
if "`callalg'" == "metanby"{ // if by, then send to metanby and sort out there
`callalg' `varlist' `if' `in', `by' label(`code') `keep' `table' `graph' /*
*/ method(`method') method2(`method_2') `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'") lcols("`lcols'") rcols("`rcols'") /*
*/ `groupla' `nextcall' `sstat' `nosecsub'
}
}
if "`second'" == ""{
if "`callalg'" != "metanby"{
`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'") lcols("`lcols'") rcols("`rcols'") /*
*/ `groupla' `nextcall' `sstat'
}
if "`callalg'" == "metanby"{
`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'") lcols("`lcols'") rcols("`rcols'") /*
*/ `groupla' `nextcall' `sstat'
}
}
if $S_8<0 {
di as err "Insufficient data to perform this meta-analysis"
}
*return log or eform as appropriate for OR/RR
return scalar ES=$S_1
if "$MA_method2" != ""{
return scalar ES_2=$MA_second_ES
return local method_1 "$MA_method1"
return local method_2 "$MA_method2"
}
if ("`sumstat'"=="OR" | "`sumstat'"=="RR") & ("`log'"=="") {
return scalar selogES=$S_2
if "$MA_method2" != ""{
return scalar selogES_2=$MA_second_SE_ES
}
}
else if ("`sumstat'"=="ES" & "`eform'"!="") {
return scalar selogES=$S_2
if "$MA_method2" != ""{
return scalar selogES_2=$MA_second_SE_ES
}
}
else {
return scalar seES=$S_2
if "$MA_method2" != ""{
return scalar seES_2=$MA_second_SE_ES
}
}
return scalar ci_low=$S_3
return scalar ci_upp=$S_4
if "$MA_method2" != ""{
return scalar ci_low_2=$MA_second_LCI
return scalar ci_upp_2=$MA_second_UCI
}
return scalar z=$S_5
return scalar p_z=$S_6
return scalar i_sq=$S_51 // ADDED I2 IN RETURN
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 9.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) LCOLS(string) RCOLS(string)
noOVERALL noWT noSTATS LOG BRESLOW COUNTS WGT(varlist numeric max=1) noGROUPLA RJHSECOND] ;
#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
tokenize "`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 str `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
} // end while
} // end Cornfield
*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) */
global S_51 =max(0, ( 100*($S_7-$S_8))/($S_7) )
}
} /* end M-H method */
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" & "`method'"!= "USER"{
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) */
global S_51 =max(0, ( 100*($S_7-$S_8))/($S_7) )
}
if "`method'" == "USER"{
forvalues i = 1/15{
global S_`i' = .
}
cap drop `weight'
gen `weight'=.
if "`rjhsecond'" != ""{
global S_1 = $MA_userES
global S_3 = $MA_userCIlow
global S_4 = $MA_userCIupp
}
else{
global S_1 = $MA_userESM
global S_3 = $MA_userCIlowM
global S_4 = $MA_userCIuppM
}
}
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'") lcols("`lcols'") rcols("`rcols'") `overall' `wt' /*
*/ `stats' `counts' `log' `groupla' `udwind' `cornfield' `rjhsecond'
end
program define Peto
version 9.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) LCOLS(string) RCOLS(string) noOVERALL noWT noSTATS LOG COUNTS RJHSECOND] ;
#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
tokenize "`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 str `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'="."
} /* end counts */
*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)
global S_51 =max(0, ( 100*($S_7-$S_8))/($S_7) )
gen `weight' =100*`v'/`V'
if "`method'" == "USER"{
forvalues i = 1/15{
global S_`i' = .
}
cap drop `weight'
gen `weight'=.
if "`rjhsecond'" != ""{
global S_1 = $MA_userES
global S_3 = $MA_userCIlow
global S_4 = $MA_userCIupp
}
else{
global S_1 = $MA_userESM
global S_3 = $MA_userCIlowM
global S_4 = $MA_userCIuppM
}
}
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'") lcols("`lcols'") rcols("`rcols'") /*
*/ `rjhsecond' `overall' `wt' `stats' `counts' `log'
end
program define RR
version 9.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) LCOLS(string) RCOLS(string) noOVERALL noWT noSTATS LOG COUNTS RJHSECOND
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
tokenize "`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 str `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'="."
} /* end counts */
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)
global S_51 =max(0, ( 100*($S_7-$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" & "`method'"!="USER"{
cap gen `weight'=.
iv `lnrr' `v', method(`method') `randomi' `exp'
replace `weight'=100/( (`v'+$S_12)*($MA_W) )
}
if "`method'" == "USER"{
forvalues i = 1/15{
global S_`i' = .
}
cap drop `weight'
gen `weight'=.
if "`rjhsecond'" != ""{
global S_1 = $MA_userES
global S_3 = $MA_userCIlow
global S_4 = $MA_userCIupp
}
else{
global S_1 = $MA_userESM
global S_3 = $MA_userCIlowM
global S_4 = $MA_userCIuppM
}
}
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'") lcols("`lcols'") rcols("`rcols'") `overall' `wt' /*
*/ `stats' `counts' `log' `udwind' `rjhsecond'
end
program define RD
version 9.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) LCOLS(string) RCOLS(string) noOVERALL noWT noSTATS COUNTS WGT(varlist numeric max=1) RJHSECOND ] ;
#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
tokenize "`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 str `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'="."
} /* end counts */
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)
global S_51 =max(0, ( 100*($S_7-$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" & "`method'"!= "USER" {
iv `rd' `v', method(`method') `randomi'
replace `weight'=100/( (`v'+$S_12)*($MA_W) )
}
if "`method'" == "USER"{
forvalues i = 1/15{
global S_`i' = .
}
cap drop `weight'
gen `weight'=.
if "`rjhsecond'" != ""{
global S_1 = $MA_userES
global S_3 = $MA_userCIlow
global S_4 = $MA_userCIupp
}
else{
global S_1 = $MA_userESM
global S_3 = $MA_userCIlowM
global S_4 = $MA_userCIuppM
}
}
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'") lcols("`lcols'") rcols("`rcols'") /*
*/ `rjhsecond' `overall' `wt' `stats' `counts' `udwind'
end
program define MD
version 9.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) LCOLS(string) RCOLS(string) noOVERALL noWT noSTATS COUNTS WGT(string) RJHSECOND ] ;
#delimit cr
qui {
tempvar n1 x1 sd1 n2 x2 sd2 use n s md v se ill iul weight id qhet rawdata
tokenize "`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 `rawdata'= string(`n1') + ", " + string(`x1',"%7.3g") +" (" + string(`sd1',"%7.3g") + /*
*/ ") ; " + string(`n2') + ", " + string(`x2',"%7.3g") +" (" + string(`sd2',"%7.3g") +") "
replace `rawdata'= trim(`rawdata')
qui summ `n1'
local sum1 = r(sum)
qui summ `n2'
local sum2 = r(sum)
global MA_ODC = string(`sum1') + "; " + string(`sum2')
}
else {
gen str1 `rawdata'="."
} /* end counts */
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) )
}
if "`method'" == "USER"{
forvalues i = 1/15{
global S_`i' = .
}
cap drop `weight'
gen `weight'=.
if "`rjhsecond'" != ""{
global S_1 = $MA_userES
global S_3 = $MA_userCIlow
global S_4 = $MA_userCIupp
}
else{
global S_1 = $MA_userESM
global S_3 = $MA_userCIlowM
global S_4 = $MA_userCIuppM
}
}
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'") lcols("`lcols'") rcols("`rcols'") /*
*/ `rjhsecond' `overall' `wt' `stats' `udwind' `counts'
end
program define iv_init
version 9.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) LCOLS(string) RCOLS(string) noOVERALL noWT noSTATS EFORM WGT(string) RJHSECOND ] ;
#delimit cr
qui {
tempvar es se use v ill iul weight id rawdata
tokenize "`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'"!=""{
cap drop `weight'
gen `weight' = `wgt' if `use'==1
udw `es' `v', wgt(`weight') `exp' `rjhsecond'
replace `weight'=100*`wgt'/($MA_W)
local udwind "wgt(`wgt')"
}
else {
iv `es' `v', method(`method') `exp' randomi `rjhsecond'
*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'") lcols("`lcols'") rcols("`rcols'") `overall' `wt' `stats' `eform' /*
*/ `var3' `udwind' `rjhsecond'
end
program define iv
version 9.0
#delimit ;
syntax varlist(min=2 max=2 default=none numeric) [if] [in] [,
METHOD(string) RANDOMI EXP RJHSECOND ] ;
#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
tokenize "`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') )
global RJH_TAU2 = $S_12
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)
global S_51 =max(0, ( 100*($S_7-$S_8))/($S_7) )
if "`method'" == "USER"{
forvalues i = 1/15{
global S_`i' = .
}
cap drop `weight'
tempvar weight
gen `weight'=.
if "`rjhsecond'" != ""{
global S_1 = $MA_userES
global S_3 = $MA_userCIlow
global S_4 = $MA_userCIupp
}
else{
global S_1 = $MA_userESM
global S_3 = $MA_userCIlowM
global S_4 = $MA_userCIuppM
}
}
end
program define udw
* user defined weights to combine trials
version 9.0
#delimit ;
syntax varlist(min=2 max=2 default=none numeric) [if] [in] [,
METHOD(string) EXP WGT(varlist numeric max=1) RJHSECOND ] ;
#delimit cr
tempvar stat v w e_w varcomp qhet
tempname W E_W OV W2 Vnum V QHET
tokenize "`varlist'", parse(" ")
gen `stat'=`1'
gen `v' =`2'
gen `w' =`wgt' if `stat'!=.
sum `w',meanonly
scalar `W'=r(sum)
if `W'==0 {
di as err "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)
global S_51 =max(0, ( 100*($S_7-$S_8))/($S_7) )
if "$MA_method1" == "USER"{
cap drop `weight'
tempvar weight
gen `weight'=.
forvalues i = 1/15{
global S_`i' = .
}
if "`rjhsecond'" != ""{
global S_1 = $MA_userES
global S_3 = $MA_userCIlow
global S_4 = $MA_userCIupp
}
else{
global S_1 = $MA_userESM
global S_3 = $MA_userCIlowM
global S_4 = $MA_userCIuppM
}
global S_8 = 0
}
end
program define _disptab
version 9.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) LCOLS(string) RCOLS(string) noOVERALL noWT noSTATS COUNTS LOG EFORM noGROUPLA SORTBY(string)
WGT(string) VAR3 CORNFIELD /* RJH */ RJHSECOND ] ;
#delimit cr
tempvar effect se lci uci weight use label tlabel rawdata id tau2 df
tokenize "`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 `rawdata' = `8'
gen `tau2' = .
gen `df' = .
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" {
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')"
}
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"
} /* end if-keep */
preserve
if "`overall'"=="" & "`rjhsecond'" == ""{ // only do this on main run
**If overall figure requested, add an extra line to contain overall stats
local nobs1=_N+1
set obs `nobs1'
replace `weight'=100 in `nobs1'
replace `effect'= ($S_1) in `nobs1'
replace `lci'=($S_3) in `nobs1'
replace `uci'=($S_4) in `nobs1'
replace `use'=5 in `nobs1'
replace `tau2' = $S_12 in `nobs1'
replace `df' = $S_8 in `nobs1'
if "`counts'"!="" {
replace `rawdata'="$MA_ODC" in `nobs1'
}
local i2=max(0, (100*($S_7-$S_8)/($S_7)) )
local hetp=$S_9
replace `label' = "Overall (I-squared = " + string(`i2', "%5.1f")+ "%, p = " + ///
string(`hetp', "%5.3f") + ")" in `nobs1'
* RJH code for second method
if "$MA_method2" != "" {
local nobs1=_N+1
set obs `nobs1'
replace `weight'=100 in `nobs1'
replace `effect'= $MA_second_ES in `nobs1'
replace `lci'=$MA_second_LCI in `nobs1'
replace `uci'=$MA_second_UCI in `nobs1'
replace `use'=17 in `nobs1'
if "$MA_second_TAU2" != ""{
replace `tau2' = $MA_second_TAU2 in `nobs1'
replace `df' = $MA_second_DF in `nobs1'
}
replace `label' = "Overall" in `nobs1'
}
} /* end overall stuff */
local usetot=$S_8+1
count if `use'==2
local alltot=r(N)+`usetot'
gen `id'=_n
tempvar rjhorder
qui gen `rjhorder' = `use'
qui replace `rjhorder' = 3.5 if `use' == 19 // SWAP ROUND SO BLANK IN RIGHT PLACE
sort `rjhorder' `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 { // BEGIN WHILE LOOP
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 NORMAL TRIAL, OR OVERALL EFFECT
if ( (`use'[`i']==1) | (`use'[`i']==5) | `use'[`i'] == 17 ) {
if (`use'[`i'])==1 {
*trial results
di in gr `tlabel'[`i'] _cont
}
else {
*overall
// RJH
if `use'[`i'] == 5 {
local dispM1 = "$MA_method1"
if "$MA_method1" == "USER"{
local dispM1 = "$MA_userDescM"
}
di in gr _dup(21) "-" "+" _dup(11) "-" "`insert'" _dup(20) "-" _n /*
*/ "`dispM1' pooled `log'`sumstat'" _cont
}
if `use'[`i'] == 17{ // SECOND EST
local dispM2 = "$MA_method2"
if "$MA_method2" == "USER"{
local dispM2 = "$MA_userDesc"
}
di in gr "`dispM2' pooled `log'`sumstat'" _cont
}
} // end else
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
} /* END WHILE */
di in gr _dup(21) "-" "+" _dup(51) "-"
if "`overall'"=="" & "$MA_method1" != "USER"{
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. */
} // end if table
if "`graph'"=="" & `usetot'>0 {
qui drop if `use' == 9
_dispgby `effect' `lci' `uci' `weight' `use' `label' `rawdata' `tau2' `df', `log' /*
*/ `xlabel' `xtick' `force' sumstat(`sumstat') `saving' `box' t1("`t1'") /*
*/ t2("`t2'") b1("`b1'") b2("`b2'") lcols("`lcols'") rcols("`rcols'") `overall' `wt' `stats' `counts' `eform' /*
*/ `groupla' `cornfield'
}
restore
end
program define metanby
version 9.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) METHOD2(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) LCOLS(string) RCOLS(string) noWT noSTATS COUNTS noBOX noGROUPLA NOSECSUB ] ;
#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 df 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 "`counts'"!="" {
replace `rawdata'= string(`1') + ", " + string(`2',"%7.3g") +" (" + string(`3',"%7.3g") + /*
*/ ") ; " + string(`4') + ", " + string(`5',"%7.3g") +" (" + string(`6',"%7.3g") +") "
replace `rawdata'= trim(`rawdata') if `use' != 9
}
}
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"
}
*RJH- second estimate
if "`method2'" != ""{
`nextcall' `varlist' if `use'==1, nograph notable method(`method2') `randomi' /*
*/ label(`label') `wgt' `cornfield' `chi2' `cc' `standard' `log' `eform' `breslow' rjhsecond
global MA_second_ES = $S_1 // KEEP ESTIMATES AND STICK IN SOMEWHERE LATER
global MA_second_SE_ES = $S_2
global MA_second_LCI = $S_3
global MA_second_UCI = $S_4
global MA_second_TAU2 = $S_12
global MA_second_DF = $S_8
}
*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
*Convert "by" variable to string
* modified July 2008- keeps original order if by variable is numeric (was ignored before)
cap confirm numeric var `by'
if _rc == 0{
tempvar by_num
cap decode `by', gen(`by_num')
if _rc != 0{
local f: format `by'
gen `by_num' = string(`by', "`f'")
}
qui drop `by'
rename `by_num' `by'
}
cap confirm numeric var `by'
* This replaces the old encode statement
* The _by variable is generated according to the original
* sort order of the data, and not done alpha-numerically
qui count
local N = r(N)
gen `by2' = 1 in 1
local lab = `by'[1]
cap label drop bylab
label define bylab 1 "`lab'"
local found1 "`lab'"
local max = 1
forvalues i = 2/`N'{
local thisval = `by'[`i']
local already = 0
forvalues j = 1/`max'{
if "`thisval'" == "`found`j''"{
local already = `j'
}
}
if `already' > 0{
replace `by2' = `already' in `i'
}
else{
local max = `max' + 1
replace `by2' = `max' in `i'
local lab = `by'[`i']
label define bylab `max' "`lab'", modify
local found`max' "`lab'"
}
}
label values `by2' bylab
*Keep only neccesary data (have to put preserve here in order to keep _ES etc)
preserve
sort `by2' `sortby' `id'
qui 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 `df' = .
gen `tsig'=.
gen `psig'=.
*Create new "by" variable to take on codes 1,2,3..
gen `newby'=(`by2'>`by2'[_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'
*RJH plus another line if second estimate
if "`method2'" != ""{
local nobs2=_N+1
set obs `nobs2'
replace `use'=17 in `nobs2'
replace `newby'=`ngroups'+1 in `nobs2'
replace `effect'= ($MA_second_ES) in `nobs2'
replace `lci'=($MA_second_LCI) in `nobs2'
replace `uci'=($MA_second_UCI) in `nobs2'
if "`method2'" == "D+L"{
replace `tau2' = $MA_second_TAU2 in `nobs2'
replace `hetdf' = $MA_second_DF in `nobs2'
}
}
*Put cell counts in subtotal row
if ("`counts'"!="") {
if "`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'
}
if "`nextcall'"=="MD" {
sum `1' if (`use'==1 | `use'==2)
local sum1 =r(sum)
sum `4' if (`use'==1 | `use'==2)
local sum2 =r(sum)
replace `rawdata'= "`sum1'; `sum2'" 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 `df' = $S_8 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'
}
} /* end if overall */
*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)
*RJH- add another line if SECOND sub estimates
sort `newby' `use' `sortby' `id'
by `newby': gen `expand'=1 + 2*(_n==1) + (_n==1 & "`subgroup'"=="") ///
+ (_n==1 & "`method2'"!="" & "`nosecsub'"=="")
replace `expand'=1 if `use'==5 | `use' == 17
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 */
by `newby': replace `use'=19 if `expand'>1 & _n==5 /* (if specified) RJH extra line for second estimate */
* 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' { // HUGE LOOP THROUGH EACH SUBGROUP
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 {
/* SECOND SUB-ESTIMATES */
if "`method2'" != "" & "`nosecsub'" == ""{
`nextcall' `varlist' if (`newby'==`j' & `use'==1) , nograph /*
*/ notable label(`label') method(`method2') `randomi' `wgt' `cornfield' `chi2' /*
*/ `cc' `standard' `log' `eform' `breslow'
replace `effect'=($S_1) if `use'==19 & `newby'==`j'
replace `lci'=($S_3) if `use'==19 & `newby'==`j'
replace `uci'=($S_4) if `use'==19 & `newby'==`j'
replace `hetdf' = $S_8 if `use'==19 & `newby'==`j'
if "`method2'"=="D+L" {
replace `tau2' = $S_12 if `use'==19 & `newby'==`j'
}
}
/* THEN GET REGULAR ESTIMATES AS USUAL */
`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'
}
} /* END OF IF SUBGROUP N > 0 */
*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')
}
if "`nextcall'"=="MD" {
sum `1' if (`use'==1 | `use'==2) & (`newby'==`j')
local sum1 =r(sum)
sum `4' if (`use'==1 | `use'==2) & (`newby'==`j')
local sum2 =r(sum)
replace `rawdata'= "`sum1'; `sum2'" if (`use'==3 & `newby'==`j')
}
}
} /* END OF if "`subgroup'" == "" */
*Label attatched (if any) to byvar
local lbl: value label `by2'
sum `by2' if `newby'==`j'
local byvlu=r(mean)
if "`lbl'"=="" {
local lab "`by2'==`byvlu'"
}
else {
local lab: label `lbl' `byvlu'
}
replace `label' = "`lab'" if ( `use'==0 & `newby'==`j')
replace `label' = "Subtotal" if ( `use'==3 & `newby'==`j')
/* RMH I-squared added in next line
RJH- also p-val as recommended by Mike Bradburn */
replace `label' = "Subtotal (I-squared = " + string(`i2', "%5.1f")+ "%, p = " + ///
string(`hetp', "%5.3f") + ")" if ( `use'==3 & `newby'==`j' & "$MA_nohet" == "")
local j=`j'+1
} /* FINALLY, THE END OF THE WHILE LOOP! */
replace `label' = "Overall (I-squared = " + string(`i2', "%5.1f")+ "%, p = " + ///
string(`hetp', "%5.3f") + ")" if ( `use'==5 & "$MA_nohet" == "")
if "`subgroup'"==""{
qui sum `hetc' if `use' == 5
local rjhet = r(mean)
qui sum `hetc' if `use'==3
local btwghet = (`rjhet') -r(sum)
local df = `ngroups'-1
global rjhHetGrp = chiprob(`df',`btwghet')
}
} /*End of quietly loop*/
*Put table up (if requested)
tempvar rjhorder
qui gen `rjhorder' = `use'
qui replace `rjhorder' = 3.5 if `use' == 19 // SWAP ROUND SO BLANK IN RIGHT PLACE
sort `newby' `rjhorder' `sortby' `id'
// need to ditch this if SECOND specified
if "`subgroup'" != ""{
qui drop if `use' == 3 | `use' == 19
}
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 | `use'[`i']==19) & "`subgroup'"=="") | (`use'[`i']==5 | `use'[`i']==17) {
*Subgroup effect size or overall effect size
if (`use'[`i'])==3 {
di in gr " Sub-total" _col(22) "|"
}
if `use'[`i']==17 | `use'[`i']==5{
if $IND!=$OVE {
local insert "[$OVE% Conf. Interval]"
}
if `use'[`i'] == 5{
di in gr "Overall" _col(22) "|" _col(34) "`insert'"
}
}
if "`ww'"=="." {
local ww
}
// RJH
if `use'[`i'] == 3 | `use'[`i'] == 5{
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'] == 19 | `use'[`i'] == 17{
di in gr " $MA_method2 pooled `log'`sumstat'" _col(22) "| " in ye %7.3f /*
*/ `effect'[`i'] _col(35) %7.3f `lci'[`i'] " " %7.3f `uci'[`i']
}
if (`use'[`i'])==5 & "$MA_method2" == "" | `use'[`i'] == 17{
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
} /* END OF WHILE LOOP */
*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"
}
local maxHet = 0
local i=1
while `i'<= _N {
if ("`subgroup'"=="" & (`use'[`i'])==0) | ( (`use'[`i'])==5) {
if `use'[`i'] != 5{
di in gr _n `tlabel'[`i'] _cont
}
else{
di in gr _n "Overall" _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 `use'[`i'] == 3{
local maxHet = max(`maxHet',`i2'[`i'])
}
if `use'[`i'] == 5{
local ovHet = `i2'[`i']
}
if "`method'"=="D+L" {
di in ye " " %7.4f `tau2'[`i'] _cont
}
if (`use'[`i']==5) & ("`subgroup'"=="") & ("$MA_method1" == "I-V") { // FIXED I-V ONLY
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
// DISPLAY BETWEEN-GROUP TEST WARNINGS
if "`overall'" == ""{
if `maxHet' < 50 & `maxHet' > 0 & ("$MA_method1" == "I-V"){
di in gr "Some heterogeneity observed (up to "%4.1f `maxHet' "%) in one or more sub-groups,"
di in gr "Test for heterogeneity between sub-groups may be invalid"
}
if `maxHet' < 75 & `maxHet' >= 50 & ("$MA_method1" == "I-V"){
di in gr "Moderate heterogeneity observed (up to "%4.1f `maxHet' "%) in one or more sub-groups,"
di in gr "Test for heterogeneity between sub-groups likely to be invalid"
}
if `maxHet' < . & `maxHet' >= 75 & ("$MA_method1" == "I-V"){
di in gr "Considerable heterogeneity observed (up to "%4.1f `maxHet' "%) in one or more sub-groups,"
di in gr "Test for heterogeneity between sub-groups likely to be invalid"
}
if "$MA_method1" != "I-V"{
di in gr "Note: between group heterogeneity not calculated;"
di in gr "only valid with inverse variance method"
}
}
*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) {
if `use'[`i'] != 5{
di in gr _n `tlabel'[`i'] _cont
}
else{
di in gr _n "Overall" _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 if ("`subgroup'"=="" | "`overall'"=="") */
} /* end of table display */
if "`overall'"=="" {
*need to return overall effect to $S_1 macros and so on...
local N = _N
if "$MA_method2" != ""{
local N = _N-1
}
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']
global S_51 =`i2'[`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'
}
} /* end if overall */
else {
forvalues i = 1/14{
global S_`i' .
}
}
if "`graph'"=="" {
_dispgby `effect' `lci' `uci' `weight' `use' `label' `rawdata' `wtdisp' `tau2' `hetdf', /*
*/ `log' `xlabel' `xtick' `force' sumstat(`sumstat') `saving' `box' t1("`t1'") /*
*/ t2("`t2'") b1("`b1'") b2("`b2'") lcols("`lcols'") rcols("`rcols'") `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 ordering
gen `ordering' = _n
bysort `by2': gen `tempsum'=sum(`weight')
local N = _N
if "$MA_method2" != ""{
local N = _N-1
}
bysort `by2': replace `tempsum'=`tempsum'[`N']
gen _WT=`weight'*100/`tempsum'
local sg "(subgroup) "
sort `ordering'
}
cap label var _WT "`method' `sg'% weight"
} /* end qui */
} /* end if keep */
end
**********************************************************
*** ***
*** NEW ***
*** _DISPGBY PROGRAM ***
*** ROSS HARRIS ***
*** JULY 2006 ***
*** * * * ***
*** ***
**********************************************************
program define _dispgby
version 9.0
// AXmin AXmax ARE THE OVERALL LEFT AND RIGHT COORDS
// DXmin dxMAX ARE THE LEFT AND RIGHT COORDS OF THE GRAPH PART
#delimit ;
syntax varlist(min=6 max=10 default=none ) [if] [in] [,
LOG XLAbel(string) XTICK(string) FORCE SAVING(string) noBOX SUMSTAT(string)
T1(string) T2(string) B1(string) B2(string) LCOLS(string) /* JUNK NOW */
RCOLS(string) noOVERALL noWT noSTATS COUNTS EFORM
noGROUPLA CORNFIELD];
#delimit cr
tempvar effect lci uci weight wtdisp use label tlabel id yrange xrange Ghsqrwt rawdata i2 mylabel
tokenize "`varlist'", parse(" ")
qui{
gen `effect'=`1'
gen `lci' =`2'
gen `uci' =`3'
gen `weight'=`4' // was 4
gen byte `use'=`5'
gen str `label'=`6'
gen str `mylabel'=`6'
if "`lcols'" == ""{
local lcols "`mylabel'"
label var `mylabel' "Study ID"
}
gen str80 `rawdata' = `7'
compress `rawdata'
if "`8'"!="" & "$MA_rjhby" != ""{
gen `wtdisp'=`8'
}
else {
gen `wtdisp'=`weight'
}
if "`10'" != "" & "$MA_rjhby" != ""{
tempvar tau2 df
gen `tau2' = `9'
gen `df' = `10'
}
if "`9'" != "" & "$MA_rjhby" == ""{ // DIFFERENT IF FROM metan OR metanby
tempvar tau2 df
gen `tau2' = `8'
gen `df' = `9'
}
replace `weight' = `wtdisp' // bodge solu for SG weights
if "$MA_summaryonly" != ""{
drop if `use' == 1
}
// SET UP EXTENDED CIs FOR RANDOM EFFECTS DISTRIBUTION
// THIS CODE IS A BIT NASTY AS I SET THIS UP BADLY INITIALLY
// REQUIRES MAJOR REWORK IDEALLY...
tempvar tauLCI tauUCI SE tauLCIinf tauUCIinf
*replace `tau2' = .a if `tau2' == 0 // no heterogeneity
replace `tau2' = .b if `df'-1 == 0 // inestimable predictive distribution
replace `tau2' = . if (`use' == 5 | `use' == 3) & "$MA_method1" != "D+L"
replace `tau2' = . if (`use' == 17 | `use' == 19) & "$MA_method2" != "D+L"
gen `tauLCI' = .
gen `tauUCI' = .
gen `tauLCIinf' = .
gen `tauUCIinf' = .
gen `SE' = .
// modified so rf CI (rflevel) used
if "$MA_rfdist" != ""{
if ( ("`sumstat'"=="OR" | "`sumstat'"=="RR") & ("`log'"=="") ) | ("`eform'"!="") {
replace `SE' = (ln(`uci')-ln(`lci')) / (invnorm($RFL/200+0.5)*2)
replace `tauLCI' = exp( ln(`effect') - invttail((`df'-1), 0.5-$RFL/200)*sqrt( `tau2' +`SE'^2 ) )
replace `tauUCI' = exp( ln(`effect') + invttail((`df'-1), 0.5-$RFL/200)*sqrt( `tau2' +`SE'^2 ) )
replace `tauLCI' = 1e-9 if `tau2' == .b
replace `tauUCI' = 1e9 if `tau2' == .b
}
else{
replace `SE' = (`uci'-`lci') / ($RFL/200+0.5)
replace `tauLCI' = `effect'-invttail((`df'-1), 0.5-$RFL/200)*sqrt(`tau2'+`SE'^2)
replace `tauUCI' = `effect'+invttail((`df'-1), 0.5-$RFL/200)*sqrt(`tau2'+`SE'^2)
replace `tauLCI' = -1e9 if `tau2' == .b
replace `tauUCI' = 1e9 if `tau2' == .b
}
}
if "$MA_rfdist" != ""{
qui count
local prevN = r(N)
tempvar expTau orderTau
gen `orderTau' = _n
gen `expTau' = 1
replace `expTau' = 2 if `tau2' != . // but expand if .a or .b
expand `expTau'
replace `use' = 4 if _n > `prevN'
replace `orderTau' = `orderTau' + 0.5 if _n > `prevN'
sort `orderTau'
}
tempvar estText weightText RFdistText RFdistLabel
local dp = $MA_dp
gen str `estText' = string(`effect', "%10.`dp'f") + " (" + string(`lci', "%10.`dp'f") + ", " +string(`uci', "%10.`dp'f") + ")"
replace `estText' = "(Excluded)" if `use' == 2
// don't show effect size again, just CI
gen `RFdistLabel' = "with estimated predictive interval" if `use' == 4 & `tau2' < .
gen `RFdistText' = /* string(`effect', "%10.`dp'f") + */ ". (" + string(`tauLCI', "%10.`dp'f") + ", " +string(`tauUCI', "%10.`dp'f") ///
+ ")" if `use' == 4 & `tau2' < .
/* not used
replace `RFdistLabel' = "No observed heterogeneity" if `use' == 4 & `tau2' == .a
replace `RFdistText' = string(`effect', "%10.`dp'f") + " (" + string(`lci', "%10.`dp'f") + ", " +string(`uci', "%10.`dp'f") ///
+ ")" if `use' == 4 & `tau2' == .a
*/
// don't show effect size again, just CI
replace `RFdistLabel' = "Inestimable predictive distribution with <3 studies" if `use' == 4 & `tau2' == .b
replace `RFdistText' = /* string(`effect', "%4.2f") + */ ". ( - , - )" if `use' == 4 & `tau2' == .b
qui replace `estText' = " " + `estText' if `effect' >= 0 & `use' != 4
gen str `weightText' = string(`weight', "%4.2f")
replace `weightText' = "" if `use' == 17 | `use' == 19 // can cause confusion and not necessary
replace `rawdata' = "" if `use' == 17 | `use' == 19
if "`counts'" != ""{
if $MA_params == 6{
local type "N, mean (SD);"
}
else{
local type "Events,"
}
tempvar raw1 raw2
gen str `raw1' = substr(`rawdata',1,(strpos(`rawdata',";")-1) )
gen str `raw2' = substr(`rawdata',(strpos(`rawdata',";")+1), (length(`rawdata')) )
label var `raw1' "`type' $MA_G1L"
label var `raw2' "`type' $MA_G2L"
}
/* RJH - probably a better way to get this but I've just used globals from earlier */
if "`overall'" == "" & "$MA_nohet" == ""{
if "$MA_method1" == "USER"{
if "$MA_firststats" != ""{
replace `label' = "Overall ($MA_firststats)" if `use'==5
}
else{
replace `label' = "Overall" if `use'==5
}
}
replace `label' = "Overall ($MA_secondstats)" if `use' == 17 & "$MA_method2" == "USER" & "$MA_secondstats" != ""
replace `label' = "Overall" if `use' == 17 & "$MA_method2" == "USER" & "$MA_secondstats" == ""
}
if "`overall'" == "" & "$MA_nohet" != ""{
replace `label' = "Overall" if `use' == 5 | `use' == 17
}
tempvar hetGroupLabel expandOverall orderOverall
if "$MA_rjhby" != "" & "$MA_nohet" == "" & "$MA_method1" == "I-V"{
* replace `label' = `label' + ";" if `use' == 5
qui count
local prevMax = r(N)
gen `orderOverall' = _n
gen `expandOverall' = 1
replace `expandOverall' = 2 if `use' == 5
expand `expandOverall'
replace `orderOverall' = `orderOverall' -0.5 if _n > `prevMax'
gen `hetGroupLabel' = "Heterogeneity between groups: p = " + ///
string($rjhHetGrp, "%5.3f") if _n > `prevMax'
replace `use' = 4 if _n > `prevMax'
sort `orderOverall'
}
else{
gen `hetGroupLabel' = .
}
replace `label' = "Overall" if `use' == 17 & "$MA_method2" != "USER"
replace `label' = "Subtotal" if `use' == 19
qui count if (`use'==1 | `use'==2)
local ntrials=r(N)
qui count if (`use'>=0 & `use'<=5)
local ymax=r(N)
gen `id'=`ymax'-_n+1 if `use'<9 | `use' == 17 | `use' == 19
if "$MA_method2" != "" | "$MA_method1" == "USER" {
local dispM1 = "$MA_method1"
local dispM2 = "$MA_method2"
if "$MA_method1" == "USER"{
local dispM1 "$MA_userDescM"
}
if "$MA_method2" == "USER"{
local dispM2 "$MA_userDesc"
}
replace `label' = "`dispM1'" + " " + `label' if (`use' == 3 | `use' == 5) & substr(`label',1,3) != "het"
replace `label' = "`dispM2'" + " " + `label' if `use' == 17 | `use' == 19
}
// GET MIN AND MAX DISPLAY
// SORT OUT TICKS- CODE PINCHED FROM MIKE AND FIDDLED. TURNS OUT I'VE BEEN USING SIMILAR NAMES...
// AS SUGGESTED BY JS JUST ACCEPT ANYTHING AS TICKS AND RESPONSIBILITY IS TO USER!
qui summ `lci', detail
local DXmin = r(min)
qui summ `uci', detail
local DXmax = r(max)
local h0 = 0
// MIKE MAKES A MAX VALUE IF SOMETHING EXTREME OCCURS...
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 `DXmin'<1e-8 {
local DXmin=1e-8
}
if `DXmax'>1e8 {
local DXmax=1e8
}
}
if "$MA_NULL" != ""{
local h0 = $MA_NULL
}
if `h0' != 0 & `h0' != 1{
noi di "Null specified as `h0' in graph- for most effect measures null is 0 or 1"
}
if "`cornfield'"!="" {
replace `lci'=`log'(1e-9) if ( (`lci'==. | `lci'==0) & (`effect'!=. & `use'==1) )
replace `uci'=`log'(1e9) if ( (`uci'==.) & (`effect'!=. & `use'==1) )
}
// THIS BIT CHANGED- THE USER CAN PUT ANYTHING IN
local flag1=0
if ("`xlabel'"=="" | "`xtick'" == "") & "$MA_nulloff" == ""{ // if no xlabel or tick
local xtick "`h0'"
}
if "`xlabel'"==""{
local Gmodxhi=max( abs(`xlog'(`DXmin')),abs(`xlog'(`DXmax')))
if `Gmodxhi'==. {
local Gmodxhi=2
}
local DXmin=`xexp'(-`Gmodxhi')
local DXmax=`xexp'( `Gmodxhi')
if "$MA_nulloff" == ""{
local xlabel "`DXmin',`h0',`DXmax'"
}
else{
local xlabel "`DXmin',`DXmax'"
}
}
local DXmin2 = min(`xlabel',`DXmin')
local DXmax2 = max(`xlabel',`DXmax')
if "`force'" == ""{
local Gmodxhi=max( abs(`xlog'(`DXmin')), abs(`xlog'(`DXmax')), ///
abs(`xlog'(`DXmin2')), abs(`xlog'(`DXmax2')) )
if `Gmodxhi'==. {
local Gmodxhi=2
}
local DXmin=`xexp'(-`Gmodxhi')
local DXmax=`xexp'( `Gmodxhi')
if "`xlabel'" != "" & "$MA_nulloff" == ""{
local xlabel "`h0',`xlabel'"
}
}
if "`force'" != ""{
local DXmin = min(`xlabel')
local DXmax = max(`xlabel')
if "$MA_nulloff" == ""{
local xlabel "`h0',`xlabel'"
}
}
// LABELS- DON'T ALLOW SILLY NO. OF DECIMAL PLACES
local lblcmd ""
tokenize "`xlabel'", parse(",")
while "`1'" != ""{
if "`1'" != ","{
local lbl = string(`1',"%7.3g")
local val = `1'
local lblcmd `lblcmd' `val' "`lbl'"
}
mac shift
}
if "`xtick'" == ""{
local xtick = "`xlabel'"
}
local xtick2 = ""
tokenize "`xtick'", parse(",")
while "`1'" != ""{
if "`1'" != ","{
local xtick2 = "`xtick2' " + string(`1')
}
if "`1'" == ","{
local xtick2 = "`xtick2'`1'"
}
mac shift
}
local xtick = "`xtick2'"
local DXmin=`xlog'(min(`xlabel',`xtick',`DXmin'))
local DXmax=`xlog'(max(`xlabel',`xtick',`DXmax'))
if ("`eform'" != "" | "`xlog'" != "") {
local lblcmd ""
tokenize "`xlabel'", parse(",")
while "`1'" != ""{
if "`1'" != ","{
local lbl = string(`1',"%7.3g")
local val = ln(`1')
local lblcmd `lblcmd' `val' "`lbl'"
}
mac shift
}
replace `effect' = ln(`effect')
replace `lci' = ln(`lci')
replace `uci' = ln(`uci')
replace `tauLCI' = ln(`tauLCI')
replace `tauUCI' = ln(`tauUCI')
local xtick2 ""
tokenize "`xtick'", parse(",")
while "`1'" != ""{
if "`1'" != ","{
local ln = ln(`1')
local xtick2 "`xtick2' `ln'"
}
if "`1'" == ","{
local xtick2 "`xtick2'`1'"
}
mac shift
}
local xtick "`xtick2'"
local h0 = 0
}
// JUNK
*noi di "min: `DXmin', `DXminLab'; h0: `h0', `h0Lab'; max: `DXmax', `DXmaxLab'"
local DXwidth = `DXmax'-`DXmin'
if `DXmin' > 0{
local h0 = 1
}
} // END QUI
// END OF TICKS AND LABLES
// MAKE OFF-SCALE ARROWS
qui{
tempvar offLeftX offLeftX2 offRightX offRightX2 offYlo offYhi
local arrowWidth = 0.02 // FRACTION OF GRAPH WIDTH
local arrowHeight = 0.5/2 // Y SCALE IS JUST ORDERED NUMBER- 2x0.25 IS 0.5 OF AVAILABLE SPACE
gen `offLeftX' = `DXmin' if `lci' < `DXmin' | `tauLCI' < `DXmin'
gen `offLeftX2' = `DXmin' + `DXwidth'*`arrowWidth' if `lci' < `DXmin' | `tauLCI' < `DXmin'
gen `offRightX' = `DXmax' if `uci' > `DXmax' | (`tauUCI' > `DXmax' & `tauLCI' < .)
gen `offRightX2' = `DXmax' - `DXwidth'*`arrowWidth' if `uci' > `DXmax' | (`tauUCI' > `DXmax' & `tauLCI' < .)
gen `offYlo' = `id' - `arrowHeight'
gen `offYhi' = `id' + `arrowHeight'
replace `lci' = `DXmin' if `lci' < `DXmin' & (`use' == 1 | `use' == 2)
replace `uci' = `DXmax' if `uci' > `DXmax' & (`use' == 1 | `use' == 2)
replace `lci' = . if `uci' < `DXmin' & (`use' == 1 | `use' == 2)
replace `uci' = . if `lci' > `DXmax' & (`use' == 1 | `use' == 2)
replace `effect' = . if `effect' < `DXmin' & (`use' == 1 | `use' == 2)
replace `effect' = . if `effect' > `DXmax' & (`use' == 1 | `use' == 2)
} // end qui
************************
** COLUMNS **
************************
// OPTIONS FOR L-R JUSTIFY?
// HAVE ONE MORE COL POSITION THAN NECESSARY, COULD THEN R-JUSTIFY
// BY ADDING 1 TO LOOP, ALSO HAVE MAX DIST FOR OUTER EDGE
// HAVE USER SPECIFY % OF GRAPH USED FOR TEXT?
qui{ // KEEP QUIET UNTIL AFTER DIAMONDS
local titleOff = 0
if "`lcols'" == ""{
local lcols = "`label'"
local titleOff = 1
}
// DOUBLE LINE OPTION
if "$MA_DOUBLE" != "" & ("`lcols'" != "" | "`rcols'" != ""){
tempvar expand orig
gen `orig' = _n
gen `expand' = 1
replace `expand' = 2 if `use' == 1
expand `expand'
sort `orig'
replace `id' = `id' - 0.45 if `id' == `id'[_n-1]
replace `use' = 2 if mod(`id',1) != 0 & `use' != 5
replace `effect' = . if mod(`id',1) != 0
replace `lci' = . if mod(`id',1) != 0
replace `uci' = . if mod(`id',1) != 0
replace `estText' = "" if mod(`id',1) != 0
cap replace `raw1' = "" if mod(`id',1) != 0
cap replace `raw2' = "" if mod(`id',1) != 0
replace `weightText' = "" if mod(`id',1) != 0
foreach var of varlist `lcols' `rcols'{
cap confirm string var `var'
if _rc == 0{
tempvar length words tosplit splitwhere best
gen `splitwhere' = 0
gen `best' = .
gen `length' = length(`var')
summ `length', det
gen `words' = wordcount(`var')
gen `tosplit' = 1 if `length' > r(max)/2+1 & `words' >= 2
summ `words', det
local max = r(max)
forvalues i = 1/`max'{
replace `splitwhere' = strpos(`var',word(`var',`i')) ///
if abs( strpos(`var',word(`var',`i')) - length(`var')/2 ) < `best' ///
& `tosplit' == 1
replace `best' = abs(strpos(`var',word(`var',`i')) - length(`var')/2) ///
if abs(strpos(`var',word(`var',`i')) - length(`var')/2) < `best'
}
replace `var' = substr(`var',1,(`splitwhere'-1)) if `tosplit' == 1 & mod(`id',1) == 0
replace `var' = substr(`var',`splitwhere',length(`var')) if `tosplit' == 1 & mod(`id',1) != 0
replace `var' = "" if `tosplit' != 1 & mod(`id',1) != 0 & `use' != 5
drop `length' `words' `tosplit' `splitwhere' `best'
}
if _rc != 0{
replace `var' = . if mod(`id',1) != 0 & `use' != 5
}
}
}
summ `id' if `use' != 9
local max = r(max)
local new = r(N)+4
if `new' > _N {
set obs `new'
}
forvalues i = 1/4{ // up to four lines for titles
local multip = 1
local add = 0
if "$MA_DOUBLE" != ""{ // DOUBLE OPTION- CLOSER TOGETHER, GAP BENEATH
local multip = 0.45
local add = 0.5
}
local idNew`i' = `max' + `i'*`multip' + `add'
local Nnew`i'=r(N)+`i'
local tmp = `Nnew`i''
replace `id' = `idNew`i'' + 1 in `tmp'
replace `use' = 1 in `tmp'
if `i' == 1{
global borderline = `idNew`i''-0.25
}
}
local maxline = 1
if "`lcols'" != ""{
tokenize "`lcols'"
local lcolsN = 0
while "`1'" != ""{
cap confirm var `1'
if _rc!=0 {
di in re "Variable `1' not defined"
exit _rc
}
local lcolsN = `lcolsN' + 1
tempvar left`lcolsN' leftLB`lcolsN' leftWD`lcolsN'
cap confirm string var `1'
if _rc == 0{
gen str `leftLB`lcolsN'' = `1'
}
if _rc != 0{
cap decode `1', gen(`leftLB`lcolsN'')
if _rc != 0{
local f: format `1'
gen str `leftLB`lcolsN'' = string(`1', "`f'")
replace `leftLB`lcolsN'' = "" if `leftLB`lcolsN'' == "."
}
}
replace `leftLB`lcolsN'' = "" if (`use' != 1 & `use' != 2)
local colName: variable label `1'
if "`colName'"==""{
local colName = "`1'"
}
// WORK OUT IF TITLE IS BIGGER THAN THE VARIABLE
// SPREAD OVER UP TO FOUR LINES IF NECESSARY
local titleln = length("`colName'")
tempvar tmpln
gen `tmpln' = length(`leftLB`lcolsN'')
qui summ `tmpln' if `use' != 0
local otherln = r(max)
drop `tmpln'
// NOW HAVE LENGTH OF TITLE AND MAX LENGTH OF VARIABLE
local spread = int(`titleln'/`otherln')+1
if `spread'>4{
local spread = 4
}
local line = 1
local end = 0
local count = -1
local c2 = -2
local first = word("`colName'",1)
local last = word("`colName'",`count')
local nextlast = word("`colName'",`c2')
while `end' == 0{
replace `leftLB`lcolsN'' = "`last'" + " " + `leftLB`lcolsN'' in `Nnew`line''
local check = `leftLB`lcolsN''[`Nnew`line''] + " `nextlast'" // what next will be
local count = `count'-1
local last = word("`colName'",`count')
if "`last'" == ""{
local end = 1
}
if length(`leftLB`lcolsN''[`Nnew`line'']) > `titleln'/`spread' | ///
length("`check'") > `titleln'/`spread' & "`first'" == "`nextlast'"{
if `end' == 0{
local line = `line'+1
}
}
}
if `line' > `maxline'{
local maxline = `line'
}
mac shift
}
}
if `titleOff' == 1 {
forvalues i = 1/4{
replace `leftLB1' = "" in `Nnew`i'' // get rid of horrible __var name
}
}
replace `leftLB1' = `label' if `use' != 1 & `use' != 2 // put titles back in (overall, sub est etc.)
// STUFF ADDED FOR JS TO INCLUDE EFFICACY AS COLUMN WITH OVERALL
*effect lci uci tempvars
if "$MA_efficacy" != ""{
tempvar vetemp ucivetemp lcivetemp vaccine_efficacy
qui {
gen `vetemp'=100*(1-exp(`effect'))
tostring `vetemp', replace force format(%4.0f)
gen `ucivetemp'=100*(1-exp(`lci'))
tostring `ucivetemp', replace force format(%4.0f)
gen `lcivetemp'=100*(1-exp(`uci'))
tostring `lcivetemp', replace force format(%4.0f)
gen str30 `vaccine_efficacy'=`vetemp'+" ("+`lcivetemp'+", "+`ucivetemp'+")" if `effect' != .
label var `vaccine_efficacy' "Vaccine efficacy (%)"
local rcols = "`vaccine_efficacy' " + "`rcols' "
}
}
if "`wt'" == ""{
local rcols = "`weightText' " + "`rcols'"
if "$MA_method2" != ""{
label var `weightText' "% Weight ($MA_method1)"
}
else{
label var `weightText' "% Weight"
}
}
if "`counts'" != ""{
local rcols = "`raw1' " + "`raw2' " + "`rcols'"
}
if "`stats'" == ""{
local rcols = "`estText' " + "`rcols'"
if "$MA_ESLA" == ""{
global MA_ESLA = "`sumstat'"
}
label var `estText' "$MA_ESLA ($IND% CI)"
}
tempvar extra
gen `extra' = ""
label var `extra' " "
local rcols = "`rcols' `extra'"
local rcolsN = 0
if "`rcols'" != ""{
tokenize "`rcols'"
local rcolsN = 0
while "`1'" != ""{
cap confirm var `1'
if _rc!=0 {
di in re "Variable `1' not defined"
exit _rc
}
local rcolsN = `rcolsN' + 1
tempvar right`rcolsN' rightLB`rcolsN' rightWD`rcolsN'
cap confirm string var `1'
if _rc == 0{
gen str `rightLB`rcolsN'' = `1'
}
if _rc != 0{
local f: format `1'
gen str `rightLB`rcolsN'' = string(`1', "`f'")
replace `rightLB`rcolsN'' = "" if `rightLB`rcolsN'' == "."
}
local colName: variable label `1'
if "`colName'"==""{
local colName = "`1'"
}
// WORK OUT IF TITLE IS BIGGER THAN THE VARIABLE
// SPREAD OVER UP TO FOUR LINES IF NECESSARY
local titleln = length("`colName'")
tempvar tmpln
gen `tmpln' = length(`rightLB`rcolsN'')
qui summ `tmpln' if `use' != 0
local otherln = r(max)
drop `tmpln'
// NOW HAVE LENGTH OF TITLE AND MAX LENGTH OF VARIABLE
local spread = int(`titleln'/`otherln')+1
if `spread'>4{
local spread = 4
}
local line = 1
local end = 0
local count = -1
local c2 = -2
local first = word("`colName'",1)
local last = word("`colName'",`count')
local nextlast = word("`colName'",`c2')
while `end' == 0{
replace `rightLB`rcolsN'' = "`last'" + " " + `rightLB`rcolsN'' in `Nnew`line''
local check = `rightLB`rcolsN''[`Nnew`line''] + " `nextlast'" // what next will be
local count = `count'-1
local last = word("`colName'",`count')
if "`last'" == ""{
local end = 1
}
if length(`rightLB`rcolsN''[`Nnew`line'']) > `titleln'/`spread' | ///
length("`check'") > `titleln'/`spread' & "`first'" == "`nextlast'"{
if `end' == 0{
local line = `line'+1
}
}
}
if `line' > `maxline'{
local maxline = `line'
}
mac shift
}
}
// now get rid of extra title rows if they weren't used
if `maxline'==3{
drop in `Nnew4'
}
if `maxline'==2{
drop in `Nnew3'/`Nnew4'
}
if `maxline'==1{
drop in `Nnew2'/`Nnew4'
}
/* BODGE SOLU- EXTRA COLS */
while `rcolsN' < 2{
local rcolsN = `rcolsN' + 1
tempvar right`rcolsN' rightLB`rcolsN' rightWD`rcolsN'
gen str `rightLB`rcolsN'' = " "
}
local skip = 1
if "`stats'" == "" & "`wt'" == ""{ // sort out titles for stats and weight, if there
local skip = 3
}
if "`stats'" != "" & "`wt'" == ""{
local skip = 2
}
if "`stats'" == "" & "`wt'" != ""{
local skip = 2
}
if "`counts'" != ""{
local skip = `skip' + 2
}
if "$MA_efficacy" != ""{
local skip = `skip' + 1
}
/* SET TWO DUMMY RCOLS IF NOSTATS NOWEIGHT */
forvalues i = `skip'/`rcolsN'{ // get rid of junk if not weight, stats or counts
replace `rightLB`i'' = "" if (`use' != 1 & `use' != 2)
}
forvalues i = 1/`rcolsN'{
replace `rightLB`i'' = "" if (`use' ==0)
}
local leftWDtot = 0
local rightWDtot = 0
local leftWDtotNoTi = 0
forvalues i = 1/`lcolsN'{
getWidth `leftLB`i'' `leftWD`i''
qui summ `leftWD`i'' if `use' != 0 & `use' != 4 & `use' != 3 & `use' != 5 & ///
`use' != 17 & `use' != 19 // DON'T INCLUDE OVERALL STATS AT THIS POINT
local maxL = r(max)
local leftWDtotNoTi = `leftWDtotNoTi' + `maxL'
replace `leftWD`i'' = `maxL'
}
tempvar titleLN // CHECK IF OVERALL LENGTH BIGGER THAN REST OF LCOLS
getWidth `leftLB1' `titleLN'
qui summ `titleLN' if `use' != 0 & `use' != 4
local leftWDtot = max(`leftWDtotNoTi', r(max))
forvalues i = 1/`rcolsN'{
getWidth `rightLB`i'' `rightWD`i''
qui summ `rightWD`i'' if `use' != 0 & `use' != 4
replace `rightWD`i'' = r(max)
local rightWDtot = `rightWDtot' + r(max)
}
// CHECK IF NOT WIDE ENOUGH (I.E., OVERALL INFO TOO WIDE)
// LOOK FOR EDGE OF DIAMOND summ `lci' if `use' == ...
tempvar maxLeft
getWidth `leftLB1' `maxLeft'
qui count if `use' == 3 | `use' == 5 | `use' == 17 | `use' == 19
if r(N) > 0{
summ `maxLeft' if `use' == 3 | `use' == 5 | `use' == 17 | `use' == 19 // NOT TITLES THOUGH!
local max = r(max)
if `max' > `leftWDtotNoTi'{
// WORK OUT HOW FAR INTO PLOT CAN EXTEND
// WIDTH OF LEFT COLUMNS AS FRACTION OF WHOLE GRAPH
local x = `leftWDtot'*($MA_AS_TEXT/100)/(`leftWDtot'+`rightWDtot')
tempvar y
// SPACE TO LEFT OF DIAMOND WITHIN PLOT (FRAC OF GRAPH)
gen `y' = ((100-$MA_AS_TEXT)/100)*(`lci'-`DXmin') / (`DXmax'-`DXmin')
qui summ `y' if `use' == 3 | `use' == 5
local extend = 1*(r(min)+`x')/`x'
local leftWDtot = max(`leftWDtot'/`extend',`leftWDtotNoTi') // TRIM TO KEEP ON SAFE SIDE
// ALSO MAKE SURE NOT LESS THAN BEFORE!
}
}
global LEFT_WD = `leftWDtot'
global RIGHT_WD = `rightWDtot'
local ratio = $MA_AS_TEXT // USER SPECIFIED- % OF GRAPH TAKEN BY TEXT (ELSE NUM COLS CALC?)
local textWD = (`DXwidth'/(1-`ratio'/100)-`DXwidth') /(`leftWDtot'+`rightWDtot')
forvalues i = 1/`lcolsN'{
gen `left`i'' = `DXmin' - `leftWDtot'*`textWD'
local leftWDtot = `leftWDtot'-`leftWD`i''
}
gen `right1' = `DXmax'
forvalues i = 2/`rcolsN'{
local r2 = `i'-1
gen `right`i'' = `right`r2'' + `rightWD`r2''*`textWD'
}
local AXmin = `left1'
local AXmax = `DXmax' + `rightWDtot'*`textWD'
foreach type in "" "inf"{
replace `tauLCI`inf'' = `DXmin' if `tauLCI' < `DXmin' & `tauLCI`inf'' != .
replace `tauLCI`inf'' = . if `lci' < `DXmin'
replace `tauLCI`inf'' = . if `tauLCI`inf'' > `lci'
replace `tauUCI`inf'' = `DXmax' if `tauUCI`inf'' > `DXmax' & `tauUCI`inf'' != .
replace `tauUCI`inf'' = . if `uci' > `DXmax'
replace `tauUCI`inf'' = . if `tauUCI`inf'' < `uci'
replace `tauLCI`inf'' = . if (`use' == 3 | `use' == 5) & "$MA_method1" != "D+L"
replace `tauUCI`inf'' = . if (`use' == 3 | `use' == 5) & "$MA_method1" != "D+L"
replace `tauLCI`inf'' = . if (`use' == 17 | `use' == 19) & "$MA_method2" != "D+L"
replace `tauUCI`inf'' = . if (`use' == 17 | `use' == 19) & "$MA_method2" != "D+L"
}
// DIAMONDS TAKE FOREVER...I DON'T THINK THIS IS WHAT MIKE DID
tempvar DIAMleftX DIAMrightX DIAMbottomX DIAMtopX DIAMleftY1 DIAMrightY1 DIAMleftY2 DIAMrightY2 DIAMbottomY DIAMtopY
gen `DIAMleftX' = `lci' if `use' == 3 | `use' == 5 | `use' == 17 | `use' == 19
replace `DIAMleftX' = `DXmin' if `lci' < `DXmin' & (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19)
replace `DIAMleftX' = . if `effect' < `DXmin' & (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19)
gen `DIAMleftY1' = `id' if `use' == 3 | `use' == 5 | `use' == 17 | `use' == 19
replace `DIAMleftY1' = `id' + 0.4*( abs((`DXmin'-`lci')/(`effect'-`lci')) ) if `lci' < `DXmin' & (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19)
replace `DIAMleftY1' = . if `effect' < `DXmin' & (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19)
gen `DIAMleftY2' = `id' if `use' == 3 | `use' == 5 | `use' == 17 | `use' == 19
replace `DIAMleftY2' = `id' - 0.4*( abs((`DXmin'-`lci')/(`effect'-`lci')) ) if `lci' < `DXmin' & (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19)
replace `DIAMleftY2' = . if `effect' < `DXmin' & (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19)
gen `DIAMrightX' = `uci' if `use' == 3 | `use' == 5 | `use' == 17 | `use' == 19
replace `DIAMrightX' = `DXmax' if `uci' > `DXmax' & (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19)
replace `DIAMrightX' = . if `effect' > `DXmax' & (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19)
gen `DIAMrightY1' = `id' if `use' == 3 | `use' == 5 | `use' == 17 | `use' == 19
replace `DIAMrightY1' = `id' + 0.4*( abs((`uci'-`DXmax')/(`uci'-`effect')) ) if `uci' > `DXmax' & (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19)
replace `DIAMrightY1' = . if `effect' > `DXmax' & (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19)
gen `DIAMrightY2' = `id' if `use' == 3 | `use' == 5 | `use' == 17 | `use' == 19
replace `DIAMrightY2' = `id' - 0.4*( abs((`uci'-`DXmax')/(`uci'-`effect')) ) if `uci' > `DXmax' & (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19)
replace `DIAMrightY2' = . if `effect' > `DXmax' & (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19)
gen `DIAMbottomY' = `id' - 0.4 if `use' == 3 | `use' == 5 | `use' == 17 | `use' == 19
replace `DIAMbottomY' = `id' - 0.4*( abs((`uci'-`DXmin')/(`uci'-`effect')) ) if `effect' < `DXmin' & (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19)
replace `DIAMbottomY' = `id' - 0.4*( abs((`DXmax'-`lci')/(`effect'-`lci')) ) if `effect' > `DXmax' & (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19)
gen `DIAMtopY' = `id' + 0.4 if `use' == 3 | `use' == 5 | `use' == 17 | `use' == 19
replace `DIAMtopY' = `id' + 0.4*( abs((`uci'-`DXmin')/(`uci'-`effect')) ) if `effect' < `DXmin' & (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19)
replace `DIAMtopY' = `id' + 0.4*( abs((`DXmax'-`lci')/(`effect'-`lci')) ) if `effect' > `DXmax' & (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19)
gen `DIAMtopX' = `effect' if `use' == 3 | `use' == 5 | `use' == 17 | `use' == 19
replace `DIAMtopX' = `DXmin' if `effect' < `DXmin' & (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19)
replace `DIAMtopX' = `DXmax' if `effect' > `DXmax' & (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19)
replace `DIAMtopX' = . if (`uci' < `DXmin' | `lci' > `DXmax') & (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19)
gen `DIAMbottomX' = `DIAMtopX'
} // END QUI
// v1.11 TEXT SIZE SOLU
// v1.16 TRYING AGAIN!
// IF aspect IS USED IN "$MA_OTHEROPTS" (OTHER GRAPH OPTS) THEN THIS HELPS TO CALCULATE TEXT SIZE
// IF NO ASPECT, BUT xsize AND ysize USED THEN FIND RATIO MANUALLY
// STATA ALWAYS TRIES TO PRODUCE A GRAPH WITH ASPECT ABOUT 0.77 - TRY TO FIND "NATURAL ASPECT"
local aspect = .
if strpos(`"$MA_OTHEROPTS"',"aspect") > 0{
local aspectTXT = substr( `"$MA_OTHEROPTS"', (strpos(`"$MA_OTHEROPTS"',"aspect")), (length(`"$MA_OTHEROPTS"')) )
local aspectTXT = substr( "`aspectTXT'", 1, ( strpos("`aspectTXT'",")")) )
local aspect = real( substr( "`aspectTXT'", ( strpos("`aspectTXT'","(") +1 ), ///
( strpos("`aspectTXT'",")") - strpos("`aspectTXT'","(") -1 ) ))
}
if strpos(`"$MA_OTHEROPTS"',"xsize") > 0 ///
& strpos(`"$MA_OTHEROPTS"',"ysize") > 0 ///
& strpos(`"$MA_OTHEROPTS"',"aspect") == 0{
local xsizeTXT = substr( `"$MA_OTHEROPTS"', (strpos(`"$MA_OTHEROPTS"',"xsize")), (length(`"$MA_OTHEROPTS"')) )
// Ian White's bug fix!
local xsizeTXT = substr( `"`xsizeTXT'"', 1, ( strpos(`"`xsizeTXT'"',")")) )
local xsize = real( substr( `"`xsizeTXT'"', ( strpos(`"`xsizeTXT'"',"(") +1 ), ///
( strpos(`"`xsizeTXT'"',")") - strpos(`"`xsizeTXT'"',"(") -1 ) ))
local ysizeTXT = substr( `"$MA_OTHEROPTS"', (strpos(`"$MA_OTHEROPTS"',"ysize")), (length(`"$MA_OTHEROPTS"')) )
local ysizeTXT = substr( `"`ysizeTXT'"', 1, ( strpos(`"`ysizeTXT'"',")")) )
local ysize = real( substr( `"`ysizeTXT'"', ( strpos(`"`ysizeTXT'"',"(") +1 ), ///
( strpos(`"`ysizeTXT'"',")") - strpos(`"`ysizeTXT'"',"(") -1 ) ))
local aspect = `ysize'/`xsize'
}
local approx_chars = ($LEFT_WD + $RIGHT_WD)/($MA_AS_TEXT/100)
qui count if `use' != 9
local height = r(N)
local natu_aspect = 1.3*`height'/`approx_chars'
if `aspect' == .{
// sort out relative to text, but not to ridiculous degree
local new_asp = 0.5*`natu_aspect' + 0.5*1
global MA_OTHEROPTS `"$MA_OTHEROPTS aspect(`new_asp')"'
local aspectRat = max( `new_asp'/`natu_aspect' , `natu_aspect'/`new_asp' )
}
if `aspect' != .{
local aspectRat = max( `aspect'/`natu_aspect' , `natu_aspect'/`aspect' )
}
local adj = 1.25
if `natu_aspect' > 0.7{
local adj = 1/(`natu_aspect'^1.3+0.2)
}
local textSize = `adj' * $MA_TEXT_SCA / (`approx_chars' * sqrt(`aspectRat') )
local textSize2 = `adj' * $MA_TEXT_SCA / (`approx_chars' * sqrt(`aspectRat') )
forvalues i = 1/`lcolsN'{
local lcolCommands`i' "(scatter `id' `left`i'' if `use' != 4, msymbol(none) mlabel(`leftLB`i'') mlabcolor(black) mlabpos(3) mlabsize(`textSize')) "
}
forvalues i = 1/`rcolsN'{
local rcolCommands`i' "(scatter `id' `right`i'' if `use' != 4, msymbol(none) mlabel(`rightLB`i'') mlabcolor(black) mlabpos(3) mlabsize(`textSize')) "
}
if "$MA_rfdist" != ""{
if "`stats'" == ""{
local predIntCmd "(scatter `id' `right1' if `use' == 4, msymbol(none) mlabel(`RFdistText') mlabcolor(black) mlabpos(3) mlabsize(`textSize')) "
}
if "$MA_nohet" == ""{
local predIntCmd2 "(scatter `id' `left1' if `use' == 4, msymbol(none) mlabel(`RFdistLabel') mlabcolor(black) mlabpos(3) mlabsize(`textSize')) "
}
}
if "$MA_nohet" == "" & "$MA_rjhby" != ""{
local hetGroupCmd "(scatter `id' `left1' if `use' == 4, msymbol(none) mlabel(`hetGroupLabel') mlabcolor(black) mlabpos(3) mlabsize(`textSize')) "
}
// OTHER BITS AND BOBS
local dispBox "none"
if "`nobox'" == ""{
local dispBox "square "
}
local boxsize = $MA_FBSC/150
if "$MA_FAVOURS" != ""{
local pos = strpos("$MA_FAVOURS", "#")
local leftfav = substr("$MA_FAVOURS",1,(`pos'-1))
local rightfav = substr("$MA_FAVOURS",(`pos'+1),(length("$MA_FAVOURS")-`pos'+1) )
}
if `h0' != . & "$MA_nulloff" == ""{
local leftfp = `DXmin' + (`h0'-`DXmin')/2
local rightfp = `h0' + (`DXmax'-`h0')/2
}
else{
local leftfp = `DXmin'
local rightfp = `DXmax'
}
// GRAPH APPEARANCE OPTIONS- ADDED v1.15
/*
if `"$MA_OPT"' != "" & strpos(`"$MA_OPT"',"m") == 0{(
global MA_OPT = `"$MA_OPT m()"'
}
*/
if `"$MA_BOXOPT"' != "" & strpos(`"$MA_BOXOPT"',"msymbol") == 0{ // make defaults if unspecified
global MA_BOXOPT = `"$MA_BOXOPT msymbol(square)"'
}
if `"$MA_BOXOPT"' != "" & strpos(`"$MA_BOXOPT"',"mcolor") == 0{ // make defaults if unspecified
global MA_BOXOPT = `"$MA_BOXOPT mcolor("180 180 180")"'
}
if `"$MA_BOXOPT"' == ""{
local boxopt "msymbol(`dispBox') msize(`boxsize') mcolor("180 180 180")"
}
else{
if strpos(`"$MA_BOXOPT"',"mla") != 0{
di as error "Option mlabel() not allowed in boxopt()"
exit
}
if strpos(`"$MA_BOXOPT"',"msi") != 0{
di as error "Option msize() not allowed in boxopt()"
exit
}
local boxopt `"$MA_BOXOPT msize(`boxsize')"'
}
if "$MA_classic" != ""{
local boxopt "mcolor(black) msymbol(square) msize(`boxsize')"
}
if "`box'" != ""{
local boxopt "msymbol(none)"
}
if `"$MA_DIAMOPT"' == ""{
local diamopt "lcolor("0 0 100")"
}
else{
if strpos(`"$MA_DIAMOPT"',"hor") != 0 | strpos(`"$MA_DIAMOPT"',"vert") != 0{
di as error "Options horizontal/vertical not allowed in diamopt()"
exit
}
if strpos(`"$MA_DIAMOPT"',"con") != 0{
di as error "Option connect() not allowed in diamopt()"
exit
}
if strpos(`"$MA_DIAMOPT"',"lp") != 0{
di as error "Option lpattern() not allowed in diamopt()"
exit
}
local diamopt `"$MA_DIAMOPT"'
}
if `"$MA_POINTOPT"' != "" & strpos(`"$MA_POINTOPT"',"msymbol") == 0{(
global MA_POINTOPT = `"$MA_POINTOPT msymbol(diamond)"'
}
if `"$MA_POINTOPT"' != "" & strpos(`"$MA_POINTOPT"',"msize") == 0{(
global MA_POINTOPT = `"$MA_POINTOPT msize(vsmall)"'
}
if `"$MA_POINTOPT"' != "" & strpos(`"$MA_POINTOPT"',"mcolor") == 0{(
global MA_POINTOPT = `"$MA_POINTOPT mcolor(black)"'
}
if `"$MA_POINTOPT"' == ""{
local pointopt "msymbol(diamond) msize(vsmall) mcolor("0 0 0")"
}
else{
local pointopt `"$MA_POINTOPT"'
}
if "$MA_classic" != "" & "`box'" == ""{
local pointopt "msymbol(none)"
}
if `"$MA_CIOPT"' != "" & strpos(`"$MA_CIOPT"',"lcolor") == 0{(
global MA_CIOPT = `"$MA_CIOPT lcolor(black)"'
}
if `"$MA_CIOPT"' == ""{
local ciopt "lcolor("0 0 0")"
}
else{
if strpos(`"$MA_CIOPT"',"hor") != 0 | strpos(`"$MA_CIOPT"',"vert") != 0{
di as error "Options horizontal/vertical not allowed in ciopt()"
exit
}
if strpos(`"$MA_CIOPT"',"con") != 0{
di as error "Option connect() not allowed in ciopt()"
exit
}
if strpos(`"$MA_CIOPT"',"lp") != 0{
di as error "Option lpattern() not allowed in ciopt()"
exit
}
local ciopt `"$MA_CIOPT"'
}
// END GRAPH OPTS
if "$MA_method1" == "D+L"{
tempvar noteposx noteposy notelab
qui{
summ `id'
gen `noteposy' = r(min) -1.5 in 1
summ `left1'
gen `noteposx' = r(mean) in 1
gen `notelab' = "NOTE: Weights are from random effects analysis" in 1
local notecmd "(scatter `noteposy' `noteposx', msymbol(none) mlabel(`notelab') mlabcolor(black) mlabpos(3) mlabsize(`textSize')) "
}
if "$MA_nowarning" != ""{
local notecmd
}
}
if "`overall'" != ""{
local overallCommand ""
qui drop if `use' == 5
qui summ `id'
local DYmin = r(min)
cap replace `noteposy' = r(min) -.5 in 1
}
// quick bodge to get overall- can't find log version!
tempvar tempOv ovLine ovMin ovMax h0Line
qui gen `tempOv' = `effect' if `use' == 5
sort `tempOv'
qui summ `id'
local DYmin = r(min)-2
local DYmax = r(max)+1
qui gen `ovLine' = `tempOv' in 1
qui gen `ovMin' = r(min)-2 in 1
qui gen `ovMax' = $borderline in 1
qui gen `h0Line' = `h0' in 1
if `"$MA_OLINEOPT"' == ""{
local overallCommand " (pcspike `ovMin' `ovLine' `ovMax' `ovLine', lwidth(thin) lcolor(maroon) lpattern(shortdash)) "
}
else{
local overallCommand `" (pcspike `ovMin' `ovLine' `ovMax' `ovLine', $MA_OLINEOPT) "'
}
if `ovLine' > `DXmax' | `ovLine' < `DXmin' | "`overall'" != ""{ // ditch if not on graph
local overallCommand ""
}
local nullCommand " (pcspike `ovMin' `h0Line' `ovMax' `h0Line', lwidth(thin) lcolor(black) ) "
// gap if "favours" used
if "`leftfav'" != "" | "`rightfav'" != ""{
local gap = "labgap(5)"
}
// if summary only must not have weights
local awweight "[aw= `weight']"
if "$MA_summaryonly" != ""{
local awweight ""
}
qui summ `weight'
if r(N) == 0{
local awweight ""
}
// rfdist off scale arrows only used when appropriate
qui{
tempvar rfarrow
gen `rfarrow' = 0
if "$MA_rfdist" != ""{
if "$MA_method1" == "D+L"{
replace `rfarrow' = 1 if `use' == 3 | `use' == 5
}
if "$MA_method2" == "D+L"{
replace `rfarrow' = 1 if `use' == 17 | `use' == 19
}
}
} // end qui
// final addition- if aspect() given but not xsize() ysize(), put these in to get rid of gaps
// need to fiddle to allow space for bottom title
// should this just replace the aspect option?
// suppose good to keep- most people hopefully using xsize and ysize and can always change themselves if using aspect
if strpos(`"$MA_OTHEROPTS"',"xsize") == 0 & strpos(`"$MA_OTHEROPTS"',"ysize") == 0 ///
& strpos(`"$MA_OTHEROPTS"',"aspect") > 0 {
local aspct = substr(`"$MA_OTHEROPTS"', (strpos(`"$MA_OTHEROPTS"',"aspect(")+7 ) , length(`"$MA_OTHEROPTS"') )
local aspct = substr(`"`aspct'"', 1, (strpos(`"`aspct'"',")")-1) )
if `aspct' > 1{
local xx = (11.5+(2-2*1/`aspct'))/`aspct'
local yy = 12
}
if `aspct' <= 1{
local yy = 12*`aspct'
local xx = 11.5-(2-2*`aspct')
}
global MA_OTHEROPTS = `"$MA_OTHEROPTS"' + " xsize(`xx') ysize(`yy')"
}
// switch off null if wanted
if "$MA_nulloff" != ""{
local nullCommand ""
}
***************************
*** GRAPH ***
***************************
#delimit ;
twoway
/* NOTE FOR RF, OVERALL AND NULL LINES FIRST */
`notecmd' `overallCommand' `nullCommand' `predIntCmd' `predIntCmd2' `hetGroupCmd'
/* PLOT BOXES AND PUT ALL THE GRAPH OPTIONS IN THERE */
(scatter `id' `effect' `awweight' if `use' == 1,
`boxopt'
yscale(range(`DYmin' `DYmax') noline )
ylabel(none) ytitle("")
xscale(range(`AXmin' `AXmax'))
xlabel(`lblcmd', labsize(`textSize2') )
yline($borderline, lwidth(thin) lcolor(gs12))
/* THIS BIT DOES favours. NOTE SPACES TO SUPPRESS IF THIS IS NOT USED */
xmlabel(`leftfp' "`leftfav' " `rightfp' "`rightfav' ", noticks labels labsize(`textSize')
`gap' /* PUT LABELS UNDER xticks? Yes as labels now extended */ )
xtitle("") legend(off) xtick("`xtick'") )
/* END OF FIRST SCATTER */
/* HERE ARE THE CONFIDENCE INTERVALS */
(pcspike `id' `lci' `id' `uci' if `use' == 1, `ciopt')
/* ADD ARROWS IF OFFSCALE USING offLeftX offLeftX2 offRightX offRightX2 offYlo offYhi */
(pcspike `id' `offLeftX' `offYlo' `offLeftX2' if `use' == 1, `ciopt')
(pcspike `id' `offLeftX' `offYhi' `offLeftX2' if `use' == 1, `ciopt')
(pcspike `id' `offRightX' `offYlo' `offRightX2' if `use' == 1, `ciopt')
(pcspike `id' `offRightX' `offYhi' `offRightX2' if `use' == 1, `ciopt')
/* DIAMONDS FOR SUMMARY ESTIMATES -START FROM 9 O'CLOCK */
(pcspike `DIAMleftY1' `DIAMleftX' `DIAMtopY' `DIAMtopX' if `use' == 3 | `use' == 5 | `use' == 17 | `use' == 19, `diamopt')
(pcspike `DIAMtopY' `DIAMtopX' `DIAMrightY1' `DIAMrightX' if `use' == 3 | `use' == 5 | `use' == 17 | `use' == 19, `diamopt')
(pcspike `DIAMrightY2' `DIAMrightX' `DIAMbottomY' `DIAMbottomX' if `use' == 3 | `use' == 5 | `use' == 17 | `use' == 19, `diamopt')
(pcspike `DIAMbottomY' `DIAMbottomX' `DIAMleftY2' `DIAMleftX' if `use' == 3 | `use' == 5 | `use' == 17 | `use' == 19, `diamopt')
/* EXTENDED CI FOR RANDOM EFFECTS, SHOW DISTRIBUTION AS RECOMMENDED BY JULIAN HIGGINS
DOTTED LINES FOR INESTIMABLE DISTRIBUTION */
(pcspike `DIAMleftY1' `DIAMleftX' `DIAMleftY1' `tauLCI' if (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19) & `tau2' < ., `diamopt')
(pcspike `DIAMrightY1' `DIAMrightX' `DIAMrightY1' `tauUCI' if (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19) & `tau2' < ., `diamopt')
(pcspike `DIAMleftY1' `DIAMleftX' `DIAMleftY1' `tauLCI' if (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19) & `tau2' ==.b, `diamopt' lpattern(shortdash))
(pcspike `DIAMrightY1' `DIAMrightX' `DIAMrightY1' `tauUCI' if (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19) & `tau2' ==.b, `diamopt' lpattern(shortdash))
/* DIAMOND EXTENSION FOR RF DIST ALSO HAS ARROWS... */
(pcspike `id' `offLeftX' `offYlo' `offLeftX2' if (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19) & `rfarrow' == 1, `diamopt')
(pcspike `id' `offLeftX' `offYhi' `offLeftX2' if (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19) & `rfarrow' == 1, `diamopt')
(pcspike `id' `offRightX' `offYlo' `offRightX2' if (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19) & `rfarrow' == 1, `diamopt')
(pcspike `id' `offRightX' `offYhi' `offRightX2' if (`use' == 3 | `use' == 5 | `use' == 17 | `use' == 19) & `rfarrow' == 1, `diamopt')
/* COLUMN VARIBLES */
`lcolCommands1' `lcolCommands2' `lcolCommands3' `lcolCommands4' `lcolCommands5' `lcolCommands6'
`lcolCommands7' `lcolCommands8' `lcolCommands9' `lcolCommands10' `lcolCommands11' `lcolCommands12'
`rcolCommands1' `rcolCommands2' `rcolCommands3' `rcolCommands4' `rcolCommands5' `rcolCommands6'
`rcolCommands7' `rcolCommands8' `rcolCommands9' `rcolCommands10' `rcolCommands11' `rcolCommands12'
(scatter `id' `right1' if `use' != 4 & `use' != 0,
msymbol(none) mlabel(`rightLB1') mlabcolor("0 0 0") mlabpos(3) mlabsize(`textSize'))
(scatter `id' `right2' if `use' != 4 & `use' != 0,
msymbol(none) mlabel(`rightLB2') mlabcolor("0 0 0") mlabpos(3) mlabsize(`textSize'))
/* (scatter `id' `right2', mlabel(`use')) JUNK, TO SEE WHAT'S WHERE */
/* LAST OF ALL PLOT EFFECT MARKERS TO CLARIFY AND OVERALL EFFECT LINE */
(scatter `id' `effect' if `use' == 1, `pointopt' )
, $MA_OTHEROPTS /* RMH added */ plotregion(margin(zero));
#delimit cr
end
program define getWidth
version 9.0
// ROSS HARRIS, 13TH JULY 2006
// TEXT SIZES VARY DEPENDING ON CHARACTER
// THIS PROGRAM GENERATES APPROXIMATE DISPLAY WIDTH OF A STRING
// FIRST ARG IS STRING TO MEASURE, SECOND THE NEW VARIABLE
// PREVIOUS CODE DROPPED COMPLETELY AND REPLACED WITH SUGGESTION
// FROM Jeff Pitblado
qui{
gen `2' = 0
count
local N = r(N)
forvalues i = 1/`N'{
local this = `1'[`i']
local width: _length "`this'"
replace `2' = `width' +1 in `i'
}
} // end qui
end
exit
// METAN UPDATE
// ROSS HARRIS, DEC 2006
// MAIN UPDATE IS GRAPHICS IN THE _dispgby PROGRAM
// ADDITIONAL OPTIONS ARE lcols AND rcols
// THESE AFFECT DISPLAY ONLY AND ALLOW USER TO SPECIFY
// VARIABLES AS A FORM OF TABLE. THIS EXTENDS THE label(namevar yearvar)
// SYNTAX, ALLOWING AS MANY LEFT COLUMNS AS REQUIRED (WELL, LIMIT IS 10)
// IF rcols IS OMMITTED DEFAULT IS THE STUDY EFFECT (95% CI) AND WEIGHT
// AS BEFORE- THESE ARE ALWAYS IN UNLESS OMITTED USING OPTIONS
// ANYTHING ADDED TO rcols COMES AFTER THIS.
// v1.4 WAS GETTING THERE
// v1.5 SORT OUT EXTRA LINE AT THE TOP AND ALLOW "DOUBLE LINES"
// FOR FIXED AND RANDOM EFFECTS
// SOMETHING TO ADD- RETURN ES_2?
// v1.7 - TRY TO SORT OUT LABELLING
// CHANGED LABELS TO 7.3g -WORKS NICELY
// "FAVOURS" NOW WORKS- USES xmlabel
// v1.8 ADDED IN COUNTS OPTION, SORTED OUT TEXTSIZE, PROPER DEFINITION AND SPLIT OF VAR LABELS
// v1.9 DELETE UNECESSARY OPTIONS
// OH, AND ADD effect OPTION
// v1.10 FINAL TIDYING, USED Jeff Pitblado's SUGGESTION FOR getWidth
// v1.11 USE label() OPTIONS IF NO lcols rcols, WORK ON AUTO FIT TEXT
// v1.12 FURTHER WORK...
// v1.14 DONE ON 12TH OCTOBER, FINALLY DISCOVERED WHAT IS CAUSING PROBLEM
// WITH "NON-MATCHING CLOSE BRACE" AT END OF FILE- NO v7 STYLE IF STATEMENTS!
// EVERYTHING GOES ON A SEPARATE LINE NOW. PHEW.
// v1.15 NOW ADDING IN OPTIONS TO CONTROL BOXES, CI LINES, OVERALL
// TITLES WEREN'T SPREADING ACROSS SINCE OPTION TO CONTROL OVERALL TEXT- FIXED AGAIN
// v1.16 LAST ATTEMPT TO GET TEXT SIZE RIGHT! WORK OUT WHAT ASPECT SHOULD BE AND USE
// IF ASPECT DEFINED THEN DECREASE TEXT SIZE BY RATIO TO IDEAL ASPECT
// TEXT SCALING WORKING BETTER NOW
// LAST THING TO TRY TO SORT IS LOOK FOR LEFT OF DIAMOND AND MOVE HET STATS
// COULD PUT UNDERNEATH IF NOT MUCH SPACE? THIS WOULD BE GOOD v1.17
// STILL DEBATING WHETHER TO PUT favours BIT BELOW xticks...
// V19 LOTS OF COMMENTS FROM JONATHAN AND BITS TO DO. SUMMARY:
// aspect Y
// note if random weights Y
// update to v8 Y
// graph in mono Y
// extend overall text into plot Y
// labels Y
// help file not v8 yet
// v1.21 EVERY PROGRAM NOW CONVERTED TO v9.0, NO "CODE FOLLOWS BRACES!"
// WHAT ELSE DID PATRICK DO TO UPDATE TO v8?
// NO "#delimit ;" - I QUITE LIKE THIS THOUGH!
// GLOBALS ARE DECLARED WITHOUT = SIGN - EXCEPT WHEN USING STRING FUNCTION ETC. IT DOESN'T LIKE THIS!
// - WILL THIS EVER CAUSE PROBLEMS?
// - CAN'T BE BOTHERED TO CHANGE ALL THE NUMERIC ONES
// USE TOKENIZE INSTEAD OF PARSE - DONE
// USE di as err NOT in red, EXIT - DONE, PROPER RETURN CODES STILL NEEDED, MAYBE SOMEDAY!
// DECENT HELP FILE - USED, JUST ADD IN NEW BITS
// v1.22 ENCODE STUFF FOR metanby NOW LABORIOUSLY RECODED SO THAT GROUPS ARE IN ORIGINAL SORT ORDER
// THIS IS USEFUL IF YOU DON'T WANT STUFF IN ALPHA-NUMERIC ORDER, OR TO PUT "1..." "2..." ETC.
// counts OPTION DOES NOT WORK WITH MEAN DIFFERENCES- AND LOOKS LIKE IT NEVER DID- PUT IN
// DO OWN LINES FOR OVERALL ETC. EXTENDS TOO FAR
// LABELS NEVER RUN TO FOUR LINES- SORT OUT- QUICK SOLU- DO FIVE TIMES AND DROP ONE!
// v1.23 USES pcspike FOR OVERALL AND NULL LINES TO PREVENT OVER-EXTENDING
// NOW HAS OPTION FOR USER DEFINED "SECOND" ANALYSIS
// v1.24 ALLOW USER TO COMPLETELY DEFINE ANALYSIS WITH WEIGHTS
// v2.34 problem with nosubgroup nooverall sorted out (this combination failed)
********************
** May 2007 fixes **
********************
// "nostandard" had disappeared from help file- back in
// I sq. in return list
// sorted out the extra top line that appears in column labels
// fixed when using aspect ratio using xsize and ysize so inner bit matches graph area- i.e., get rid of spaces for long/wide graphs
// variable display format preserved for lcols and rcols
// abbreviated varlist now allowed
// between groups het. only available with fixed
// warnings if any heterogeneity with fixed (for between group het if any sub group has het, overall est if any het)
// nulloff option to get rid of line