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.
350 lines
8.9 KiB
Plaintext
350 lines
8.9 KiB
Plaintext
9 months ago
|
*! Version 1.0.2, JACS, 18 August 2003
|
||
|
program define metafunnel
|
||
|
version 8.1
|
||
|
* Graphs funnel plot, with standard error on the vertical axis
|
||
|
|
||
|
if ("`*'" == "") {
|
||
|
di "Syntax is:"
|
||
|
di in wh "metafunnel " in gr "{ theta { se | var } | " _c
|
||
|
di in gr "exp(theta) | ll ul [cl] } [" _c
|
||
|
di in wh "if " in gr "exp] [" in wh "in " in gr "range]"
|
||
|
di in gr " [ " in wh ", by(" in gr "by_var"in wh ")" _c
|
||
|
di in gr " { " in wh "v" in gr "ar | " in wh "ci" in gr " } " _c
|
||
|
di in wh "nol" in gr "ines " in wh "forc" in gr "enull " _c
|
||
|
di in wh "rev" in gr "erse " in wh "ef" in gr "orm"
|
||
|
di in gr " graph_options ]"
|
||
|
|
||
|
exit
|
||
|
}
|
||
|
|
||
|
syntax varlist(numeric min=2 max=4) [if] [in], [ by(varname) ///
|
||
|
Var CI SUbtitle(str) NOLines FORCenull REVerse EForm ///
|
||
|
XTitle(string) YTitle(string) XScale(string) YScale(string) ///
|
||
|
MSymbol(string) * ]
|
||
|
|
||
|
tempvar touse theta setheta etheta zz
|
||
|
tempvar ll2 ul2 vl z mmm vvar w sw wl swl RRm orign
|
||
|
tempname oe
|
||
|
|
||
|
tokenize `varlist'
|
||
|
local theta `1'
|
||
|
|
||
|
if "`3'" == "" {
|
||
|
local setheta `2'
|
||
|
}
|
||
|
else {
|
||
|
tempvar ll ul cl
|
||
|
local ll `2'
|
||
|
local ul `3'
|
||
|
local cl `4'
|
||
|
}
|
||
|
|
||
|
* input error traps
|
||
|
if "`ci'" != "" & "`var'" != "" {
|
||
|
di _n as error "Error: options 'ci' and 'var' cannot " _c
|
||
|
di as error "be specified together."
|
||
|
exit
|
||
|
}
|
||
|
if "`ci'" == "ci" & "`ul'" != "" {
|
||
|
di _n as text "Note: option 'ci' specified."
|
||
|
}
|
||
|
if "`ci'" == "ci" & "`ul'" == "" {
|
||
|
di _n as error "Error: option 'ci' specified but varlist " _c
|
||
|
di as error "has only 2 variables."
|
||
|
exit
|
||
|
}
|
||
|
if "`ci'" != "ci" & "`var'" != "var" & "`ul'" != "" {
|
||
|
di _n as text "Warning: varlist has 3 variables but option " _c
|
||
|
di as text "'ci' not specified; 'ci' assumed."
|
||
|
local ci "ci"
|
||
|
local var ""
|
||
|
}
|
||
|
if "`var'" == "var" & "`ul'" != "" {
|
||
|
di _n as error "Error: option 'var' specified but varlist " _c
|
||
|
di as error "has more than 2 variables."
|
||
|
exit
|
||
|
}
|
||
|
if "`var'" == "var" & "`ul'" == "" {
|
||
|
di _n as text "Note: option 'var' specified."
|
||
|
}
|
||
|
if "`var'" != "var" & "`ul'" == "" {
|
||
|
di _n as text "Note: default data input format (theta, " _c
|
||
|
di as text "se_theta) assumed."
|
||
|
}
|
||
|
|
||
|
* Select data to analyze
|
||
|
mark `touse' `if' `in'
|
||
|
if "`ul'" == "" {
|
||
|
markout `touse' `theta' `setheta'
|
||
|
}
|
||
|
else {
|
||
|
markout `touse' `theta' `ll' `ul'
|
||
|
}
|
||
|
|
||
|
preserve
|
||
|
quietly keep if `touse'
|
||
|
quietly count
|
||
|
if r(N)==0 {
|
||
|
di as error "No observations with nonmissing values of `by'"
|
||
|
exit 999
|
||
|
}
|
||
|
|
||
|
|
||
|
* initial calculations...
|
||
|
if "`var'" == "var" {
|
||
|
qui replace `setheta' = sqrt(`setheta')
|
||
|
}
|
||
|
|
||
|
if "`ci'" == "ci" {
|
||
|
di _n as text "Warning: ci option assumes that ratio measures are being used"
|
||
|
capture confirm variable `cl'
|
||
|
if _rc~=0 {
|
||
|
qui gen `zz' = invnorm(.975)
|
||
|
}
|
||
|
else {
|
||
|
qui replace `cl' = `cl' * 100 if `cl' < 1
|
||
|
qui gen `zz' = -1 * invnorm((1- `cl' / 100) / 2 )
|
||
|
qui replace `zz' = invnorm(.025) if `zz'==.
|
||
|
}
|
||
|
qui gen `setheta' = ( ln(`ul') - ln(`ll')) / 2 / `zz'
|
||
|
qui replace `theta' = ln(`theta')
|
||
|
}
|
||
|
|
||
|
* Graph options
|
||
|
|
||
|
if "`xtitle'" == "" {
|
||
|
local xti : variable label `theta'
|
||
|
if "`xti'" == "" {
|
||
|
local xti "`theta'"
|
||
|
}
|
||
|
}
|
||
|
else if "`xtitle'" ~= "" {
|
||
|
local xti "`xtitle'"
|
||
|
}
|
||
|
|
||
|
if "`ytitle'" == "" {
|
||
|
local yti : variable label `setheta'
|
||
|
if "`yti'" == "" {
|
||
|
local yti "s.e. of `theta'"
|
||
|
}
|
||
|
}
|
||
|
else if "`ytitle'" ~= "" {
|
||
|
local yti "`ytitle'"
|
||
|
}
|
||
|
|
||
|
capture assert "`ysca'"==""
|
||
|
if _rc~=0 {
|
||
|
display as error "ysca option not permitted"
|
||
|
exit 999
|
||
|
}
|
||
|
|
||
|
if "`yscale'"~="" {
|
||
|
local chkrev=index("`yscale'","rev")
|
||
|
display "chkrev: `chkrev'"
|
||
|
if `chkrev'~=0 & "`reverse'"=="" {
|
||
|
local ysca "`yscale'"
|
||
|
}
|
||
|
if `chkrev'==0 & "`reverse'"=="" {
|
||
|
local ysca "`yscale' reverse"
|
||
|
}
|
||
|
if `chkrev'~=0 & "`reverse'"~="" {
|
||
|
display "Parsing yscale: ,`yscale' "
|
||
|
tokenize `yscale'
|
||
|
while "`1'"~="" {
|
||
|
if index("`1'","rev")==0 {
|
||
|
local ysca "`ysca' `1'"
|
||
|
}
|
||
|
mac shift
|
||
|
}
|
||
|
}
|
||
|
if `chkrev'==0 & "`reverse'"~="" {
|
||
|
local ysca "`yscale'"
|
||
|
}
|
||
|
}
|
||
|
if "`yscale'"=="" {
|
||
|
if "`reverse'"=="" {
|
||
|
local ysca "reverse"
|
||
|
}
|
||
|
if "`reverse'"~="" {
|
||
|
local ysca "noreverse"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if "`subtitle'" == "" {
|
||
|
local subtitle = "Funnel plot with pseudo 95% confidence limits"
|
||
|
}
|
||
|
else if "`subtitle'" == "." { /* "." means blank it out */
|
||
|
local subtitle "" ""
|
||
|
}
|
||
|
local subtitle "subtitle(`subtitle')"
|
||
|
|
||
|
if "`msymbol'"=="" {
|
||
|
local symopt "O T S D + X Oh Th Sh Dh o t s d x oh th sh dh p"
|
||
|
}
|
||
|
if "`msymbol'"~="" {
|
||
|
local symopt "`msymbol'"
|
||
|
}
|
||
|
local msymbol "msymbol(`symopt')"
|
||
|
|
||
|
qui {
|
||
|
gen `orign'=_n
|
||
|
|
||
|
gen `vvar' = `setheta'^2
|
||
|
gen `w' = 1/`vvar'
|
||
|
egen `sw' = sum(`w') if `touse'
|
||
|
gen `wl' = `w' * `theta'
|
||
|
egen `swl' = sum(`wl') if `touse'
|
||
|
sort `orign'
|
||
|
gen `RRm' = `swl' / `sw'
|
||
|
local rxl=`RRm'[1]
|
||
|
scalar `oe' = `RRm'
|
||
|
egen `mmm' = min(`RRm')
|
||
|
replace `RRm' = `mmm' if `setheta' == 0
|
||
|
|
||
|
if "`forcenull'"~="" {
|
||
|
local rxl=0
|
||
|
}
|
||
|
|
||
|
sort `orign'
|
||
|
local obs1=_N+1
|
||
|
local obs2=_N+2
|
||
|
local obs3=_N+3
|
||
|
local obs4=_N+4
|
||
|
local obs5=_N+5
|
||
|
local obs6=_N+6
|
||
|
set obs `obs6'
|
||
|
replace `orign'=_n
|
||
|
replace `theta'=`rxl' in `obs1'
|
||
|
replace `theta'=`rxl' in `obs3'
|
||
|
gen `ll2' = 0 in `obs1'
|
||
|
gen `ul2' = 0 in `obs3'
|
||
|
|
||
|
sort `orign'
|
||
|
qui summ `setheta'
|
||
|
local maxse=r(max)
|
||
|
replace `theta' = `rxl'-(1.96*`maxse') in `obs2'
|
||
|
replace `theta' = `rxl'+(1.96*`maxse') in `obs4'
|
||
|
replace `ll2' = `maxse' in `obs2'
|
||
|
replace `ul2' = `maxse' in `obs4'
|
||
|
|
||
|
gen `vl' = 0 in `obs5'
|
||
|
replace `vl' = `maxse' in `obs6'
|
||
|
replace `theta' = `rxl' in `obs5'
|
||
|
replace `theta' = `rxl' in `obs6'
|
||
|
|
||
|
label var `ll2' "Lower CI"
|
||
|
label var `ul2' "Lower CI"
|
||
|
label var `vl' "Pooled"
|
||
|
if "`forcenull'"~="" {
|
||
|
label var `vl' "No effect"
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
* list `setheta' `ll2' `ul2' `vl' `theta' `orign'
|
||
|
* display "RRm: `rxl'"
|
||
|
|
||
|
local funopt "yscale(`ysca') `subtitle' ytitle("`yti'") xtitle("`xti'")"
|
||
|
|
||
|
if "`by'"=="" {
|
||
|
local yvar "`setheta'"
|
||
|
local legopt "legend(off)"
|
||
|
}
|
||
|
|
||
|
if "`by'"~="" {
|
||
|
|
||
|
qui levels `by', local(bylev)
|
||
|
local lev: word count `bylev'
|
||
|
if `lev'>20 {
|
||
|
di as text "Note: distinct group markers available for only 19 groups"
|
||
|
}
|
||
|
sort `orign'
|
||
|
qui drop if `by'==.&_n<`obs1'
|
||
|
qui count if `by'~=.
|
||
|
if r(N)==0 {
|
||
|
di as error "No observations with nonmissing values of `by'"
|
||
|
exit 999
|
||
|
}
|
||
|
|
||
|
forvalues b=1/`lev' {
|
||
|
local bylab ""
|
||
|
local bygroup: word `b' of `bylev'
|
||
|
tempname bg`b'
|
||
|
qui gen `bg`b''=`setheta' if `by'==`bygroup'
|
||
|
local bylab: label (`by') `b'
|
||
|
if "`bylab"=="" {
|
||
|
label variable `bg`b'' "`by'=`b'"
|
||
|
}
|
||
|
if "`bylab"~="" {
|
||
|
label variable `bg`b'' "`bylab'"
|
||
|
}
|
||
|
}
|
||
|
local yvar "`bg1'-`bg`lev''"
|
||
|
|
||
|
} /* end by processing */
|
||
|
|
||
|
if "`nolines'"=="nolines"&"`eform'"=="" {
|
||
|
* display "clause 1"
|
||
|
twoway (scatter `yvar' `theta', `legopt' `msymbol') ///
|
||
|
if `touse', `funopt' `options'
|
||
|
}
|
||
|
|
||
|
if "`nolines'"==""&"`eform'"=="" {
|
||
|
* display "clause 2"
|
||
|
twoway (scatter `yvar' `theta', `legopt' `msymbol') ///
|
||
|
(line `ll2' `ul2' `vl' `theta', msymbol(none none none) ///
|
||
|
clcolor(black black black) clpat(dash dash solid) ///
|
||
|
clwidth(medium medium medium)) ///
|
||
|
if `touse', `funopt' `options'
|
||
|
}
|
||
|
|
||
|
if "`eform'"~="" {
|
||
|
gen `etheta'=exp(`theta')
|
||
|
if "`xtitle'"=="" {
|
||
|
local xti : variable label `theta'
|
||
|
if "`xti'" == "" {
|
||
|
local xti "exp(`theta'), log scale"
|
||
|
}
|
||
|
else if "`xti'" ~= "" {
|
||
|
local xti "exp(`xti'), log scale"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
capture assert "`xsca'"==""
|
||
|
if _rc~=0 {
|
||
|
display as error "xsca option not permitted"
|
||
|
exit 999
|
||
|
}
|
||
|
if "`xscale'"~="" {
|
||
|
local chklog=index("`xscale'","log")
|
||
|
if `chklog'==0 {
|
||
|
local xsca "`xscale' log"
|
||
|
}
|
||
|
else if `chklog'~=0 {
|
||
|
local xsca "`xscale'"
|
||
|
}
|
||
|
}
|
||
|
else if "`xscale'"=="" {
|
||
|
local xsca "log"
|
||
|
}
|
||
|
|
||
|
if "`nolines'"=="nolines" {
|
||
|
* display "clause 3"
|
||
|
|
||
|
twoway (scatter `yvar' `etheta', `legopt' `msymbol') ///
|
||
|
if `touse', `funopt' xscale(`xsca') `options'
|
||
|
}
|
||
|
|
||
|
if "`nolines'"=="" {
|
||
|
* display "clause 4"
|
||
|
local rxl=exp(`rxl')
|
||
|
twoway (scatter `yvar' `etheta', `legopt' `msymbol') ///
|
||
|
(line `ll2' `ul2' `vl' `etheta', msymbol(none none none) ///
|
||
|
clcolor(black black black) clpat(dash dash solid) ///
|
||
|
clwidth(medium medium medium)) ///
|
||
|
if `touse', `funopt' xscale(`xsca') `options'
|
||
|
}
|
||
|
}
|
||
|
end
|
||
|
|