/* CREATED: 8 Sep 2017 AUTHOR: Victoria N Nyaga PURPOSE: To fit a bivariate random-effects model to diagnostic data and produce a series of graphs(sroc and forestsplots). VERSION: 2.0.0 NOTES 1. Variable names should not contain underscore(_) 2. Data should be sorted and no duplicates ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ UPDATES ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ DATE: DETAILS: 24.08.2020 grid: Grid lines between studies noveral in help file changed to noOVerall Print full matrix of rr when data is not repeated Print # of studies Correct the tick & axis position for sp Correct computation of the I2 graphsave(filename) option included 03.09.2020 Change paired to comparative 14.09.2020 Correct way of counting the distinct groups in the meta-analysis Check to ensure variable names do no contain underscore. 15.02.2021 paired data: tp1 fp1 .. fn2 tn2 comparator index covariates, by(byvar) comparator, index, byvar need to be string Need to test more with more covariates!!! vline > xline 29.03.2021 order tp fp fn tn 07.05.2021 Reduce the blank lines in the output 11.05.2021 fixed dp = 4 for p-value, fixed dp=0 for df 14.06.2021 Graph save issues 17.06.2021 Subline fix 15.10.2021 Subgroup analysis with superimposed graphs; stratify option 09.06.2022 Network meta-analysis; 26.07.2022 REF(label, top|bottom) ; default is bottom 01.08.2022 Renamed network to abnetwork; and paired to cbnetwork 10.10.2022 introduce variance only on se or sp FUTURE Work on the absolutes for the paired analysis; */ /*++++++++++++++++++++++ METADTA +++++++++++++++++++++++++++++++++++++++++++ WRAPPER FUNCTION +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ cap program drop metadta program define metadta, eclass sortpreserve byable(recall) version 14.0 #delimit ; syntax varlist(min=4) [if] [in], /*tp fp fn tn tp2 fp2 fn2 tn2 */ STudyid(varname) /*Study Idenfier*/ [ STRatify /*Stratified analysis, requires byvar()*/ LAbel(string asis) /*namevar=namevar, yearvar=yearvar*/ DP(integer 2) /*Decimal place*/ POWer(integer 0) /*Exponentiating power*/ MODel(string asis) /*fixed (1 < n < 3) | random (random)*/ COV(string asis) /*UNstructured(default)|INdependent | IDentity | EXchangeable, also includes second covariance for the network*/ SORTby(varlist) /*order data by varlist. How data appears on the table and forest plot*/ INteraction(string) /*sesp(default)|se|sp*/ CVeffect(string) /*sesp(default)|se|sp*/ Level(integer 95) /*Significance level*/ COMParative /*Comparative data or not*/ SUMtable(string) /*Which summary tables to present:abs|logodds|rr|all*/ CImethod(string) /*ci method for the study proportions*/ noFPlot /*No forest plot*/ noITable /*No study specific summary table*/ noHTable /*No heterogeneity table*/ noMC /*No Model comparison - Saves time*/ PROGress /*See the model fitting*/ noSRoc /*No SROC*/ noOVerall /*Dont report the overall in the Itable & fplot*/ noSUBgroup /*Dont report the subgroup in the Itable & fplot*/ SUMMaryonly /*Present only summary in the Itable & fplot*/ DOWNload(string) /*Keep a copy of data used in the plotting*/ Alphasort /*Sort the categorical variable alphabetically*/ FOptions(string asis) /*Options specific to the forest plot*/ SOptions(string asis) /*Options specific to the sroc plot*/ by(varname) /*the grouping variable*/ PAIRed /*Paired; now called CBnetwork*/ CBnetwork /*AB network*/ ABnetwork /*AB network*/ REF(string asis) /*Reference level in network, comparative analysis */ ] ; #delimit cr preserve cap ereturn clear marksample touse, strok qui drop if !`touse' tempvar rid se sp event total invtotal use id neolabel es lci uci grptotal uniq rowid obsid tempname logodds absout logoddsi absouti rrout rrouti sptestnl setestnl sptestnli setestnli selogodds absoutse selogoddsi /// absoutsei serrout serrouti splogodds absoutsp splogoddsi absoutspi /// sprrout sprrouti coefmat coefvar BVar BVari WVar WVari Esigma omat isq2 isq2i Isq Isq2 Isq2i /// bghet refe bgheti refei lrtestp V Vi dftestnl ptestnl semc semci spmc spmci samtrix /// serow sprow serrow sprrow nltest if _by() { global by_index_ = _byindex() if ("`fplot'" == "" | "`sroc'" == "") & "$by_index_" == "1" { cap graph drop _all global fplotname = 0 global srocname = 0 } } else { global by_index_ } /*Check if variables exist*/ foreach var of local varlist { cap confirm var `var' if _rc!=0 { di in re "Variable `var' not in the dataset" exit _rc } } /*Check for mu; its reserved*/ qui ds local vlist = r(varlist) foreach v of local vlist { if ("`v'" == "se") | ("`v'" == "sp") { di as error "se/sp is a reserved variables name; drop or rename se/sp" exit _rc } } //define the design of analysis if "`paired'" != "" { di as res "Use of the option -paired- is deprecated and replaced with -cbnetwork-" local cbnetwork "cbnetwork" } if "`abnetwork'`comparative'`cbnetwork'" != "" { cap assert ("`cbnetwork'" != "") + ("`comparative'" != "") + ("`abnetwork'" != "") == 1 if _rc!=0 { di as error "Define 1 option from: `cbnetwork' `comparative' `abnetwork'" exit _rc } } if "`cbnetwork'" != "" { local design = "cbnetwork" } else if "`comparative'" != "" { local design = "comparative" } else if "`abnetwork'" != "" { local design = "abnetwork" } else if "`abnetwork'`comparative'`cbnetwork'" == "" { local design = "independent" //default design } //General housekeeping if "`ref'" != "" { tokenize "`ref'", parse(",") local ref `1' local refpos "`3'" } if "`refpos'" != "" { if (strpos("`refpos'", "top") == 1) { local refpos "top" } else if (strpos("`refpos'", "bot") == 0) { local refpos "bottom" } else { di as error "Option `refpos' not allowed in ref(`ref', `refpos')" exit } } //default if "`refpos'" == "" { local refpos "bottom" } if "`model'" == "" { local model random } else { tokenize "`model'", parse(",") local model `1' local modelopts "`3'" } if strpos("`model'", "f") == 1 { local model "fixed" } else if strpos("`model'", "r") == 1 { local model "random" } else { di as error "Option `model' not allowed in [`model', `modelopts']" di as error "Specify either -fixed- or -random-" exit } if "`model'" == "fixed" & strpos("`modelopts'", "ml") != 0 { di as error "Option ml not allowed in [`model', `modelopts']" exit } if "`model'" == "fixed" & strpos("`modelopts'", "irls") != 0 { di as error "Option irls not allowed in [`model', `modelopts']" exit } qui count if `=r(N)' < 2 { di as err "Insufficient data to perform meta-analysis" exit } if `=r(N)' < 3 & "`model'" == "random" { local model fixed //If less than 3 studies, use fixed model di as res _n "Note: Fixed-effects model imposed whenever number of studies is less than 3." if "`modelopts'" != "" { local modelopts di as res _n "Warning: Model options ignored." di as res _n "Warning: Consider specifying options for the fixed-effects model." } } if "`model'" == "random" { if "`cov'" != "" { tokenize "`cov'", parse(",") if "`1'" != "," { local bcov "`1'" local wcov "`3'" } else{ local bcov local wcov "`2'" } if "`bcov'" != "" { if strpos("`bcov'", "un")== 1 { local bcov = "unstructured" } else if ustrregexm("`bcov'", "ind", 1){ local bcov = "independent" } else if ustrregexm("`bcov'", "id", 1){ local bcov = "identity" } else if strpos("`bcov'", "ex") == 1 { local bcov = "exchangeable" } else if strpos("`bcov'", "se") == 1 { local bcov = "se" } else if strpos("`bcov'", "sp") == 1 { local bcov = "sp" } else { di as error "Allowed covariance structures: se, sp, unstructured, independent, identity, or exchangeable" exit } } if "`abnetwork'`cbnetwork'" != "" & "`wcov'" != "" { if ustrregexm("`wcov'", "ind", 1){ local wcov = "independent" } else if ustrregexm("`wcov'", "id", 1){ local wcov = "identity" } else if ustrregexm("`wcov'", "ze", 1){ local wcov "zero" } else { di as error "Allowed second covariance structures: independent, identity or zero" exit } } } if "`bcov'" == "" { local bcov = "unstructured" } if "`abnetwork'`cbnetwork'" != "" { if "`wcov'" == "" { local wcov = "independent" } if "`wcov'" == "zero" { local wcov } } } else { local bcov local wcov } if `level' < 1 { local level `level'*100 } if `level'>99 | `level'<10 { local level 95 } /*By default the regressor variabels apply to both sensitivity and specificity*/ if "`cveffect'" == "" { local cveffect "sesp" } else { local rc_ = ("`cveffect'"=="sesp") + ("`cveffect'"=="se") + ("`cveffect'"=="sp") if `rc_' != 1 { di as err "Options cveffect(`cveffect') incorrectly specified" di as err "Allowed options: sesp, se sp" exit } } if "`interaction'" != "" { if ("`interaction'" != "`cveffect'") & ("`cveffect'" != "sesp"){ di as err "Conflict in cveffect(`cveffect') & interaction(`interaction')" exit } } tokenize `varlist' if "`design'" != "cbnetwork" { local depvars "`1' `2' `3' `4'" // tp fp fn tn macro shift 4 } else { tempvar index byvar assignment idpair cap assert "`10'" != "" if _rc != 0 { di as err "cbnetwork data requires atleast 10 variable" exit _rc } local depvars "`1' `2' `3' `4' `5' `6' `7' `8'" // tp1 fp1 fn1 tn1 tp2 fp2 fn2 tn2 local tp = "`1'" local fp = "`2'" local fn = "`3'" local tn = "`4'" local Comparator = "`10'" local Index = "`9'" forvalues num = 1/8 { cap confirm integer number `num' if _rc != 0 { di as error "`num' found where integer expected" exit } } cap confirm string variable `9' if _rc != 0 { di as error "The index variable in cbnetwork analysis should be a string" exit, _rc } cap confirm string variable `10' if _rc != 0 { di as error "The comparator variable in cbnetwork analysis should be a string" exit, _rc } macro shift 10 } local regressors "`*'" gettoken varx confounders : regressors local p: word count `regressors' *local idpair: word 1 of `regressors' if ("`design'" == "independent") & ("`stratify'" != "") & ("`by'" != "") & (`p' > 0) { di as err "Re-frame your analysis. The options _stratify & by()_ in meta-regression is confusing. Consider using the prefix by: instead" exit } //check no underscore in the variable names if strpos("`regressors'", "_") != 0 { di as error "Underscore is a reserved character and covariate(s) containing underscore(s) are not allowed" di as error "Rename the covariate(s) and remove the underscore(s) character" exit } if `p' < 2 & "`interaction'" !="" { di as error "Interactions allowed with atleast 2 covariates" exit } if ("`design'" == "comparative") | ("`design'" == "abnetwork") { gettoken first confounders : regressors cap assert `p' > 0 if _rc != 0 { di as error "`design' analysis requires at least 1 covariate to be specified" exit _rc } *gettoken varx confounders : regressors if "`first'" != "" { cap confirm string variable `first' if _rc != 0 { di as error "The first covariate in `design' analysis should be a string" exit, _rc } } local typevarx = "i" } //======================================================================================================================= //======================================================================================================================= tempfile master qui save "`master'" fplotcheck,`design' `foptions' first(`first') by(`by') //Forest plot advance housekeeping local outplot = r(outplot) local foptions = r(foptions) local lcols = r(lcols) if "`lcols'" == " " { //if empty local lcols } *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 } } qui { *put name/year variables into appropriate macros if "`namevar'"!="" { local lbnvl : value label `namevar' if "`lbnvl'"!="" { quietly decode `namevar', gen(`neolabel') } else { gen str10 `neolabel'="" cap confirm string variable `namevar' if _rc==0 { replace `neolabel'=`namevar' } else if _rc==7 { replace `neolabel'=string(`namevar') } } } if "`namevar'"=="" { cap confirm numeric variable `studyid' if _rc != 0 { gen `neolabel' = `studyid' } if _rc == 0{ gen `neolabel' = string(`studyid') } } if "`yearvar'"!="" { local yearvar "`yearvar'" cap confirm string variable `yearvar' if _rc==7 { local str "string" } if "`namevar'"=="" { replace `neolabel'=`str'(`yearvar') } else { replace `neolabel'=`neolabel'+" ("+`str'(`yearvar')+")" } } } //Long format longsetup `varlist', rid(`rid') se(`se') event(`event') total(`total') `design' rowid(`rowid') idpair(`idpair') assignment(`assignment') first(`first') //Index if "`design'" == "cbnetwork" { tempvar ipair qui gen `ipair' = "Yes" qui replace `ipair' = "No" if `idpair' } //byvar if "`by'" != "" { cap confirm string variable `by' if _rc != 0 { di as error "The by() variable should be a string" exit, _rc } if strpos(`"`varlist'"', "`by'") == 0 { tempvar byvar my_ncod `byvar', oldvar(`by') drop `by' rename `byvar' `by' } } buildregexpr `varlist', cveffect(`cveffect') interaction(`interaction') se(`se') sp(`sp') `alphasort' `design' ipair(`ipair') baselevel(`ref') local regexpression = r(regexpression) local seregexpression = r(seregexpression) local spregexpression = r(spregexpression) local catreg = r(catreg) local contreg = r(contreg) local basecode = r(basecode) if "`interaction'" != "" { local varx = r(varx) local typevarx = r(typevarx) } if "`design'" == "comparative" { *local varx : word 1 of `regressors' gettoken varx catreg : catreg local typevarx = "i" local baselab:label `varx' `basecode' if `basecode' == 1 { local indexcode "2" } else { local indexcode "1" } local indexlab:label `varx' `indexcode' } local pcat: word count `catreg' /*if "`cbnetwork'" != "" { local varx = "`index'" local typevarx = "i" */ /*if ("`cbnetwork'" == "") & ("`comparative'" == "") & ("`interaction'" == "") { local varx local typevarx }*/ if "`design'" == "cbnetwork" { local varx = "`ipair'" local typevarx = "i" } //Ensure varx in comparative is bi-categorical if "`comparative'" != "" { qui label list `varx' local ncat = r(max) cap assert `ncat' == 2 if _rc != 0 { di as error "Comparative analysis requires that `varx' only has 2 categories but found `ncat'" exit _rc } } local pcont: word count `contreg' if "`typevarx'" != "" & "`typevarx'" == "c" { local ++pcont } if `pcont' > 0 { local continuous = "continuous" } /*Model presenations*/ local regressorss "`regressors'" /*Overall*/ local lmuse = "mu_lse" local lmusp = "mu_lsp" if "`design'" == "independent" | "`design'" == "comparative" { local nuse = "mu_lse" local nusp = "mu_lsp" } else if "`design'" == "cbnetwork" { if "`interaction'" != "" { local nuse = "mu_lse + Ipair*`Comparator' + `Index'" local nusp = "mu_lsp + Ipair*`Comparator' + `Index'" } else { local nuse = "mu_lse + Ipair + `Index'" local nusp = "mu_lsp + Ipair + `Index'" } } else { *abnetwork local nuse = "mu.`first'_lse" local nusp = "mu.`first'_lsp" tokenize `regressors' mac shift local regressorss "`*'" } //Build the rest of the equation *local VarX: word 1 of `regressors' local q: word count `regressors' forvalues i=1/`q' { local c:word `i' of `regressorss' //se if "`cveffect'" != "sp" { local nuse = "`nuse' + `c'" if ("`interaction'" == "sesp" | "`interaction'" == "se") & `i' > 1 { local nuse = "`nuse' + `c'*`varx'" } } //sp if "`cveffect'" != "se" { local nusp = "`nusp' + `c'" if ("`interaction'" == "sesp" | "`interaction'" == "sp") & `i' > 1 { local nusp = "`nusp' + `c'*`varx'" } } } if ("`catreg'" != " " | "`typevarx'" =="i" | ("`design'" == "comparative" | "`design'" == "cbnetwork")) { if "`design'" == "cbnetwork" { local catregs = "`catreg' `Index'" } if "`design'" == "comparative" { local catregs = "`catreg' `varx'" } if "`design'" == "abnetwork" { tokenize `catreg' macro shift local catregs "`*'" } if "`design'" == "independent" { local catregs "`catreg'" } } if "`subgroup'" == "" & ("`catreg'" != "" | "`typevarx'" =="i" ) { if "`outplot'" == "abs" { if "`typevarx'" =="i" { local groupvar = "`varx'" } else { local groupvar : word 1 of `catreg' } } if "`outplot'" == "rr" & "`varx'" !="" { local groupvar : word 1 of `catreg' } } if "`by'" != "" { local groupvar "`by'" local byvar "`by'" *How many times to loop qui label list `by' local nlevels = r(max) } if "`design'" == "abnetwork" { local groupvar "`first'" local overall "nooverall" if "`outplot'" == "rr" { local itable "noitable" } } if "`by'" == "" & "`cbnetwork'" != "" { local groupvar "`Index'" local byvar "`Index'" } *Stratify not allow in cbnetwork or abnetwork analysis if "`stratify'" != "" { if ("`design'" == "cbnetwork") | ("`design'" == "abnetwork") { di as error"The option stratify is not allowed in `design' analysis" exit } } *Check by is active & that there are more than 1 levels if "`stratify'" != "" { if "`by'" == "" { di as error "The by() variable needs to be specified in stratified analysis" exit } else { if `nlevels' < 2 { di as error "The by() variable should have atleast 2 categories in stratified analysis" exit } } //nomc local mc } if "`groupvar'" == "" { local subgroup nosubgroup } qui gen `sp' = 1 - `se' *qui gen `use' = . //fit the model if "`progress'" != "" { local echo noi } else { local echo qui } *Loop should begin here if "`stratify'" == "" { local nlevels = 0 } local i = 1 local byrownames local bybirownames if "`design'" == "abnetwork" { local hetdim 7 } else { if (`p' == 0) & ("`model'" == "random") & ("`design'" != "cbnetwork" ) { local hetdim 5 } else { local hetdim 4 } } if "`outplot'" == "abs" { local sumstatse "Sensitivity" local sumstatsp "Specificity" } else { local sumstatse "Relative Sensitivity" local sumstatsp "Relative Specificity" } //Should run atleast once while `i' < `=`nlevels' + 2' { local modeli = "`model'" local modeloptsi = "`modelopts'" //don't run last loop if stratify *if (`i' > `nlevels') & ("`stratify'" != "") & ("`design'" == "comparative") { * local overall "nooverall" * continue, break *} *Stratify except the last loop for the overall if (`i' < `=`nlevels' + 1') & ("`stratify'" != "") { local strataif `"if `by' == `i'"' local ilab:label `by' `i' local stratalab `":`by' = `ilab'"' local ilab = ustrregexra("`ilab'", " ", "_") local byrownames = "`byrownames' `by':`ilab'" if "`design'" == "comparative" & "`stratify'" != "" { local bybirownames = "`bybirownames' `ilab':`baselab' `ilab':`indexlab' `ilab':Overall" } *Check if there is enough data in each strata //Number of obs in the analysis qui egen `obsid' = group(`rid') if `by' == `i' qui summ `obsid' local Nobs= r(max) drop `obsid' //Number of studies in the analysis qui egen `uniq' = group(`studyid') if `by' == `i' qui summ `uniq' local Nuniq = r(max) drop `uniq' } else { //Nullify local strataif if "`stratify'" != "" { local stratalab ": Full" local byrownames = "`byrownames' Overall" } //Number of obs in the analysis qui count local Nobs= r(N) if "`cbnetwork'" != "" { local Nobs = `Nobs'*0.25 } else { local Nobs = `Nobs'*0.5 } qui egen `uniq' = group(`studyid') qui summ `uniq' local Nuniq = r(max) drop `uniq' } if "`design'" == "comparative" { cap assert mod(`Nobs', 2) == 0 if _rc != 0 { di as error "Comparative analysis requires 2 observations per study" exit _rc } } if "`design'" == "abnetwork" { cap assert `Nobs'/`Nuniq' >= 2 if _rc != 0 { di as error "abnetwork design requires atleast 2 observations per study" exit _rc } } if `Nuniq' < 3 & "`modeli'" == "random" { local modeli fixed //If less than 3 studies, use fixed model if "`modeloptsi'" != "" { local modeloptsi di as res _n "Warning: Random-effects model options ignored." di as res _n "Warning: Fixed-effects model fitted instead." } } di as res _n "*********************************** Fitted model`stratalab' ***************************************" tokenize `varlist' di "{phang} `1' ~ binomial(se, `1' + `3'){p_end}" di "{phang} `4' ~ binomial(sp, `4' + `2'){p_end}" if "`modeli'" == "random" { di "{phang} logit(se) = `nuse' + `studyid'_lse{p_end}" di "{phang} logit(sp) = `nusp' + `studyid'_lsp{p_end}" } else { di "{phang} logit(se) = `nuse'{p_end}" di "{phang} logit(sp) = `nusp'{p_end}" } if "`modeli'" == "random" { if "`bcov'" == "se" { di "{phang}`studyid'_lse ~ normal(0, sigma){p_end}" } else if "`bcov'" == "sp" { di "{phang}`studyid'_lsp ~ normal(0, sigma){p_end}" } else { di "{phang}`studyid'_lse, `studyid'_lsp ~ biv.normal(0, sigma){p_end}" } } if "`design'" == "cbnetwork" { di "{phang} Ipair = 0 if 1st set{p_end}" di "{phang} Ipair = 1 if 2nd set{p_end}" } if "`design'" == "abnetwork" { if "`wcov'" == "identity" { di "{phang}`first'_lse, `first'_lsp ~ N(0, `first'.tau2){p_end}" } else if "`wcov'" == "independent" { di "{phang}`first'_lse ~ N(0, `first'_lse.tau2){p_end}" di "{phang}`first'_lsp ~ N(0, `first'_lsp.tau2){p_end}" } qui label list `first' local nfirst = r(max) } if "`design'" == "cbnetwork" { if "`wcov'" == "identity" { di "{phang}Ipair_lse, Ipair_lsp ~ N(0, Ipair.tau2){p_end}" } else if "`wcov'" == "independent" { di "{phang}Ipair_lse ~ N(0, Ipair_lse.tau2){p_end}" di "{phang}Ipair_lsp ~ N(0, Ipair_lsp.tau2){p_end}" } } if ("`catreg'" != " " | "`typevarx'" =="i" | ("`design'" == "comparative" | "`design'" == "cbnetwork")) { di _n "{phang}Base levels{p_end}" di _n as txt "{pmore} Variable -- Base Level{p_end}" } foreach fv of local catregs { local lab:label `fv' 1 di "{pmore} `fv' -- `lab'{p_end}" } if "`design'" == "abnetwork" { local lab:label `first' `basecode' di "{pmore} `first' -- `lab'{p_end}" } di as txt "{phang}Number of observations = " as res "`Nobs'{p_end}" di as txt "{phang}Number of studies = " as res "`Nuniq'{p_end}" if "`design'" == "abnetwork" { di as txt "{phang}Number of `first's = " as res "`nfirst'{p_end}" } *Run model if more than 1 study if `Nobs' > 1 { `echo' madamodel `event' `total' `se' `sp' `strataif', bcov(`bcov') wcov(`wcov') modelopts(`modeloptsi') model(`modeli') /// regexpression(`regexpression') sid(`studyid') `design' ipair(`ipair') level(`level') nested(`first') estimates store metadta_modest cap drop _ESAMPLE qui gen _ESAMPLE = e(sample) mat `coefmat' = e(b) mat `coefvar' = e(V) } else { mat `coefmat' = (0 , 0) mat `coefvar' = e(V) } estcovar, matrix(`coefmat') model(`modeli') bcov(`bcov') wcov(`wcov') `design' local kcov = r(k) //#covariance parameters mat `BVari' = r(BVar) //Between var-cov mat `WVari' = r(WVar) //Within var-cov mat colnames `BVari' = logitse logitsp mat rownames `BVari' = logitse logitsp mat colnames `WVari' = logitse logitsp mat rownames `WVari' = logitse logitsp local tausesp = `BVari'[1, 2] local rho = `BVari'[1, 2]/sqrt(`BVari'[1, 1]*`BVari'[2, 2]) local tau2se = `BVari'[1, 1] local tau2sp = `BVari'[2, 2] local tau2g = (1 - (`BVari'[1, 2]/sqrt(`BVari'[1, 1]*`BVari'[2, 2]))^2)*`BVari'[1, 1]*`BVari'[2, 2] if("`sumtable'" != "") { local loddslabel = "Log_odds" local abslabel = "Proportion" local rrlabel = "Rel_Ratio" } if `Nobs' > 1 { local S_1 = e(N) - e(k) //df } else { local S_1 = . } local S_2 = . //between study heterogeneity chi2 local S_3 = . // between study heterogeneity pvalues local i2g = . //Isq local i2se = . //Isqse local i2sp = . //Isqsp local S_81 = . //Full vs Null chi2 -- se local S_91 = . //Full vs Null pvalue -- se local S_891 = . //Full vs Null df -- se local S_82 = . //Full vs Null chi2 -- sp local S_92 = . //Full vs Null pvalue -- sp local S_892 = . //Full vs Null df -- sp //Consider a reduced model if "`modeli'" == "random" { qui estimates restore metadta_modest local S_2 = e(chi2_c) local S_3 = e(p_c) } if `p' == 0 & "`design'" == "independent" { /*Compute I2*/ mat `Esigma' = J(2, 2, 0) /*Expected within study variance*/ if "`strataif'" != "" { qui gen `invtotal' = 1/`total' qui summ `invtotal' if `se' & `by' == `i' local invtotalse = r(sum) qui summ `invtotal' if `sp' & `by' == `i' local invtotalsp = r(sum) drop `invtotal' } else { qui gen `invtotal' = 1/`total' qui summ `invtotal' if `se' local invtotalse = r(sum) qui summ `invtotal' if `sp' local invtotalsp = r(sum) } mat `Esigma'[1, 1] = (exp(`BVari'[1, 1]*0.5 + `coefmat'[1, 1]) + exp(`BVari'[1, 1]*0.5 - `coefmat'[1, 1]) + 2)*(1/(`Nuniq'))*`invtotalse' mat `Esigma'[2, 2] = (exp(`BVari'[2, 2]*0.5 + `coefmat'[1, 2]) + exp(`BVari'[2, 2]*0.5 - `coefmat'[1, 2]) + 2)*(1/(`Nuniq'))*`invtotalsp' local detEsigma = `Esigma'[1, 1]*`Esigma'[2, 2] local detSigma = (1 - (`BVari'[2, 1]/sqrt(`BVari'[1, 1]*`BVari'[2, 2]))^2)*`BVari'[1, 1]*`BVari'[2, 2] local IsqE = sqrt(`detSigma')/(sqrt(`detEsigma') + sqrt(`detSigma')) local i2g = `IsqE' local i2se = (`BVari'[1, 1]/(`Esigma'[1, 1] + `BVari'[1, 1])) //se local i2sp = (`BVari'[2, 2]/(`Esigma'[2, 2] + `BVari'[2, 2])) //sp } local nmc = 0 if (`p' > 0 & "`mc'" == "") { forvalues j=1/2 { local S_9`j' = . local S_8`j' = . local S_89`j' = . } if "`interaction'" !="" { local confariates "`confounders'" } if "`interaction'" =="" { if ("`design'" == "abnetwork" | "`design'" == "comparative") { tokenize `regressors' macro shift local confariates "`*'" } else { local confariates "`regressors'" } } local initialse 1 local initialsp 1 local rownamesmcse local rownamesmcsp if "`confariates'" != "" { di "*********************************** ************* ***************************************" di as txt "Just a moment - Fitting reduced models for comparisons" } foreach c of local confariates { if "`cveffect'" != "sp" { if "`interaction'" =="sesp" | "`interaction'" =="se" { local xterm = "`c'#`typevarx'.`varx'" local xnu = "`c'*`varx'" } else { local xterm = "`c'" local xnu = "`c'" } //Sensivitivity terms local nullse foreach term of local seregexpression { if ("`term'" != "i.`xterm'#c.`se'")&("`term'" != "c.`xterm'#c.`se'")&("`term'" != "`xterm'#c.`se'") { local nullse "`nullse' `term'" } } local nullnuse = subinstr("`nuse'", "+ `xnu'", "", 1) di as res _n "Ommitted : `xnu' in logit(se)" di as res "{phang} logit(se) = `nullnuse'{p_end}" di as res "{phang} logit(sp) = `nusp'{p_end}" local nullse = "`nullse' `spregexpression'" `echo' madamodel `event' `total' `se' `sp' `strataif', bcov(`bcov') wcov(`wcov') modelopts(`modelopts') model(`model') /// regexpression(`nullse') sid(`studyid') `design' ipair(`ipair') level(`level') nested(`first') estimates store metadta_Nullse //LR test the model qui lrtest metadta_modest metadta_Nullse local selrp :di %10.`dp'f chi2tail(r(df), r(chi2)) local selrchi2 = r(chi2) local selrdf = r(df) estimates drop metadta_Nullse if `initialse' { mat `semci' = [`selrchi2', `selrdf', `selrp'] local initialse 0 } else { mat `semci' = [`selrchi2', `selrdf', `selrp'] \ `semci' } local rownamesmcse "`rownamesmcse' `xnu'" } if "`cveffect'" != "se" { if "`interaction'" =="sesp" | "`interaction'" =="sp" { local xterm = "`c'#`typevarx'.`varx'" local xnu = "`c'*`varx'" } else { local xterm = "`c'" local xnu = "`c'" } //Specificity terms local nullsp foreach term of local spregexpression { if ("`term'" != "i.`xterm'#c.`sp'")&("`term'" != "c.`xterm'#c.`sp'")&("`term'" != "`xterm'#c.`sp'") { local nullsp "`nullsp' `term'" } } local nullnusp = subinstr("`nusp'", "+ `xnu'", "", 1) di as res _n "Ommitted : `xnu' in logit(sp)" di as res "{phang} logit(se) = `nuse'{p_end}" di as res "{phang} logit(sp) = `nullnusp'{p_end}" local nullsp = "`seregexpression' `nullsp'" `echo' madamodel `event' `total' `se' `sp' `strataif', bcov(`bcov') wcov(`wcov') modelopts(`modelopts') model(`model') /// regexpression(`nullsp') sid(`studyid') `design' ipair(`ipair') level(`level') nested(`first') estimates store metadta_Nullsp //LR test the model qui lrtest metadta_modest metadta_Nullsp local splrp :di %10.`dp'f chi2tail(r(df), r(chi2)) local splrchi2 = r(chi2) local splrdf = r(df) estimates drop metadta_Nullsp if `initialsp' { mat `spmci' = [`splrchi2', `splrdf', `splrp'] local initialsp 0 } else { mat `spmci' = [`splrchi2', `splrdf', `splrp'] \ `spmci' } local rownamesmcsp "`rownamesmcsp' `xnu'" } local ++nmc } //Ultimate null model if more than one term if (`p' > 0) & (`nmc' > 1) { if "`cveffect'" != "sp" { local nullse `se' local nullse = "`nullse' `spregexpression'" `echo' madamodel `event' `total' `se' `sp' `strataif', bcov(`bcov') wcov(`wcov') modelopts(`modelopts') model(`model') regexpression(`nullse') /// sid(`studyid') `design' ipair(`ipair') level(`level') nested(`first') estimates store metadta_Nullse qui lrtest metadta_modest metadta_Nullse local selrp :di %10.`dp'f chi2tail(r(df), r(chi2)) local selrchi2 = r(chi2) local selrdf = r(df) estimates drop metadta_Nullse if `initialse' { mat `semci' = [`selrchi2', `selrdf', `selrp'] local initialse 0 } else { mat `semci' = `semci' \ [`selrchi2', `selrdf', `selrp'] } local rownamesmcse "`rownamesmcse' All" } if "`cveffect'" != "se" { local nullsp `sp' local nullsp = "`seregexpression' `nullsp'" `echo' madamodel `event' `total' `se' `sp' `strataif', bcov(`bcov') wcov(`wcov') modelopts(`modelopts') model(`model') regexpression(`nullsp') /// sid(`studyid') `design' ipair(`ipair') level(`level') nested(`first') estimates store metadta_Nullsp qui lrtest metadta_modest metadta_Nullsp local splrp :di %10.`dp'f chi2tail(r(df), r(chi2)) local splrchi2 = r(chi2) local splrdf = r(df) estimates drop metadta_Nullsp if `initialsp' { mat `spmci' = [`splrchi2', `splrdf', `splrp'] local initialsp 0 } else { mat `spmci' = `spmci' \ [`splrchi2', `splrdf', `splrp'] } local rownamesmcsp "`rownamesmcsp' All" } } if "`cveffect'" != "sp" & `nmc' > 0 { mat roweq `semci' = Sensitivity mat rownames `semci' = `rownamesmcse' mat colnames `semci' = chi2 df pval } if "`cveffect'" != "se" & `nmc' > 0 { mat roweq `spmci' = Specificity mat rownames `spmci' = `rownamesmcsp' mat colnames `spmci' = chi2 df pval } } if `nmc' == 0 { local mc "nomc" } mat `BVari' = (`tau2se', `i2se', `tau2sp', `i2sp', `tau2g', `i2g', `tausesp', `rho') mat colnames `BVari' = sensitivity:tausq sensitivity:isq specificity:tausq specificity:isq Generalized:tausq Generalized:isq covar rho mat rownames `BVari' = Overall *mat `isq2i' = (`S_71', `S_7' \ `S_7', `S_72') //Isq *mat `Isq2i' = (`S_7' , `S_71', `S_72') *mat colnames `Isq2i' = Generalized logitse logitse *mat rownames `Isq2i' = Overall //model comparison *mat `mci' = (`S_81', `S_91', `S_891', `S_82', `S_92' , `S_892' ) // Full vs Null *mat colnames `mci' = sensitivity:Chi2 sensitivity:pval sensitivity:df specificity:Chi2 specificity:pval specificity:df *mat rownames `bgheti' = overall mat `refei' = (`S_2', `kcov', `S_3') // chisq re vs fe, df, pv re vs fe mat colnames `refei' = Chi2 df pval mat rownames `refei' = Overall if `Nobs' > 1 { //LOG ODDS estp `strataif', estimates(metadta_modest) sumstat(`loddslabel') depname(Effect) interaction(`interaction') cveffect(`cveffect') /// catreg(`catreg') contreg(`contreg') se(`se') level(`level') dp(`dp') varx(`varx') typevarx(`typevarx') /// by(`by') regexpression(`regexpression') `design' `stratify' mat `Vi' = r(Vmatrix) //var-cov for catreg & overall mat `logoddsi' = r(outmatrix) mat `selogoddsi' = r(outmatrixse) mat `splogoddsi' = r(outmatrixsp) if "`interaction'" == "" { //names of the V matrix local vnames local rnames :rownames `Vi' local nrowsv = rowsof(`Vi') forvalues r = 1(1)`nrowsv' { //Labels local rname`r':word `r' of `rnames' tokenize `rname`r'', parse("#") local left = "`1'" local right = "`3'" tokenize `left', parse(.) local parm = substr("`1'", 1, 1) if `parm' == 0 { local eqlab "sp" } else { local eqlab "se" } if "`right'" == "" { local lab = "Overall" } else { tokenize `right', parse(.) local rightv = "`3'" local rightlabel = substr("`1'", 1, 1) local rlab:label `rightv' `rightlabel' local rlab = ustrregexra("`rlab'", " ", "-") local lab = "`rightv'_`rlab'" } local vnames = "`vnames' `eqlab':`lab'" } mat rownames `Vi' = `vnames' mat colnames `Vi' = `vnames' } //ABS estp `strataif', estimates(metadta_modest) sumstat(`abslabel') depname(Effect) interaction(`interaction') cveffect(`cveffect') /// catreg(`catreg') contreg(`contreg') se(`se') level(`level') expit power(`power') dp(`dp') varx(`varx') /// typevarx(`typevarx') by(`byvar') regexpression(`regexpression') `design' `stratify' mat `absouti' = r(outmatrix) mat `absoutsei' = r(outmatrixse) mat `absoutspi' = r(outmatrixsp) //RR if `pcat' > 0 | "`typevarx'" == "i" { estr `strataif', estimates(metadta_modest) sumstat(`rrlabel') `comparative' cveffect(`cveffect') /// catreg(`catreg') se(`se') level(`level') power(`power') dp(`dp') varx(`varx') /// typevarx(`typevarx') by(`byvar') `stratify' regexpression(`regexpression') `design' /// baselevel(`basecode') refpos(`refpos') comparator(`Comparator') mat `rrouti' = r(outmatrix) mat `serrouti' = r(outmatrixse) mat `sprrouti' = r(outmatrixsp) local inltest = r(inltest) if "`inltest'" == "yes" { mat `setestnli' = r(setestnl) //Equality of RR mat `sptestnli' = r(sptestnl) } } else { local rr "norr" } } *if one study else { mat `absouti' = J(2, 6, 0) mat `absoutsei' = J(1, 6, 0) mat `absoutspi' = J(1, 6, 0) mat `rrouti' = J(2, 6, 1) mat `serrouti' = J(1, 6, 1) mat `sprrouti' = J(1, 6, 1) mat `setestnli' = J(1, 3, .) mat `sptestnli' = J(1, 3, .) mat `Vi' = J(2, 2, .) //var-cov for catreg & overall mat `logoddsi' = J(2, 6, 0) mat `selogoddsi' = J(1, 6, 0) mat `splogoddsi' = J(1, 6, 0) mat `BVari' = J(1, 8, .) mat `WVari' = J(1, 2, 0) if ((`p' > 0 & "`design'" != "abnetwork") | (`p' > 1 & "`design'" == "abnetwork")) & "`mc'" == "" { mat `semci' = J(1, 3, .) mat `spmci' = J(1, 3, .) } } if "`stratify'" != "" { if ((`p' > 0 & "`design'" != "abnetwork") | (`p' > 1 & "`design'" == "abnetwork")) & "`mc'" == "" { mat roweq `semci' = Sensitivity mat roweq `spmci' = Specificity mat coleq `semci' = `by' mat coleq `spmci' = `by' } } *Stack the matrices if `i' == 1 { mat `absout' = `absouti' mat `absoutse' = `absoutsei' mat `absoutsp' = `absoutspi' if "`rr'" == "" { mat `rrout' = `rrouti' mat `serrout' = `serrouti' mat `sprrout' = `sprrouti' if "`inltest'" == "yes" { mat `setestnl' = `setestnli' //Equality of RR mat `sptestnl' = `sptestnli' } } mat `BVar' = `BVari' mat `WVar' = `WVari' mat `V' = `Vi' //var-cov for catreg & overall mat `logodds' = `logoddsi' mat `selogodds' = `selogoddsi' mat `splogodds' = `splogoddsi' *mat `bghet' = `bgheti' mat `refe' = `refei' *mat `Isq2' = `Isq2i' if ((`p' > 0 & "`design'" != "abnetwork") | (`p' > 1 & "`design'" == "abnetwork")) & "`mc'" == "" { mat `semc' = `semci' mat `spmc' = `spmci' } } else { mat `absout' = `absout' \ `absouti' mat `absoutse' = `absoutse' \ `absoutsei' mat `absoutsp' = `absoutsp' \ `absoutspi' if "`rr'" == "" { mat `rrout' = `rrout' \ `rrouti' mat `serrout' = `serrout' \ `serrouti' mat `sprrout' = `sprrout' \ `sprrouti' if "`inltest'" == "yes" { mat `setestnl' = `setestnl' \ `setestnli' //Equality of RR mat `sptestnl' = `sptestnl' \ `sptestnli' } } mat `BVar' = `BVar' \ `BVari' mat `WVar' = `WVar' \ `WVari' mat `V' = `V' \ `Vi' mat `logodds' = `logodds' \ `logoddsi' mat `selogodds' = `selogodds' \ `selogoddsi' mat `splogodds' = `splogodds' \ `splogoddsi' if ((`p' > 0 & "`design'" != "abnetwork") | (`p' > 1 & "`design'" == "abnetwork")) & "`mc'" == "" { mat `semc' = `semc' \ `semci' mat `spmc' = `spmc' \ `semci' } *mat `bghet' =`bghet' \ `bgheti' mat `refe' = `refe' \ `refei' *mat `Isq2' = `Isq2' \ `Isq2i' } local ++i } *Loop should end here //rownames for the matrix if "`stratify'" != "" & `i' > 1 { *mat rownames `hetout' = `byrownames' mat `serow' = J(1, 6, .) mat `sprow' = J(1, 6, .) mat rownames `serow' = "*--Sensitivity--*" mat rownames `sprow' = "*--Specificity--*" //19 characters if "`design'" != "comparative" { mat rownames `absoutse' = `byrownames' mat rownames `absoutsp' = `byrownames' mat rownames `selogodds' = `byrownames' mat rownames `splogodds' = `byrownames' } else { mat rownames `absoutse' = `bybirownames' mat rownames `absoutsp' = `bybirownames' mat rownames `selogodds' = `bybirownames' mat rownames `splogodds' = `bybirownames' } mat `absoutse' = `serow' \ `absoutse' mat `absoutsp' = `sprow' \ `absoutsp' mat `absout' = `absoutse' \ `absoutsp' mat colnames `absout' = Estimate SE(logit) z(logit) P>|z| Lower Upper mat colnames `absoutse' = Estimate SE(logit) z(logit) P>|z| Lower Upper mat colnames `absoutsp' = Estimate SE(logit) z(logit) P>|z| Lower Upper if "`rr'" == "" { mat `serrow' = J(1, 6, .) mat `sprrow' = J(1, 6, .) mat rownames `serrow' = "Relative Sensitivity" mat rownames `sprrow' = "Relative Specificity" //20 chars mat rownames `serrout' = `byrownames' mat rownames `sprrout' = `byrownames' mat `serrout' = `serrow' \ `serrout' mat `sprrout' = `sprrow' \ `sprrout' mat `rrout' = `serrout' \ `sprrout' mat colnames `rrout' = Estimate SE(log) z(log) P>|z| Lower Upper mat colnames `serrout' = Estimate SE(log) z(log) P>|z| Lower Upper mat colnames `sprrout' = Estimate SE(log) z(log) P>|z| Lower Upper } mat rownames `BVar' = `byrownames' mat rownames `refe' = `byrownames' *mat coleq `spmc' = `byrownames' *mat coleq `semc' = `byrownames' } if "`rr'" == "" { if "`inltest'" == "yes" { mat `nltest' = `setestnl' \ `sptestnl' } } //CI if "`outplot'" == "rr" { if "`design'" == "comparative" { drop `sp' gettoken idpair confounders : regressors /*tokenize `regressors' macro shift local confounders `*'*/ qui count local Nobs = `=r(N)'*0.25 *cap assert mod(`Nobs', 2) == 0 /*Check if the number of studies is half*/ if _rc != 0 { di as error "Some studies cannot be compared properly" exit _rc, STATA } sort `se' `regressors' `rid' egen `id' = seq(), f(1) t(`Nobs') b(1) sort `id' `se' `varx' widesetup `event' `total' `confounders', idpair(`varx') se(`se') sid(`id') `design' gen `sp' = 1 - `se' local vlist = r(vlist) local cc0 = r(cc0) local cc1 = r(cc1) if "`refpos'" == "bottom" { koopmanci `event'`=`indexcode'-1' `total'`=`indexcode'-1' `event'`=`basecode'-1' `total'`=`basecode'-1', rr(`es') upperci(`uci') lowerci(`lci') alpha(`=1 - `level'*0.01') } else { koopmanci `event'`=`basecode'-1' `total'`=`basecode'-1' `event'`=`indexcode'-1' `total'`=`indexcode'-1', rr(`es') upperci(`uci') lowerci(`lci') alpha(`=1 - `level'*0.01') } //Rename the varying columns foreach v of local vlist { rename `v'0 `v'_`cc0' label var `v'_`cc0' "`v'_`cc0'" rename `v'1 `v'_`cc1' label var `v'_`cc1' "`v'_`cc1'" } //make new lcols, rcols foreach v of local lcols { if strpos("`vlist'", "`v'") != 0 { local lcols_rr "`lcols_rr' `v'_`cc0' `v'_`cc1'" } else { local lcols_rr "`lcols_rr' `v'" } } local lcols "`lcols_rr'" //make new depvars local depvars_rr foreach v of local depvars { if strpos("`vlist'", "`v'") != 0 { local depvars_rr "`depvars_rr' `v'_`cc0' `v'_`cc1'" } else { local depvars_rr "`depvars_rr' `v'" } } local depvars "`depvars_rr'" //make new indvars local indvars_rr foreach v of local indvars { if strpos("`vlist'", "`v'") != 0 { local indvars_rr "`indvars_rr' `v'_`cc0' `v'_`cc1'" } else { local indvars_rr "`indvars_rr' `v'" } } local regressors "`indvars_rr'" } if "`design'" == "cbnetwork" { sort `rowid' `se' `rid' drop `sp' `rid' qui reshape wide `event' `total' `ipair' `assignment', i(`rowid' `se') j(`idpair') koopmanci `event'1 `total'1 `event'0 `total'0, rr(`es') upperci(`uci') lowerci(`lci') alpha(`=1 - `level'*0.01') gen `id' = `rowid' } if "`design'" == "abnetwork" { gen `id' = _n qui gen `es' = . qui gen `lci' = . qui gen `uci' = . } } else { metadta_propci `total' `event', p(`es') lowerci(`lci') upperci(`uci') cimethod(`cimethod') level(`level') gen `id' = _n } forvalues l = 1(1)6 { local S_`l'1 = . local S_`l'2 = . } //=================================================================================== //Prepare data for display gen `use' = 1 //Individual studies prep4show `id' `se' `use' `neolabel' `es' `lci' `uci', /// sortby(`sortby') groupvar(`groupvar') grptotal(`grptotal') /// outplot(`outplot') serrout(`serrout') absoutse(`absoutse') absoutsp(`absoutsp') /// sprrout(`sprrout') `subgroup' `summaryonly' `stratify' /// `overall' download(`download') indvars(`regressors') depvars(`depvars') `design' //Extra tables if ("`sumtable'" != "none") { di as res _n "****************************************************************************************" } //het if "`model'" == "random" & "`htable'" == "" { printmat, matrixout(`BVar') type(bhet) dp(`dp') `design' p(`p') } //re vs fe if "`model'" == "random" & "`htable'" == "" { printmat, matrixout(`refe') type(refe) dp(`dp') `design' } //logodds /*if (("`sumtable'" == "all") |(strpos("`sumtable'", "logit") != 0)) { printmat, matrixout(`logodds') type(logit) dp(`dp') power(`power') `continuous' cveffect(`cveffect') }*/ //abs if (("`sumtable'" == "all") |(strpos("`sumtable'", "abs") != 0)) { printmat, matrixout(`absout') type(abs) dp(`dp') power(`power') `continuous' cveffect(`cveffect') } //rr if (("`sumtable'" == "all") |(strpos("`sumtable'", "rr") != 0)) & (`pcat' > 0 | "`typevarx'" == "i") { *if (("`sumtable'" == "all" & `pcat' > 0) | (strpos("`sumtable'", "rr") != 0)) & (("`catreg'" != " ") | ("`typevarx'" == "i")) { //rr printmat, matrixout(`rrout') type(rr) dp(`dp') power(`power') cveffect(`cveffect') //rr equal if "`inltest'" == "yes" { printmat, matrixout(`nltest') type(rre) dp(`dp') cveffect(`cveffect') } } //model comparison if ((`p' > 0 & "`design'" != "abnetwork") | (`p' > 1 & "`design'" == "abnetwork")) & ("`mc'" =="") { printmat, matrixoutse(`semc') matrixoutsp(`spmc') type(mc) dp(`dp') cveffect(`cveffect') } //Display heterogeneity if "`model'" == "random" & "`htable'" == "" { *disphetab, `htable' dp(`dp') /*isq2(`Isq2')*/ bshet(`bshet') bvar(`BVari') wvar(`WVari') p(`p') } //Display the studies if "`itable'" == "" { disptab `id' `se' `use' `neolabel' `es' `lci' `uci' `grptotal', `itable' dp(`dp') power(`power') /// `subgroup' `overall' sumstatse(`sumstatse') sumstatsp(`sumstatsp') /// isq2(`isq2') bghet(`bghet') bshet(`bshet') model(`model') bvar(`BVar') /// catreg(`catreg') outplot(`outplot') interaction(`interaction') /// se_lrtest(`se_lrtest') sp_lrtest(`sp_lrtest') p(`p') `mc' `design' } //Draw the forestplot if "`fplot'" == "" { fplot `es' `lci' `uci' `use' `neolabel' `grptotal' `id' `se', /// studyid(`studyid') power(`power') dp(`dp') level(`level') /// groupvar(`groupvar') /// outplot(`outplot') lcols(`lcols') `foptions' `design' } //Draw the SROC curve if "`outplot'" == "rr" { local sroc "nosroc" } if "`sroc'" == "" { if "`groupvar'" == "" & `p' > 0 { di as res "NOTE: SROC presented for the overall mean." } use "`master'", clear sroc `varlist', model(`model') selogodds(`selogodds') splogodds(`splogodds') v(`V') bvar(`BVar') /// groupvar(`groupvar') cimethod(`cimethod') level(`level') p(`p') `soptions' `stratify' } cap ereturn clear if ((`p' > 0 & "`design'" != "abnetwork") |(`p' > 1 & "`design'" == "abnetwork")) & "`mc'" == "" { ereturn matrix mctestse = `semc' //model comparison se ereturn matrix mctestsp = `spmc' //model comparison se *ereturn matrix bghet = `bghet' //Full vs Null model } /*if `p' == 0 & "`cbnetwork'" == "" { ereturn matrix isq2 = `isq2' //isq2 }*/ /*if "`inltest'" == "yes" { ereturn matrix setestnl = `setestnl' //Equality of RR - se ereturn matrix sptestnl = `sptestnl' }*/ cap confirm matrix `logodds' if _rc == 0 { ereturn matrix logodds = `logodds' //logodds se and sp ereturn matrix Vlogodds = `V' //var-cov for catreg & overall } cap confirm matrix `absout' if _rc == 0 { ereturn matrix absout = `absout' ereturn matrix absoutse = `absoutse' ereturn matrix absoutsp = `absoutsp' } cap confirm matrix `rrout' if _rc == 0 { ereturn matrix rrout = `rrout' ereturn matrix serrout = `serrout' ereturn matrix sprrout = `sprrout' } ereturn matrix refe = `refe' //Re vs FE ereturn matrix vcovar = `BVar' //var-covar between logit se and logit sp if "`abnetwork'" != "" { ereturn matrix wvar = `WVar' //2nd variance } restore end /*+++++++++++++++++++++++++ SUPPORTING FUNCTIONS: INDEX +++++++++++++++++++++++++ Find index of word in a string +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ cap program drop index program define index, rclass version 14.0 syntax, source(string asis) word(string asis) local nwords: word count `source' local found 0 local index 1 while (!`found') & (`index' <= `nwords'){ local iword:word `index' of `source' if "`iword'" == `word' { local found 1 } local index = `index' + 1 } if `found' { local index = `index' - 1 } else{ local index = 0 } return local index `index' end /*+++++++++++++++++++++++++ SUPPORTING FUNCTIONS: myncod +++++++++++++++++++++++++ Decode by order of data +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ cap program drop my_ncod program define my_ncod version 14.1 syntax newvarname(gen), oldvar(varname) qui { cap confirm numeric var `oldvar' tempvar by_num if _rc == 0 { decode `oldvar', gen(`by_num') drop `oldvar' rename `by_num' `oldvar' } * 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) cap drop `varlist' gen `varlist' = 1 in 1 local lab = `oldvar'[1] cap label drop `oldvar' if "`lab'" != ""{ label define `oldvar' 1 "`lab'" } local found1 "`lab'" local max = 1 forvalues i = 2/`N'{ local thisval = `oldvar'[`i'] local already = 0 forvalues j = 1/`max'{ if "`thisval'" == "`found`j''"{ local already = `j' } } if `already' > 0{ replace `varlist' = `already' in `i' } else{ local max = `max' + 1 replace `varlist' = `max' in `i' local lab = `oldvar'[`i'] if "`lab'" != ""{ label define `oldvar' `max' "`lab'", modify } local found`max' "`lab'" } } label values `varlist' `oldvar' label copy `oldvar' `varlist', replace } end /*+++++++++++++++++++++++++ SUPPORTING FUNCTIONS: MADAMODEL +++++++++++++++++++ Fit the logistic model +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ cap program drop madamodel program define madamodel version 14.0 syntax varlist [if], [ bcov(string) wcov(string) model(string) modelopts(string asis) regexpression(string) sid(varname) /// nested(varname) comparative ipair(varname) level(integer 95) abnetwork cbnetwork independent comparative] tokenize `varlist' marksample touse, strok if "`abnetwork'`cbnetwork'" != "" & "`wcov'" != "" { local nested = `"|| (`nested'`ipair': `3' `4', noc cov(`wcov'))"' } else { local nested } if ("`bcov'" == "se") | ("`bcov'" == "sp"){ if ("`bcov'" == "se") { local re = "`3'" } else { local re = "`4'" } local cov } else { local re = "`3' `4'" local cov = "cov(`bcov')" } if ("`model'" == "fixed") { capture noisily binreg `1' `regexpression' if `touse', noconstant n(`2') ml `modelopts' l(`level') local success = _rc } if ("`model'" == "random") { if strpos(`"`modelopts'"', "(iterate") == 0 { local modelopts = `"iterate(30) `modelopts'"' } if strpos(`"`modelopts'"', "intpoi") == 0 { qui count if `touse' if `=r(N)' < 7 { local modelopts = `"intpoints(`=r(N)') `modelopts'"' } } //First trial #delim ; capture noisily meqrlogit (`1' `regexpression' if `touse', noc )|| (`sid': `re', noc `cov') `nested', binomial(`2') `modelopts' l(`level'); #delimit cr local success = _rc local converged = e(converged) local try = 1 //Try to refineopts 2 times if strpos(`"`modelopts'"', "refineopts") == 0 { if (`try' < 4) & ((`converged' == 0) | (`success' != 0)) { local ++try local refine "refineopts(iterate(`=10 * `try''))" #delim ; capture noisily meqrlogit (`1' `regexpression' if `touse', noc )|| (`sid': `re', noc `cov') `nested', binomial(`2') `modelopts' l(`level') `refine' ; #delimit cr local success = _rc local converged = e(converged) } } if strpos(`"`modelopts'"', "refineopts") == 0 { if (`try' < 4) & ((`converged' == 0) | (`success' != 0)) { local ++try local refine "refineopts(iterate(`=10 * `try''))" #delim ; capture noisily meqrlogit (`1' `regexpression' if `touse', noc )|| (`sid': `re', noc `cov') `nested', binomial(`2') `modelopts' l(`level') `refine' ; #delimit cr local success = _rc local converged = e(converged) } } *Try matlog if still difficult if (strpos(`"`modelopts'"', "matlog") == 0) & ((`converged' == 0) | (`success' != 0)) { local ++try #delim ; capture noisily meqrlogit (`1' `regexpression' if `touse', noc )|| (`sid': `re', noc `cov') `nested', binomial(`2') `modelopts' l(`level') `refine' matlog; #delimit cr local success = _rc local converged = e(converged) } } if `success' != 0 { *display as error "Unexpected error performing regression" di as err "Model could not converge after `try' attempts with different options. Try fitting a simpler model" exit `success' } end /*+++++++++++++++++++++++++ SUPPORTING FUNCTIONS: metadta_PROPCI +++++++++++++++++++++++++ CI for proportions ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ cap program drop metadta_propci program define metadta_propci version 14.1 syntax varlist [if] [in], p(name) lowerci(name) upperci(name) [cimethod(string) level(real 95)] qui { tokenize `varlist' gen `p' = . gen `lowerci' = . gen `upperci' = . count `if' `in' forvalues i = 1/`r(N)' { local N = `1'[`i'] local n = `2'[`i'] cii proportions `N' `n', `cimethod' level(`level') replace `p' = r(proportion) in `i' replace `lowerci' = r(lb) in `i' replace `upperci' = r(ub) in `i' } } end /*+++++++++++++++++++++++++ SUPPORTING FUNCTIONS: WIDESETUP +++++++++++++++++++++++++ Transform data to wide format +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ cap program drop widesetup program define widesetup, rclass version 14.1 syntax varlist, [sid(varlist) idpair(varname) se(varname) comparative sortby(varlist) cbnetwork rowid(varname) index(varname) assignment(varname) comparator(varname) ] qui{ tokenize `varlist' local event = "`1'" local total = "`2'" tempvar jvar modey diffy if "`cbnetwork'" == "" { gen `jvar' = `idpair' - 1 /*Check for varying variable and store them*/ ds local vnames = r(varlist) local vlist foreach v of local vnames { cap drop `modey' `diffy' bysort `sid': egen `modey' = mode(`v'), minmode egen `diffy' = diff(`v' `modey') sum `diffy' local sumy = r(sum) if (strpos(`"`varlist'"', "`v'") == 0) & (`sumy' > 0) & "`v'" != "`jvar'" & "`v'" != "`idpair'" { if "`se'" != "" & "`v'" == "`se'"{ local v } local vlist "`vlist' `v'" } } cap drop `modey' `diffy' sort `sid' `jvar' `sortby' /*2 variables per study : n N*/ reshape wide `event' `total' `idpair' `vlist', i(`sid' `se') j(`jvar') } if "`cbnetwork'" != "" { reshape wide `varlist', i(`sid') j(`se') drop `sid' reshape wide `event'0 `total'0 `event'1 `total'1 `index' `assignment', i(`rowid') j(`comparator') } local cc0 = `idpair'0[1] local cc1 = `idpair'1[1] local idpair0 : lab `idpair' `cc0' local idpair1 : lab `idpair' `cc1' return local vlist = "`vlist'" return local cc0 = "`idpair0'" return local cc1 = "`idpair1'" } end /*+++++++++++++++++++++++++ SUPPORTING FUNCTIONS: PREP4SHOW +++++++++++++++++++++++++ Prepare data for display table and graph +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ cap program drop prep4show program define prep4show version 14.0 #delimit ; syntax varlist, [serrout(name) sprrout(name) absoutse(name) absoutsp(name) sortby(varlist) groupvar(varname) summaryonly nooverall nosubgroup outplot(string) grptotal(name) download(string asis) indvars(varlist) depvars(varlist) comparative abnetwork independent cbnetwork stratify ] ; #delimit cr tempvar sp expand tokenize `varlist' local id = "`1'" local se = "`2'" local use = "`3'" local label = "`4'" local es = "`5'" local lci = "`6'" local uci = "`7'" qui { gen `sp' = 1 - `se' gen `expand' = 1 //Groups if "`groupvar'" != "" { bys `groupvar' `se' : egen `grptotal' = count(`id') //# studies in each group gsort `groupvar' `se' `sortby' `id' bys `groupvar' `se' : replace `expand' = 1 + 1*(_n==1) + 2*(_n==_N) expand `expand' gsort `groupvar' `se' `sortby' `id' `expand' bys `groupvar' `se' : replace `use' = -2 if _n==1 //group label bys `groupvar' `se' : replace `use' = 2 if _n==_N-1 //subgroup bys `groupvar' `se' : replace `use' = 0 if _n==_N //blank replace `id' = `id' + 1 if `use' == 1 replace `id' = `id' + 2 if `use' == 2 //subgroup replace `id' = `id' + 3 if `use' == 0 //blank replace `label' = "Summary" if `use' == 2 qui label list `groupvar' local nlevels = r(max) local c = 0 local m = 1 forvalues l = 1/`nlevels' { if "`outplot'" == "abs" { if ("`stratify'" !="") { local c = 1 if ("`comparative'" !="") { local m = 3 } } local S_112 = `absoutse'[`=`l'*`m' + `c'', 1] local S_122 = `absoutsp'[`=`l'*`m' + `c'', 1] local S_312 = `absoutse'[`=`l'*`m' + `c'', 5] local S_322 = `absoutsp'[`=`l'*`m' + `c'', 5] local S_412 = `absoutse'[`=`l'*`m' + `c'', 6] local S_422 = `absoutsp'[`=`l'*`m' + `c'', 6] } else { if ("`abnetwork'" !="") | ("`stratify'" !="") { local c = 1 } local S_112 = `serrout'[`=`l' + `c'', 1] local S_122 = `sprrout'[`=`l' + `c'', 1] local S_312 = `serrout'[`=`l' + `c'', 5] local S_322 = `sprrout'[`=`l' + `c'', 5] local S_412 = `serrout'[`=`l' + `c'', 6] local S_422 = `sprrout'[`=`l' + `c'', 6] } local lab:label `groupvar' `l' replace `label' = "`lab'" if `use' == -2 & `groupvar' == `l' replace `label' = "`lab'" if `use' == 2 & `groupvar' == `l' & "`outplot'" == "rr" & "`abnetwork'" != "" replace `es' = `S_112'*`se' + `S_122'*`sp' if `use' == 2 & `groupvar' == `l' replace `lci' = `S_312'*`se' + `S_322'*`sp' if `use' == 2 & `groupvar' == `l' replace `uci' = `S_412'*`se' + `S_422'*`sp' if `use' == 2 & `groupvar' == `l' } } else { bys `se' : egen `grptotal' = count(`id') //# studies total } //Overall //Overall if "`overall'" == "" { gsort `se' `groupvar' `sortby' `id' bys `se' : replace `expand' = 1 + 2*(_n==_N) expand `expand' gsort `se' `groupvar' `sortby' `id' `expand' bys `se' : replace `use' = 3 if _n==_N-1 //Overall bys `se' : replace `use' = 0 if _n==_N //blank bys `se' : replace `id' = `id' + 1 if _n==_N-1 //Overall bys `se' : replace `id' = `id' + 2 if _n==_N //blank //Fill in the right info if "`outplot'" == "abs" { local senrows = rowsof(`absoutse') local spnrows = rowsof(`absoutsp') local S_11 = `absoutse'[`senrows', 1] //p (se) local S_31 = `absoutse'[`senrows', 5] //ll local S_41 = `absoutse'[`senrows', 6] //ul local S_12 = `absoutsp'[`spnrows', 1] //p (sp) local S_32 = `absoutsp'[`spnrows', 5] //ll local S_42 = `absoutsp'[`spnrows', 6] //ul } else { local senrows = rowsof(`serrout') local spnrows = rowsof(`sprrout') local S_11 = `serrout'[`senrows', 1] //p (se) local S_31 = `serrout'[`senrows', 5] //ll local S_41 = `serrout'[`senrows', 6] //ul local S_12 = `sprrout'[`spnrows', 1] //p (sp) local S_32 = `sprrout'[`spnrows', 5] //ll local S_42 = `sprrout'[`spnrows', 6] //ul } replace `es' = `S_11'*`se' + `S_12'*`sp' if `use' == 3 replace `lci' = `S_31'*`se' + `S_32'*`sp' if `use' == 3 replace `uci' = `S_41'*`se' + `S_42'*`sp' if `use' == 3 replace `label' = "Overall" if `use' == 3 } count if `use'==1 & `se'==1 replace `grptotal' = `=r(N)' if `use'==3 replace `grptotal' = `=r(N)' if _n==_N replace `label' = "" if `use' == 0 replace `es' = . if `use' == 0 | `use' == -2 replace `lci' = . if `use' == 0 | `use' == -2 replace `uci' = . if `use' == 0 | `use' == -2 gsort `se' `groupvar' `sortby' `id' } if "`download'" != "" { preserve qui { cap drop _ES _LCI _UCI _USE _LABEL _PARAMETER gen _ES = `es' gen _LCI = `lci' gen _UCI = `uci' gen _USE = `use' gen _LABEL = `label' gen _PARAMETER = `se' gen _ID = `id' keep `depvars' `indvars' _ES _LCI _UCI _ESAMPLE _USE _LABEL _PARAMETER _ID } di _n "Data saved" noi save "`download'", replace restore } qui { //Drop unnecessary rows if "`abnetwork'" == "" | ("`abnetwork'" != "" & "`outplot'" != "rr") { drop if (`use' == 2 | `use' == 3 ) & (`grptotal' == 1) //drop summary if 1 study } drop if (`use' == 1 & "`summaryonly'" != "" & `grptotal' > 1) //remove label if summary only replace `label' = `label'[_n-1] if (`use' == 2 & "`summaryonly'" != "") //Drop unnecessary rows drop if (`use' == 2 & "`subgroup'" != "") drop if (`use' == -2 & "`summaryonly'" != "") drop if (`use' == 3 & "`overall'" != "") if "`abnetwork'" != "" & "`outplot'" == "rr" { drop if `use' == 1 | `use' == -2 replace `use' = 1 if `use' == 2 } gsort `se' `groupvar' `sortby' `id' bys `se' : replace `id' = _n gsort `id' `se' } end /*+++++++++++++++++++++++++ SUPPORTING FUNCTIONS: DISPHETAB +++++++++++++++++++++++++ Display table +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ cap program drop disphetab program define disphetab version 14.0 #delimit ; syntax [, isq2(name) bshet(name) dp(integer 2) bvar(name) wvar(name) p(integer 0)] ; #delimit cr local rho = `bvar'[1, 2]/sqrt(`bvar'[1, 1]*`bvar'[2, 2]) local tau2se = `bvar'[1, 1] local tau2sp = `bvar'[2, 2] local tau2g = (1 - (`bvar'[1, 2]/sqrt(`bvar'[1, 1]*`bvar'[2, 2]))^2)*`bvar'[1, 1]*`bvar'[2, 2] di as txt "Between-study heterogeneity" di as txt _col(28) "covar" _cont di as res _n _col(28) %5.`=`dp''f `covar' di as txt _col(28) "rho" _cont di as res _n _col(28) %5.`=`dp''f `rho' di as txt _col(28) "Tau.sq" _cont if `p' == 0 { di as txt _col(45) "I^2(%)" _cont local isq2b = `isq2'[1, 1]*100 local isq2se = `isq2'[1, 2]*100 local isq2sp = `isq2'[1, 3]*100 } di as txt _n "Generalized" _cont di as res _col(28) %5.`=`dp''f `tau2g' _col(45) %5.`=`dp''f `isq2b' di as txt "Sensitivity" _cont di as res _col(28) %5.`=`dp''f `tau2se' _col(45) %5.`=`dp''f `isq2se' di as txt "Specificity" _cont di as res _col(28) %5.`=`dp''f `tau2sp' _col(45) %5.`=`dp''f `isq2sp' di as txt _col(30) "Chi2" _skip(8) "degrees of" _cont di as txt _n _col(28) "statistic" _skip(6) "freedom" _skip(8)"p-val" _cont local chisq = `bshet'[1, 1] local df = `bshet'[1, 2] local pv = `bshet'[1, 3] di as txt _n "LR Test: RE vs FE model" _cont di as res _col(25) %10.`=`dp''f `chisq' _col(45) `df' _col(52) %10.4f `pv' end /*+++++++++++++++++++++++++ SUPPORTING FUNCTIONS: DISPTAB +++++++++++++++++++++++++ Display table +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ cap program drop disptab program define disptab version 14.0 #delimit ; syntax varlist, [nosubgroup nooverall level(integer 95) sumstatse(string asis) sumstatsp(string asis) noitable dp(integer 2) power(integer 0) isq2(name) bghet(name) bshet(name) model(string) bvar(name) catreg(string) outplot(string) interaction(string) se_lrtest(name) sp_lrtest(name) p(integer 0) noMC independent cbnetwork comparative abnetwork] ; #delimit cr tempvar id se use label es lci uci df tokenize `varlist' qui gen `id' = `1' qui gen `se' = `2' qui gen `use' = `3' qui gen `label' = `4' qui gen `es' = `5' qui gen `lci' = `6' qui gen `uci' = `7' qui gen `df' = 8 if "`outplot'" == "abs" { local sumstat "Absolute Measures" } else { local sumstat "Relative Measures" } preserve tempvar tlabellen //study label local studylb: variable label `label' if "`studylb'" == "" { local studylb "Study" } qui replace `se' = `se' + 1 qui widesetup `label', sid(`id') idpair(`se') qui gen `tlabellen' = strlen(`label'0) qui summ `tlabellen' local nlen = r(max) + 5 local nlense = strlen("`sumstatse'") local nlensp = strlen("`sumstatsp'") di as res "****************************************************************************************" di as res "{pmore2} Study specific test accuracy: `sumstat' {p_end}" di as res "****************************************************************************************" di _n as txt _col(`nlen') "| " _skip(`=22 - round(`nlense'/2)') "`sumstatse'" /// _skip(`=44 - (22 - round(`nlense'/2)) - `nlense' - 1') "| " _skip(`=22 - round(`nlensp'/2)') "`sumstatsp'" _cont di _n as txt _col(2) "`studylb'" _col(`nlen') "| " _skip(5) "Estimate" /// _skip(5) "[`level'% Conf. Interval]" /// _skip(5) "| " _skip(5) "Estimate" /// _skip(5) "[`level'% Conf. Interval]" di _dup(`=`nlen'-1') "-" "+" _dup(44) "-" "+" _dup(44) "-" qui count local N = r(N) forvalues i = 1(1)`N' { //Group labels if ((`use'[`i']== -2)){ di _col(2) as txt `label'0[`i'] _col(`nlen') "| " _col(`=`nlen' + 45') "| " } //Studies -- se if ((`use'[`i'] ==1)) { di _col(2) as txt `label'1[`i'] _col(`nlen') "| " /// _skip(5) as res %5.`=`dp''f `es'1[`i']*(10^`power') /// _col(`=`nlen' + 20') %5.`=`dp''f `lci'1[`i']*(10^`power') /// _skip(5) %5.`=`dp''f `uci'1[`i']*(10^`power') _cont } //studies - sp if (`use'[`i'] ==1 ) { di as txt _col(`=`nlen' + 45') "| " /// _skip(5) as res %5.`=`dp''f `es'0[`i']*(10^`power') /// _col(`=`nlen' + 66') %5.`=`dp''f `lci'0[`i']*(10^`power') /// _skip(5) %5.`=`dp''f `uci'0[`i']*(10^`power') } //Summaries if ( (`use'[`i']== 3) | ((`use'[`i']== 2) & (`df'[`i'] > 1))){ if ((`use'[`i']== 2) & (`df'[`i'] > 1)) { di _col(2) as txt _col(`nlen') "| " _col(`=`nlen' + 45') "| " } di _col(2) as txt `label'0[`i'] _col(`nlen') "| " /// _skip(5) as res %5.`=`dp''f `es'1[`i']*(10^`power') /// _col(`=`nlen' + 20') %5.`=`dp''f `lci'1[`i']*(10^`power') /// _skip(5) %5.`=`dp''f `uci'1[`i']*(10^`power') /// as txt _col(`=`nlen' + 45') "| " /// _skip(5) as res %5.`=`dp''f `es'0[`i']*(10^`power') /// _col(`=`nlen' + 66') %5.`=`dp''f `lci'0[`i']*(10^`power') /// _skip(5) %5.`=`dp''f `uci'0[`i']*(10^`power') } //Blanks if (`use'[`i'] == 0 ){ di as txt _dup(`=`nlen'-1') "-" "+" _dup(44) "-" "+" _dup(44) "-" di as txt _col(`nlen') "| " _col(`=`nlen' + 45') "| " } } /*if (`p' > 0 ) { if (`p' > 0) & ("`mc'" =="") { local S_81 = `bghet'[1, 1] //chi local S_91 = `bghet'[1, 2] //p local S_891 = `bghet'[1, 3] //df local S_82= `bghet'[2, 1] //chi local S_92 = `bghet'[2, 2] //p local S_892 = `bghet'[2, 3] //df if (`p' > 0) { di as txt _n "LR Test: Full Model vs Intercept-only Model" _n di as txt _col(30) "Chi2" _skip(8) "degrees of" _cont di as txt _n _col(28) "statistic" _skip(6) "freedom" _skip(8)"p-val" _cont di as txt _n "Sensitivity " _cont di as res _col(25) %10.`=`dp''f `S_81' _col(45) `S_891' _col(52) %10.4f `S_91' _cont di as txt _n "Specificity" _cont di as res _col(25) %10.`=`dp''f `S_82' _col(45) `S_892' _col(52) %10.4f `S_92' } mat colnames `testmat2print' = chi2 df p } }*/ restore end /*+++++++++++++++++++++++++ SUPPORTING FUNCTIONS: PRINTMAT +++++++++++++++++++++++++ Print the outplot matrix beautifully +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ cap program drop printmat program define printmat version 13.1 syntax, type(string) [cveffect(string) matrixout(name) matrixoutse(name) /// matrixoutsp(name) sumstat(string) dp(integer 2) p(integer 0) power(integer 0) /// matched cbnetwork abnetwork independent comparative continuous ] local rownamesmaxlen = 10 if ("`type'" != "mc") { local nrows = rowsof(`matrixout') local ncols = colsof(`matrixout') local rnames : rownames `matrixout' forvalues r = 1(1)`nrows' { local rname : word `r' of `rnames' local nlen : strlen local rname local rownamesmaxlen = max(`rownamesmaxlen', min(`nlen', 32)) //Check if there is a longer name } } if ("`type'" == "abs") { if `nrows' > 4 { if "`cveffect'" == "sesp" { local rspec "---`="&"*`=`nrows'/2 - 2''--`="&"*`=`nrows'/2 - 2''-" } else if "`cveffect'" == "se" { local rspec "---`="&"*`=`nrows'-4''---" } else if "`cveffect'" == "sp" { local rspec "-----`="&"*`=`nrows'-4''-" } } else { local rspec "----" } } if ("`type'" == "rr") { local rownamesmaxlen = 20 if `nrows' > 4 { if "`cveffect'" == "sesp" { local rspec "---`="&"*`=`nrows'/2 - 2''--`="&"*`=`nrows'/2 - 2''-" } else if "`cveffect'" == "se" { local rspec "---`="&"*`=`nrows'-4''---" } else if "`cveffect'" == "sp" { local rspec "-----`="&"*`=`nrows'-4''-" } } else { local rspec "--&-&-" } } local nlensstat : strlen local sumstat local nlensstat = max(10, `nlensstat') if "`type'" == "rre" { local rownamesmaxlen = max(`rownamesmaxlen', 20) //Check if there is a longer name local rspec "--`="&"*`=`nrows'-1''-" di as res _n "****************************************************************************************" di as txt _n "Wald-type test for nonlinear hypothesis" di as txt _n "{phang}H0: All (log)RR equal vs. H1: Some (log)RR different {p_end}" #delimit ; noi matlist `matrixout', rowtitle(Effect) cspec(& %`rownamesmaxlen's | %8.`=`dp''f & %8.0f & %8.4f o2&) rspec(`rspec') underscore nodotz ; #delimit cr } if ("`type'" == "logit") | ("`type'" == "abs") | ("`type'" == "rr") { di as res _n "****************************************************************************************" if ("`type'" == "logit") { di as res "{pmore2} Marginal summary measures of test accuracy: Log odds {p_end}" } if ("`type'" == "abs") { di as res "{pmore2} Marginal summary measures of test accuracy: Absolute measures {p_end}" } if ("`type'" == "rr") { di as res "{pmore2} Marginal summary measures of test accuracy: Relative measures {p_end}" } di as res "****************************************************************************************" *tempname mat2print *mat `mat2print' = `matrixout' local nrows = rowsof(`matrixout') forvalues r = 1(1)`nrows' { mat `matrixout'[`r', 1] = `matrixout'[`r', 1]*10^`power' mat `matrixout'[`r', 5] = `matrixout'[`r', 5]*10^`power' mat `matrixout'[`r', 6] = `matrixout'[`r', 6]*10^`power' forvalues c = 1(1)6 { local cell = `matrixout'[`r', `c'] if "`cell'" == "." { mat `matrixout'[`r', `c'] == .z } } } #delimit ; noi matlist `matrixout', rowtitle(Effect) cspec(& %`rownamesmaxlen's | %`nlensstat'.`=`dp''f & %9.`=`dp''f & %8.`=`dp''f & %15.`=`dp''f & %8.`=`dp''f & %8.`=`dp''f o2&) rspec(`rspec') underscore nodotz ; #delimit cr } if ("`type'" == "bhet") { di as res _n "****************************************************************************************" di as txt _n "Between-study heterogeneity statistics" if `nrows' > 1 { local rspec "-`="&"*`nrows''-" *tempname mat2print *mat `mat2print' = `matrixout' forvalues r = 1(1)`nrows' { forvalues c = 1(1)`ncols' { local cell = `matrixout'[`r', `c'] if "`cell'" == "." { mat `matrixout'[`r', `c'] == .z } } } #delimit ; noi matlist `matrixout', cspec(& %`rownamesmaxlen's | %13.`=`dp''f `="& %13.`=`dp''f "*`=`ncols'-1'' o2&) rspec(`rspec') underscore nodotz ; #delimit cr } else { local rho = `matrixout'[1, 8] local covar = `matrixout'[1, 7] local tau2se = `matrixout'[1, 1] local tau2sp = `matrixout'[1, 3] local tau2g = `matrixout'[1, 5] di as txt _col(28) "covar" _col(45) "rho"_cont di as res _n _col(28) %5.`=`dp''f `covar' _col(45) %5.`=`dp''f `rho' di as txt _col(28) "Tau.sq" _cont di as txt _col(45) "I^2(%)" _cont if `p' == 0 { local isq2g = `matrixout'[1, 6]*100 local isq2se = `matrixout'[1, 2]*100 local isq2sp = `matrixout'[1, 4]*100 } else { local isq2g = `matrixout'[1, 6] local isq2se = `matrixout'[1, 2] local isq2sp = `matrixout'[1, 4] } di as txt _n "Generalized" _cont di as res _col(28) %5.`=`dp''f `tau2g' _col(45) %5.`=`dp''f `isq2g' di as txt "Sensitivity" _cont di as res _col(28) %5.`=`dp''f `tau2se' _col(45) %5.`=`dp''f `isq2se' di as txt "Specificity" _cont di as res _col(28) %5.`=`dp''f `tau2sp' _col(45) %5.`=`dp''f `isq2sp' } } if ("`type'" == "mc") { cap confirm matrix `matrixoutse' local semat = _rc cap confirm matrix `matrixoutsp' local spmat = _rc tempname matrixout if (`=`semat' + `spmat'') == 0 { mat `matrixout' = `matrixoutse' \ `matrixoutsp' local nrowse = rowsof(`matrixoutse') local nrowsp = rowsof(`matrixoutsp') local rspec "--`="&"*`=`nrowse'-1''-`="&"*`=`nrowsp'-1''-" } else if `semat' == 0 { mat `matrixout' = `matrixoutse' local nrowse = rowsof(`matrixoutse') local rspec "--`="&"*`=`nrowse'-1''-" } else { mat `matrixout' = `matrixoutsp' local nrowsp = rowsof(`matrixoutsp') local rspec "--`="&"*`=`nrowsp'-1''-" } local ncols = colsof(`matrixout') local nrows = rowsof(`matrixout') local rnames : rownames `matrixout' local rownamesmaxlen = 15 forvalues r = 1(1)`nrows' { local rname : word `r' of `rnames' local nlen : strlen local rname local rownamesmaxlen = max(`rownamesmaxlen', min(`nlen', 32)) //Check if there is a longer name } di as res _n "****************************************************************************************" di as txt _n "Model comparison(s): Leave-one/all-out LR Test(s)" #delimit ; noi matlist `matrixout', rowtitle(Excluded Effect(s)) cspec(& %`=`rownamesmaxlen' + 2's | %8.`=`dp''f `="& %8.`=`dp''f "*`=`ncols'-1'' o2&) rspec(`rspec') underscore nodotz ; #delimit cr if "`interaction'" !="" { di as txt "*NOTE: Model with and without interaction effect(s)" } else { di as txt "*NOTE: Model with and without main effect(s)" } } if ("`type'" == "refe") { di as txt _n "LR Test: RE vs FE model" if `nrows' > 1 { local rspec "--`="&"*`=`nrows'-1''-" *tempname mat2print *mat `mat2print' = `matrixout' forvalues r = 1(1)`nrows' { forvalues c = 1(1)`ncols' { local cell = `matrixout'[`r', `c'] if "`cell'" == "." { mat `matrixout'[`r', `c'] == .z } } } #delimit ; noi matlist `matrixout', cspec(& %`rownamesmaxlen's | %8.0f `="& %10.`=`dp''f "*`=`ncols'-1'' o2&) rspec(`rspec') underscore nodotz ; #delimit cr } else { local chisq = `matrixout'[1, 1] local df = `matrixout'[1, 2] local pv = `matrixout'[1, 3] di as txt _col(10) "Chi2" _skip(8) "degrees of" _cont di as txt _n _col(8) "statistic" _skip(6) "freedom" _skip(8)"p-val" di as res _col(5) %10.`=`dp''f `chisq' _col(25) `df' _col(34) %10.4f `pv' } } if ("`continuous'" != "") { di as txt "NOTE: For continuous variable margins are computed at their respective mean" } if ("`type'" == "abs") { di as txt "NOTE: H0: P = 0.5 vs. H1: P != 0.5" } end /*+++++++++++++++++++++++++ SUPPORTING FUNCTIONS: LONGSETUP +++++++++++++++++++++++++ Transform data to long format +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ cap program drop longsetup program define longsetup version 14.0 syntax varlist, rid(name) event(name) total(name) se(name) [first(name) rowid(name) assignment(name) idpair(name) cbnetwork abnetwork independent comparative ] qui{ tempvar tp tn fp fn tokenize `varlist' if "`cbnetwork'" == "" { local nvar = 4 } else { local nvar = 8 } /*The four variables should contain numbers*/ forvalue i=1(1)`nvar' { capture confirm numeric var ``i'' if _rc != 0 { di as error "The variable ``i'' must be numeric" exit } } if "`cbnetwork'" != "" { gen `tp'1 = `1' gen `fp'1 = `2' gen `fn'1 = `3' gen `tn'1 = `4' gen `tp'0 = `5' gen `fp'0 = `6' gen `fn'0 = `7' gen `tn'0 = `8' gen `rowid' = _n gen `assignment'1 = `9' gen `assignment'0 = `10' reshape long `tp' `fp' `fn' `tn' `assignment', i(`rowid') j(`idpair') } else { gen `tp' = `1' gen `fp' = `2' gen `fn' = `3' gen `tn' = `4' } /*4 variables per study : TP TN FP FN*/ gen `event'1 = `tp' /*TP*/ gen `event'0 = `tn' /*TN*/ gen `total'1 = `tp' + `fn' /*DIS = TP + FN*/ gen `total'0 = `tn' + `fp' /*NDIS = TN + FP*/ gen `rid' = _n if "`abnetwork'" != "" { reshape long `event' `total', i(`rid' `first') j(`se') } else { reshape long `event' `total', i(`rid') j(`se') } } end /*++++++++++++++++ SUPPORTING FUNCTIONS: BUILDEXPRESSIONS +++++++++++++++++++++ buildexpressions the regression and estimation expressions +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ cap program drop buildregexpr program define buildregexpr, rclass version 13.1 syntax varlist, [cveffect(string) interaction(string) se(name) sp(name) alphasort cbnetwork abnetwork independent comparative ipair(varname) baselevel(string)] tempvar holder tokenize `varlist' if "`cbnetwork'" == "" { macro shift 4 local regressors "`*'" } else { local index = "`9'" local comparator = "`10'" macro shift 10 local regressors "`*'" my_ncod `holder', oldvar(`index') drop `index' rename `holder' `index' my_ncod `holder', oldvar(`comparator') drop `comparator' rename `holder' `comparator' my_ncod `holder', oldvar(`ipair') drop `ipair' rename `holder' `ipair' } local p: word count `regressors' if "`independent'`comparative'" != "" { local seregexpression = `"`se'"' local spregexpression = `"`sp'"' } else if "`cbnetwork'" != "" { if "`interaction'" == "" { local seregexpression = `"`se' i.`ipair'#c.`se' i.`index'#c.`se' "' local spregexpression = `"`sp' i.`ipair'#c.`sp' i.`index'#c.`sp'"' } else { local seregexpression = `"`se' i.`ipair'#i.`comparator'#c.`se' i.`index'#c.`se' "' local spregexpression = `"`sp' i.`ipair'#i.`comparator'#c.`sp' i.`index'#c.`sp'"' //nulllify local interaction } } else { *abnetwork local seregexpression local spregexpression } local catreg " " local contreg " " local basecode 1 tokenize `regressors' forvalues i = 1(1)`p' { capture confirm numeric var ``i'' if _rc != 0 { if "`alphasort'" != "" { sort ``i'' } my_ncod `holder', oldvar(``i'') drop ``i'' rename `holder' ``i'' local prefix_`i' "i" } else { local prefix_`i' "c" } if "`abnetwork'`comparative'" != "" & `i'==1 { if "`abnetwork'" != "" { local prefix_`i' "ibn" } if "`baselevel'" != "" { //Find the base level qui label list ``i'' local nlevels = r(max) local found = 0 local level 1 while !`found' & `level' < `=`nlevels'+1' { local lab:label ``i'' `level' if "`lab'" == "`baselevel'" { local found = 1 local basecode `level' } local ++level } } } /*Add the proper expression for regression*/ local seregexpression = "`seregexpression' `prefix_`i''.``i''#c.`se'" local spregexpression = "`spregexpression' `prefix_`i''.``i''#c.`sp'" if `i' > 1 & "`interaction'" != "" { if "`interaction'" == "se" { local seregexpression = "`seregexpression' `prefix_`i''.``i''#`prefix_1'.`1'#c.`se'" } else if "`interaction'" == "sp" { local spregexpression = "`spregexpression' `prefix_`i''.``i''#`prefix_1'.`1'#c.`sp'" } else { local seregexpression = "`seregexpression' `prefix_`i''.``i''#`prefix_1'.`1'#c.`se'" local spregexpression = "`spregexpression' `prefix_`i''.``i''#`prefix_1'.`1'#c.`sp'" } } //Pick out the interactor variable if `i' == 1 /*& "`interaction'" != "" */{ local varx = "``i''" if "`abnetwork'" != "" { local prefix_`i' = "i" } local typevarx = "`prefix_`i''" } * (`i' > 1 & "`interaction'" != "" ) | "`interaction'" == "" { //store the rest of variables if "`prefix_`i''" == "i" { local catreg "`catreg' ``i''" } else { local contreg "`contreg' ``i''" } *}/ } if "`cveffect'" == "sp" { local seregexpression "`se'" } else if "`cveffect'" == "sp" { local spregexpression "`sp'" } return local varx = "`varx'" return local typevarx = "`typevarx'" return local regexpression = "`seregexpression' `spregexpression'" return local seregexpression = "`seregexpression'" return local spregexpression = "`spregexpression'" return local catreg = "`catreg'" return local contreg = "`contreg'" return local basecode = "`basecode'" end /*+++++++++++++++++++++++++ SUPPORTING FUNCTIONS: ESTP +++++++++++++++++++++++++ estimate log odds or proportions after modelling +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ cap program drop estp program define estp, rclass version 14.1 syntax [if], estimates(string) [sumstat(string) depname(string) expit se(varname) DP(integer 2)) /// cveffect(string) interaction(string) catreg(varlist) contreg(varlist) power(integer 0) /// level(integer 95) by(varname) varx(varname) typevarx(string) regexpression(string) abnetwork cbnetwork stratify independent comparative ] tempname outmatrix contregmatrixout catregmatrixout bycatregmatrixout secontregmatrixout spcontregmatrixout outmatrixse /// outmatrixsp serow sprow outmatrixse outmatrixsp outmatrixr overallse overallsp Vmatrix byVmatrix *Nullify by *if "`stratify'" != "" { * local by *} marksample touse, strok tokenize `regexpression' if "`cbnetwork'" != "" { if "`interaction'" != "" { tokenize `2', parse("#") tokenize `1', parse(".") } else { tokenize `3', parse("#") tokenize `1', parse(".") } local index "`3'" local catreg = "`3' `catreg'" local varx //nullify } if "`abnetwork'" != "" { tokenize `1', parse("#") tokenize `1', parse(".") local assignment "`3'" local catreg = "`3' `catreg'" } if "`interaction'" != "" & "`typevarx'" == "i" { local idpairconcat "#`varx'" } if "`typevarx'" == "i" { if "`catreg'" == "" { local catreg = "`varx'" } } if "`typevarx'" == "c" { if "`contreg'" == "" { local contreg = "`varx'" } } local marginlist while "`catreg'" != "" { tokenize `catreg' if ("`1'" != "`by'" & "`by'" != "") | ("`by'" =="") { local marginlist = `"`marginlist' `1'`idpairconcat'"' } macro shift local catreg `*' } qui estimates restore `estimates' local byncatreg 0 if "`by'" != "" & "`stratify'" == "" { qui margin if `touse', predict(xb) over(`se' `by') level(`level') mat `bycatregmatrixout' = r(table)' mat `byVmatrix' = r(V) mat `bycatregmatrixout' = `bycatregmatrixout'[1..., 1..6] local byrnames :rownames `bycatregmatrixout' local byncatreg = rowsof(`bycatregmatrixout') } /*if "`abnetwork'`cbnetwork'" == "" { local grand "grand" local Overall "Overall" }*/ /*if "`comparative'" != "" & "`stratify'" != "" { local grand local Overall } else {*/ local grand "grand" local Overall "Overall" *} local ncatreg 0 qui margin `marginlist' if `touse', over(`se') predict(xb) `grand' level(`level') mat `catregmatrixout' = r(table)' mat `Vmatrix' = r(V) mat `catregmatrixout' = `catregmatrixout'[1..., 1..6] local rnames :rownames `catregmatrixout' local ncatreg = rowsof(`catregmatrixout') local init 1 local ncontreg 0 local contserownames = "" local contsprownames = "" if "`contreg'" != "" { foreach v of local contreg { summ `v' if `touse', meanonly local vmean = r(mean) qui margin if `touse', over(`se') predict(xb) at(`v'=`vmean') level(`level') mat `contregmatrixout' = r(table)' mat `contregmatrixout' = `contregmatrixout'[1..., 1..6] if `init' { local init 0 mat `secontregmatrixout' = `contregmatrixout'[2, 1...] mat `spcontregmatrixout' = `contregmatrixout'[1, 1...] } else { mat `secontregmatrixout' = `secontregmatrixout' \ `contregmatrixout'[2, 1...] mat `spcontregmatrixout' = `spcontregmatrixout' \ `contregmatrixout'[1, 1...] } local contserownames = "`contserownames' `v'" local contsprownames = "`contsprownames' `v'" local ++ncontreg } mat rownames `secontregmatrixout' = `contserownames' mat rownames `spcontregmatrixout' = `contsprownames' } if "`expit'" != "" { forvalues r = 1(1)`byncatreg' { mat `bycatregmatrixout'[`r', 1] = invlogit(`bycatregmatrixout'[`r', 1]) mat `bycatregmatrixout'[`r', 5] = invlogit(`bycatregmatrixout'[`r', 5]) mat `bycatregmatrixout'[`r', 6] = invlogit(`bycatregmatrixout'[`r', 6]) } forvalues r = 1(1)`ncatreg' { mat `catregmatrixout'[`r', 1] = invlogit(`catregmatrixout'[`r', 1]) mat `catregmatrixout'[`r', 5] = invlogit(`catregmatrixout'[`r', 5]) mat `catregmatrixout'[`r', 6] = invlogit(`catregmatrixout'[`r', 6]) } forvalues r = 1(1)`ncontreg' { mat `secontregmatrixout'[`r', 1] = invlogit(`secontregmatrixout'[`r', 1]) mat `secontregmatrixout'[`r', 5] = invlogit(`secontregmatrixout'[`r', 5]) mat `secontregmatrixout'[`r', 6] = invlogit(`secontregmatrixout'[`r', 6]) mat `spcontregmatrixout'[`r', 1] = invlogit(`spcontregmatrixout'[`r', 1]) mat `spcontregmatrixout'[`r', 5] = invlogit(`spcontregmatrixout'[`r', 5]) mat `spcontregmatrixout'[`r', 6] = invlogit(`spcontregmatrixout'[`r', 6]) } } local serownames = "" local sprownames = "" local rownamesmaxlen = 10 /*Default*/ *if "`grand'" != "" { local nrowss = `ncatreg' + `byncatreg' - 2 //Except the grand rows *} *else { local nrowscat = `ncatreg' + `byncatreg' *} //# equations if "`cveffect'" == "sesp" { local keq 2 } else { local keq 1 } mat `serow' = J(1, 6, .) mat `sprow' = J(1, 6, .) local initse 0 local initsp 0 local rnames = "`byrnames' `rnames'" //attach the bynames //Except the grand rows forvalues r = 1(1)`=`byncatreg' + `ncatreg' - `=2*`="`grand'"!=""''' { //Labels local rname`r':word `r' of `rnames' tokenize `rname`r'', parse("#") local parm = "`1'" local left = "`3'" local right = "`5'" tokenize `left', parse(.) local leftv = "`3'" local leftlabel = "`1'" if "`right'" == "" { if "`leftv'" != "" { if strpos("`rname`r''", "1b") == 0 { local lab:label `leftv' `leftlabel' } else { local lab:label `leftv' 1 } local eqlab "`leftv'" } else { local lab "`leftlabel'" local eqlab "" } local nlencovl : strlen local llab local nlencov = `nlencovl' + 1 } else { tokenize `right', parse(.) local rightv = "`3'" local rightlabel = "`1'" if strpos("`leftlabel'", "c") == 0 { if strpos("`leftlabel'", "o") != 0 { local indexo = strlen("`leftlabel'") - 1 local leftlabel = substr("`leftlabel'", 1, `indexo') } if strpos("`leftlabel'", "1b") == 0 { local llab:label `leftv' `leftlabel' } else { local llab:label `leftv' 1 } } else { local llab } if strpos("`rightlabel'", "c") == 0 { if strpos("`rightlabel'", "o") != 0 { local indexo = strlen("`rightlabel'") - 1 local rightlabel = substr("`rightlabel'", 1, `indexo') } if strpos("`rightlabel'", "1b") == 0 { local rlab:label `rightv' `rightlabel' } else { local rlab:label `rightv' 1 } } else { local rlab } if (("`rlab'" != "") + ("`llab'" != "")) == 0 { local lab = "`leftv'#`rightv'" local eqlab = "" } if (("`rlab'" != "") + ("`llab'" != "")) == 1 { local lab = "`llab'`rlab'" local eqlab = "`leftv'*`rightv'" } if (("`rlab'" != "") + ("`llab'" != "")) == 2 { local lab = "`llab'|`rlab'" local eqlab = "`leftv'*`rightv'" } local nlencovl : strlen local leftv local nlencovr : strlen local rightv local nlencov = `nlencovl' + `nlencovr' + 1 } local lab = ustrregexra("`lab'", " ", "_") local nlenlab : strlen local lab if "`eqlab'" != "" { local nlencov = `nlencov' } else { local nlencov = 0 } local rownamesmaxlen = max(`rownamesmaxlen', min(`=`nlenlab' + `nlencov' + 1', 32)) /*Check if there is a longer name*/ if "`cbnetwork'" != "" & "`eqlab'"=="`index'" { local eqlab "`index'" } //se or sp local parm = substr("`parm'", 1, 1) if `r' > `=`byncatreg'' { mat `outmatrixr' = `catregmatrixout'[`=`r' - `byncatreg'', 1...] //select the r'th row } else{ mat `outmatrixr' = `bycatregmatrixout'[`r', 1...] //select the r'th row } if `parm' == 0 { if `initsp' == 0 { mat `outmatrixsp' = `outmatrixr' } else { mat `outmatrixsp' = `outmatrixsp' \ `outmatrixr' } local initsp 1 local sprownames = "`sprownames' `eqlab':`lab'" } else { if `initse' == 0 { mat `outmatrixse' = `outmatrixr' } else { mat `outmatrixse' = `outmatrixse' \ `outmatrixr' } local initse 1 local serownames = "`serownames' `eqlab':`lab'" } } if `nrowscat' > 2 { mat rownames `outmatrixse' = `serownames' mat rownames `outmatrixsp' = `sprownames' } /*if "`interaction'" != "" { mat rownames `serow' = "Sensitivity--**" mat rownames `sprow' = "**--Specificity--**" //19 characters } else {*/ mat rownames `serow' = "Sensitivity" mat rownames `sprow' = "Specificity" //19 characters local rownamesmaxlen = max(`rownamesmaxlen', 19) /*Check if there is a longer name*/ *if "`grand'" != "" { mat `overallsp' = `catregmatrixout'[`=`ncatreg'-1', 1...] mat `overallse' = `catregmatrixout'[`ncatreg', 1...] /*} else { mat `overallsp' = mat `overallse' = }*/ mat rownames `overallse' = "Overall" mat rownames `overallsp' = "Overall" if `=`ncatreg' + `byncatreg' - 2' > 0 | `ncontreg' > 0 { if "`cveffect'" == "sesp" { *local rspec "---`="&"*`=`nrowss'/2 + `ncontreg'''--`="&"*`=`nrowss'/2 + `ncontreg'''-" if (`=`ncatreg' + `byncatreg' - 2' > 0) & (`ncontreg' > 0) { mat `outmatrix' = `serow' \ `outmatrixse' \ `secontregmatrixout' \ `overallse' \ `sprow' \ `outmatrixsp' \ `spcontregmatrixout' \ `overallsp' } else if (`=`ncatreg' + `byncatreg' - 2' > 0) & (`ncontreg' == 0) { mat `outmatrix' = `serow' \ `outmatrixse' \ `overallse' \ `sprow' \ `outmatrixsp' \ `overallsp' } else if (`=`ncatreg' + `byncatreg' - 2' == 0) & (`ncontreg' > 0) { mat `outmatrix' = `serow' \ `secontregmatrixout' \ `overallse' \ `sprow' \ `spcontregmatrixout' \ `overallsp' } } else { if "`cveffect'" == "se" { if (`=`ncatreg' + `byncatreg' - 2' > 0) & (`ncontreg' > 0) { mat `outmatrix' = `serow' \ `outmatrixse' \ `secontregmatrixout' \ `overallse' \ `sprow' \ `overallsp' } else if (`=`ncatreg' + `byncatreg' - 2' > 0) & (`ncontreg' == 0) { mat `outmatrix' = `serow' \ `outmatrixse' \ `overallse' \ `sprow' \ `overallsp' } else if (`=`ncatreg' + `byncatreg' - 2' == 0) & (`ncontreg' > 0) { mat `outmatrix' = `serow' \ `secontregmaritxout' \ `overallse' \ `sprow' \ `overallsp' } *local rspec "---`="&"*`=(`ncatreg' + `byncatreg' - 2)/2 + `ncontreg'''---" } else { if (`=`ncatreg' + `byncatreg' - 2' > 0) & (`ncontreg' > 0) { mat `outmatrix' = `serow' \ `overallse' \ `sprow' \ `outmatrixsp' \ `spcontregmatrixout' \ `overallsp' } else if (`=`ncatreg' + `byncatreg' - 2' > 0) & (`ncontreg' == 0) { mat `outmatrix' = `serow' \ `overallse' \ `sprow' \ `outmatrixsp' \ `overallsp' } else if (`=`ncatreg' + `byncatreg' - 2' == 0) & (`ncontreg' > 0) { mat `outmatrix' = `serow' \ `overallse' \ `sprow' \ `spcontregmatrixout' \ `overallsp' } *local rspec "-----`="&"*`=(`ncatreg' + `byncatreg' - 2)/2 + `ncontreg'''-" } } if (`=`ncatreg' + `byncatreg' - 2' > 0) & (`ncontreg' > 0) { mat `outmatrixse' = `outmatrixse' \ `secontregmatrixout' \ `overallse' mat `outmatrixsp' = `outmatrixsp' \ `spcontregmatrixout' \ `overallsp' } else if (`=`ncatreg' + `byncatreg' - 2' > 0) & (`ncontreg' == 0) { mat `outmatrixse' = `outmatrixse' \ `overallse' mat `outmatrixsp' = `outmatrixsp' \ `overallsp' } else if (`=`ncatreg' + `byncatreg' - 2' == 0) & (`ncontreg' > 0) { mat `outmatrixse' = `secontregmatrixout' \ `overallse' mat `outmatrixsp' = `spcontregmatrixout' \ `overallsp' } } else { mat rownames `overallse' = "Sensitivity" mat rownames `overallsp' = "Specificity" mat `outmatrixse' = `overallse' mat `outmatrixsp' = `overallsp' *local rspec "----" mat `outmatrix' = `overallse' \ `overallsp' local rownamesmaxlen = max(`rownamesmaxlen', 12) /*Check if there is a longer name*/ } if "`expit'" == "" { mat colnames `outmatrix' = `sumstat' SE z P>|z| Lower Upper mat colnames `outmatrixse' = `sumstat' SE z P>|z| Lower Upper mat colnames `outmatrixsp' = `sumstat' SE z P>|z| Lower Upper } else { mat colnames `outmatrix' = `sumstat' SE(logit) z(logit) P>|z| Lower Upper mat colnames `outmatrixse' = `sumstat' SE(logit) z(logit) P>|z| Lower Upper mat colnames `outmatrixsp' = `sumstat' SE(logit) z(logit) P>|z| Lower Upper } return matrix outmatrixse = `outmatrixse' return matrix outmatrixsp = `outmatrixsp' return matrix outmatrix = `outmatrix' return matrix Vmatrix = `Vmatrix' end /*+++++++++++++++++++++++++ SUPPORTING FUNCTIONS: ESTR +++++++++++++++++++++++++ Estimate RR after modelling +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ cap program drop estr program define estr, rclass version 13.1 syntax [if], estimates(string) [catreg(varlist) varx(varname) typevarx(string) sumstat(string) comparator(varname) /// se(varname) comparative level(integer 95) DP(integer 2) power(integer 0) /// cveffect(string) independent cbnetwork abnetwork stratify comparative by(varname) /// regexpression(string) baselevel(integer 1) refpos(string)] marksample touse, strok local ZOVE -invnorm((100 - `level')/200) tokenize `regexpression' if "`cbnetwork'" != "" { if "`interaction'" != "" { tokenize `2', parse("#") tokenize `1', parse(".") } else { tokenize `3', parse("#") tokenize `1', parse(".") } local index "`3'" *if "`by'" == "" { * local by = "`3'" *} *local varx //nullify } if "`abnetwork'`independent'" != "" { //nullify local varx local typevarx } if "`comparative'" != "" { *tokenize `catreg' *macro shift *local catreg "`*'" local idpairconcat "#`varx'" local typevarx "i" *Nullify by if "`stratify'" != "" { local by } } *if "`by'`comparator'" != "" { local confounders "`by' `catreg'" /*} else { local confounders "`catreg'" }*/ local marginlist while "`catreg'" != "" { tokenize `catreg' local marginlist = `"`marginlist' `1'`idpairconcat'"' macro shift local catreg "`*'" } tempname lcoef lV outmatrix outmatrixse outmatrixsp serow sprow outmatrixse outmatrixsp outmatrixr overallse overallsp setestnl sptestnl serowtestnl sprowtestnl testmat2print if "`marginlist'" != "" | ("`varx'" != "" & "`by'" != "" ){ qui estimates restore `estimates' if "`marginlist'" != "" { qui margins `marginlist' if `touse', predict(xb) over(`se') post level(`level') } if "`varx'" != "" & "`by'" != "" { qui margins `varx' if `touse', predict(xb) over(`se' `by') post level(`level') } local EstRlnexpression foreach c of local confounders { qui label list `c' local nlevels = r(max) local sp_test_`c' local se_test_`c' if "`typevarx'" == "i" { forvalues l = 1/`nlevels' { if `l' == 1 { local sp_test_`c' = "_b[sp_`c'_`l']" local se_test_`c' = "_b[se_`c'_`l']" } else { local sp_test_`c' = "_b[sp_`c'_`l'] = `sp_test_`c''" local se_test_`c' = "_b[se_`c'_`l'] = `se_test_`c''" } local EstRlnexpression = "`EstRlnexpression' (sp_`c'_`l': ln(invlogit(_b[0.`se'#`l'.`c'#2.`varx'])) - ln(invlogit(_b[0.`se'#`l'.`c'#1.`varx'])))" local EstRlnexpression = "`EstRlnexpression' (se_`c'_`l': ln(invlogit(_b[1.`se'#`l'.`c'#2.`varx'])) - ln(invlogit(_b[1.`se'#`l'.`c'#1.`varx'])))" } } else { local sp_test_`c' = "_b[sp_`c'_`baselevel']" local se_test_`c' = "_b[se_`c'_`baselevel']" local init 1 forvalues l = 1/`nlevels' { if "abnetwork" !="" { if `l' == 1 { local sp_test_`c' = "_b[sp_`c'_`l']" local se_test_`c' = "_b[se_`c'_`l']" } else { local sp_test_`c' = "_b[sp_`c'_`l'] = `sp_test_`c''" local se_test_`c' = "_b[se_`c'_`l'] = `se_test_`c''" } } else { if `l' != `baselevel' { if `init' == 1 { local sp_test_`c' = "_b[sp_`c'_`l']" local se_test_`c' = "_b[se_`c'_`l']" local init 0 } else { local sp_test_`c' = "_b[sp_`c'_`l'] = `sp_test_`c''" local se_test_`c' = "_b[se_`c'_`l'] = `se_test_`c''" } } } if "`refpos'" == "top" { local EstRlnexpression = "`EstRlnexpression' (sp_`c'_`l': ln(invlogit(_b[0.`se'#`baselevel'.`c'])) - ln(invlogit(_b[0.`se'#`l'.`c'])))" local EstRlnexpression = "`EstRlnexpression' (se_`c'_`l': ln(invlogit(_b[1.`se'#`baselevel'.`c'])) - ln(invlogit(_b[1.`se'#`l'.`c'])))" } else { local EstRlnexpression = "`EstRlnexpression' (sp_`c'_`l': ln(invlogit(_b[0.`se'#`l'.`c'])) - ln(invlogit(_b[0.`se'#`baselevel'.`c'])))" local EstRlnexpression = "`EstRlnexpression' (se_`c'_`l': ln(invlogit(_b[1.`se'#`l'.`c'])) - ln(invlogit(_b[1.`se'#`baselevel'.`c'])))" } } } } qui nlcom `EstRlnexpression', post level(`level') iterate(200) mat `lcoef' = e(b) mat `lV' = e(V) mat `lV' = vecdiag(`lV') local ncols = colsof(`lcoef') //length of the vector local rnames :colnames `lcoef' local rowtestnl local i = 1 foreach c of local confounders { qui label list `c' local nlevels = r(max) if (`nlevels' > 2 & "`comparative'" == "") | (`nlevels' > 1 & ("`comparative'" != "" | "`cbnetwork'" != "" )){ qui testnl (`se_test_`c''), iterate(200) local se_testnl_`c'_chi2 = r(chi2) local se_testnl_`c'_df = r(df) local se_testnl_`c'_p = r(p) qui testnl (`sp_test_`c'') local sp_testnl_`c'_chi2 = r(chi2) local sp_testnl_`c'_df = r(df) local sp_testnl_`c'_p = r(p) if `i'==1 { mat `setestnl' = [`se_testnl_`c'_chi2', `se_testnl_`c'_df', `se_testnl_`c'_p'] mat `sptestnl' = [`sp_testnl_`c'_chi2', `sp_testnl_`c'_df', `sp_testnl_`c'_p'] } else { mat `setestnl' = `setestnl' \ [`se_testnl_`c'_chi2', `se_testnl_`c'_df', `se_testnl_`c'_p'] mat `sptestnl' = `sptestnl' \ [`sp_testnl_`c'_chi2', `sp_testnl_`c'_df', `sp_testnl_`c'_p'] } local ++i local rowtestnl = "`rowtestnl' `c' " } } if `i' > 1 { mat rownames `setestnl' = `rowtestnl' mat rownames `sptestnl' = `rowtestnl' mat colnames `setestnl' = chi2 df p mat colnames `sptestnl' = chi2 df p mat roweq `setestnl' = Relative_Sensitivity mat roweq `sptestnl' = Relative_Specificity *mat `testmat2print' = `setestnl' \ `sptestnl' *mat colnames `testmat2print' = chi2 df p local inltest = "yes" } else { local inltest = "no" } if "`comparative'" != "" | "`cbnetwork'" != "" { mat `outmatrix' = J(`=`ncols' + 2', 6, .) } else { mat `outmatrix' = J(`ncols', 6, .) } forvalues r = 1(1)`ncols' { mat `outmatrix'[`r', 1] = exp(`lcoef'[1,`r']) /*Estimate*/ mat `outmatrix'[`r', 2] = sqrt(`lV'[1, `r']) /*se in log scale, power 1*/ mat `outmatrix'[`r', 3] = `lcoef'[1,`r']/sqrt(`lV'[1, `r']) /*Z in log scale*/ mat `outmatrix'[`r', 4] = normprob(-abs(`outmatrix'[`r', 3]))*2 /*p-value*/ mat `outmatrix'[`r', 5] = exp(`lcoef'[1, `r'] - `ZOVE' * sqrt(`lV'[1, `r'])) /*lower*/ mat `outmatrix'[`r', 6] = exp(`lcoef'[1, `r'] + `ZOVE' * sqrt(`lV'[1, `r'])) /*upper*/ } } else { mat `outmatrix' = J(2, 6, .) local ncols = 0 } if "`typevarx'" == "i" { qui estimates restore `estimates' qui margins `varx' if `touse', predict(xb) over(`se') post level(`level') //log metric if `baselevel' == 1 { local indexlevel "2" } else { local indexlevel "1" } if "`refpos'" == "bottom" { qui nlcom (sp_Overall: ln(invlogit(_b[0.`se'#`indexlevel'.`varx'])) - ln(invlogit(_b[0.`se'#`baselevel'.`varx']))) /// (se_Overall: ln(invlogit(_b[1.`se'#`indexlevel'.`varx'])) - ln(invlogit(_b[1.`se'#`baselevel'.`varx']))), iterate(200) } else { qui nlcom (sp_Overall: ln(invlogit(_b[0.`se'#`baselevel'.`varx'])) - ln(invlogit(_b[0.`se'#`indexlevel'.`varx']))) /// (se_Overall: ln(invlogit(_b[1.`se'#`baselevel'.`varx'])) - ln(invlogit(_b[1.`se'#`indexlevel'.`varx']))), iterate(200) } mat `lcoef' = r(b) mat `lV' = r(V) mat `lV' = vecdiag(`lV') forvalues r=1(1)2 { mat `outmatrix'[`=`ncols' + `r'', 1] = exp(`lcoef'[1,`r']) //rr mat `outmatrix'[`=`ncols' + `r'', 2] = sqrt(`lV'[1, `r']) //se mat `outmatrix'[`=`ncols' + `r'', 3] = `lcoef'[1, `r']/sqrt(`lV'[1, `r']) //zvalue mat `outmatrix'[`=`ncols' + `r'', 4] = normprob(-abs(`lcoef'[ 1, `r']/sqrt(`lV'[1, `r'])))*2 //pvalue mat `outmatrix'[`=`ncols' + `r'', 5] = exp(`lcoef'[1, `r'] - `ZOVE'*sqrt(`lV'[1, `r'])) //ll mat `outmatrix'[`=`ncols' + `r'', 6] = exp(`lcoef'[1, `r'] + `ZOVE'*sqrt(`lV'[1, `r'])) //ul } local rnames = "`rnames' sp_Overall se_Overall" } local sprownames = "" local serownames = "" local rspec = "-" /*draw lines or not between the rows*/ local rownamesmaxlen = 10 /*Default*/ local nrows = rowsof(`outmatrix') local initse 0 local initsp 0 forvalues r = 1(1)`ncols' { local rname`r':word `r' of `rnames' tokenize `rname`r'', parse("_") local parm = "`1'" local left = "`3'" local right = "`5'" mat `outmatrixr' = `outmatrix'[`r', 1...] //select the r'th row if "`5'" != "" { local lab:label `left' `right' local lab = ustrregexra("`lab'", " ", "_") local nlen : strlen local lab local rownamesmaxlen = max(`rownamesmaxlen', min(`nlen', 32)) //Check if there is a longer name local `parm'rownames = "``parm'rownames' `left':`lab'" if `init`parm'' == 0 { mat `outmatrix`parm'' = `outmatrixr' } else { mat `outmatrix`parm'' = `outmatrix`parm'' \ `outmatrixr' } local init`parm' 1 } } if `ncols' > 0 { mat rownames `outmatrixse' = `serownames' mat rownames `outmatrixsp' = `sprownames' } *mat out = `outmatrix' mat `serow' = J(1, 6, .) mat `sprow' = J(1, 6, .) mat rownames `serow' = "Relative Sensitivity" mat rownames `sprow' = "Relative Specificity" //20 chars local rownamesmaxlen = max(`rownamesmaxlen', 21) //Check if there is a longer name if ("`comparative'" !="" | "`cbnetwork'" != "") { mat `overallsp' = `outmatrix'[`=`nrows'-1', 1...] mat `overallse' = `outmatrix'[`nrows', 1...] mat rownames `overallse' = "Overall" mat rownames `overallsp' = "Overall" } if `ncols' > 0 & ("`comparative'`cbnetwork'" != ""){ if "`cveffect'" == "sesp" { *local rspec "---`="&"*`=`nrows'/2 - 1''--`="&"*`=`nrows'/2 - 1''-" mat `outmatrix' = `serow' \ `outmatrixse' \ `overallse' \ `sprow' \ `outmatrixsp' \ `overallsp' } else if "`cveffect'" == "se" { mat `outmatrix' = `serow' \ `outmatrixse' \ `overallse' \ `sprow' \ `overallsp' } else { mat `outmatrix' = `serow' \ `overallse' \ `sprow' \ `outmatrixsp' \ `overallsp' } *local rspec "--`="&"*`=`nrows'/2''-" mat `outmatrixse' = `outmatrixse' \ `overallse' mat `outmatrixsp' = `outmatrixsp' \ `overallsp' } if `ncols' > 0 & "`cbnetwork'`comparative'" =="" { if "`cveffect'" == "sesp" { *local rspec "---`="&"*`=`nrows'/2 - 1''--`="&"*`=`nrows'/2 - 1''-" mat `outmatrix' = `serow' \ `outmatrixse' \ `sprow' \ `outmatrixsp' mat `outmatrixse' = `serow' \ `outmatrixse' mat `outmatrixsp' = `sprow' \ `outmatrixsp' } else { if "`cveffect'" == "se" { mat `outmatrix' = `serow' \ `outmatrixse' mat `outmatrixse' = `serow' \ `outmatrixse' mat `outmatrixsp' = J(1, 6, .) } else { mat `outmatrix' = `sprow' \ `outmatrixsp' mat `outmatrixsp' = `sprow' \ `outmatrixsp' mat `outmatrixse' = J(1, 6, .) } *local rspec "--`="&"*`=`nrows'/2'-1'-" } } if `ncols' == 0 { mat `outmatrixse' = `overallse' mat `outmatrixsp' = `overallsp' mat `outmatrix' = `serow' \ `outmatrixse' \ `sprow' \ `outmatrixsp' *local rspec "--&-&-" } mat colnames `outmatrixse' = `sumstat' SE(lor) z(lor) P>|z| Lower Upper mat colnames `outmatrixsp' = `sumstat' SE(lor) z(lor) P>|z| Lower Upper mat colnames `outmatrix' = `sumstat' SE(lor) z(lor) P>|z| Lower Upper if "`inltest'" == "yes" { return matrix setestnl = `setestnl' return matrix sptestnl = `sptestnl' } return local inltest = "`inltest'" return matrix outmatrix = `outmatrix' return matrix outmatrixse = `outmatrixse' return matrix outmatrixsp = `outmatrixsp' end /*+++++++++++++++++++++++++ SUPPORTING FUNCTIONS: ESTCOVAR +++++++++++++++++++++++++ Compose the var-cov matrix +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ cap program drop estcovar program define estcovar, rclass version 14.0 syntax, matrix(name) model(string) [ bcov(string) wcov(string) abnetwork cbnetwork comparative independent ] *matrix is colvector tempname matcoef BVar WVar mat `matcoef' = `matrix'' local nrows = rowsof(`matcoef') *Initialize - Default mat `BVar' = (0, 0\ /// 0, 0) mat `WVar' = (0, 0\ /// 0, 0) local b = 0 local w = 0 if "`model'" == "random" { *WVAR if "`abnetwork'" != "" { if strpos("`wcov'", "ind") != 0 { mat `WVar' = (exp(`matcoef'[ `nrows' - 1, 1])^2, 0\ /// 0, exp(`matcoef'[ `nrows', 1])^2) local w = 2 } else if strpos("`wcov'", "id") != 0 { mat `WVar' = (exp(`matcoef'[ `nrows', 1])^2, 0 \ /// 0, exp(`matcoef'[ `nrows', 1])^2) local w = 1 } } *BVAR if strpos("`bcov'", "uns") != 0 { mat `BVar' = (exp(`matcoef'[`nrows' - 2 - `w', 1])^2, exp(`matcoef'[ `nrows' - 1 - `w', 1])*exp(`matcoef'[`nrows' - 2 - `w', 1])*tanh(`matcoef'[ `nrows' - `w', 1])\ /// exp(`matcoef'[ `nrows' - 1 - `w', 1])*exp(`matcoef'[`nrows' - 2 - `w', 1])*tanh(`matcoef'[ `nrows' - `w', 1]), exp(`matcoef'[ `nrows' - 1 - `w', 1])^2) local b = 3 } else if strpos("`bcov'", "ind") != 0 { mat `BVar' = (exp(`matcoef'[ `nrows' - 1 - `w', 1])^2, 0\ /// 0, exp(`matcoef'[ `nrows' - `w', 1])^2) local b = 2 } else if strpos("`bcov'", "exc") != 0 { mat `BVar' = (exp(`matcoef'[ `nrows' - 1 - `w', 1])^2, exp(`matcoef'[ `nrows' - 1 - `w', 1])*exp(`matcoef'[ `nrows' - 1 - `w', 1])*tanh(`matcoef'[ `nrows' - `w', 1])\ /// exp(`matcoef'[ `nrows' - 1 - `w', 1])*exp(`matcoef'[ `nrows' - 1 - `w', 1])*tanh(`matcoef'[ `nrows' - `w', 1]), exp(`matcoef'[ `nrows' - 1 - `w', 1])^2) local b = 2 } else if (strpos("`bcov'", "id") != 0) { mat `BVar' = (exp(`matcoef'[ `nrows' - `w', 1])^2, 0\ /// 0, exp(`matcoef'[ `nrows' - `w', 1])^2) local b = 1 } else if (strpos("`bcov'", "sp") != 0) { mat `BVar' = (0, 0\ /// 0, exp(`matcoef'[ `nrows' - `w', 1])^2) local b = 1 } else if (strpos("`bcov'", "se") != 0) { mat `BVar' = (exp(`matcoef'[ `nrows' - `w', 1])^2, 0\ /// 0, 0) local b = 1 } } local k = `b' + `w' return matrix WVar = `WVar' return matrix BVar = `BVar' return local k = `k' end /*+++++++++++++++++++++++++ SUPPORTING FUNCTIONS: KOOPMANCI +++++++++++++++++++++++++ CI for RR ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ cap program drop koopmanci program define koopmanci version 14.0 syntax varlist, RR(name) lowerci(name) upperci(name) [alpha(real 0.05)] qui { tokenize `varlist' gen `rr' = . gen `lowerci' = . gen `upperci' = . count forvalues i = 1/`r(N)' { local n1 = `1'[`i'] local N1 = `2'[`i'] local n2 = `3'[`i'] local N2 = `4'[`i'] koopmancii `n1' `N1' `n2' `N2', alpha(`alpha') mat ci = r(ci) if (`n1' == 0) &(`n2'==0) { replace `rr' = 0 in `i' } else { replace `rr' = (`n1'/`N1')/(`n2'/`N2') in `i' } replace `lowerci' = ci[1, 1] in `i' replace `upperci' = ci[1, 2] in `i' } } end /*+++++++++++++++++++++++++ SUPPORTING FUNCTIONS: KOOPMANCII +++++++++++++++++++++++++ CI for RR ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ cap program drop koopmancii program define koopmancii, rclass version 14.0 syntax anything(name=data id="data"), [alpha(real 0.05)] local len: word count `data' if `len' != 4 { di as error "Specify full data: n1 N1 n2 N2" exit } foreach num of local data { cap confirm integer number `num' if _rc != 0 { di as error "`num' found where integer expected" exit } } tokenize `data' cap assert ((`1' <= `2') & (`3' <= `4')) if _rc != 0{ di as err "Order should be n1 N1 n2 N2" exit _rc } mata: koopman_ci((`1', `2', `3', `4'), `alpha') return matrix ci = ci return scalar alpha = `alpha' end /* SUPPORTING FUNCTIONS: FPLOTCHECK ++++++++++++++++++++++++++++++++++++++++++ Advance housekeeping for the fplot +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ capture program drop fplotcheck program define fplotcheck, rclass version 14.1 #delimit ; syntax [, /*Passed from top*/ COMParative /*passed via foptions*/ AStext(integer 50) CIOpt(passthru) DIAMopt(passthru) DOUble LCols(varlist) noOVLine noSTATS ARRowopt(passthru) OLineopt(passthru) OUTplot(string) PLOTstat(passthru) //comma seperated POINTopt(passthru) SUBLine TEXts(real 1.5) XLIne(passthru) /*silent option*/ XLAbel(passthru) XTick(passthru) GRID GRAphsave(passthru) cbnetwork logscale abnetwork independent first(varname) by(varname) * ]; #delimit cr 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 `texts' < 0 { di as res "Warning: Negative text size (TEXTSize) are ignored" local texts 1 } if "`outplot'" == "" { local outplot abs } else { local outplot = strlower("`outplot'") local rc_ = ("`outplot'" == "rr") + ("`outplot'" == "abs") if `rc_' != 1 { di as error "Options outplot(`outplot') incorrectly specified" di as error "Allowed options: abs, rr" exit } if "`outplot'" == "rr" { cap assert "`abnetwork'`comparative'`cbnetwork'" != "" if _rc != 0 { di as error "Option outplot(rr) only avaialable with abnetwork/comparative/cbnetwork analysis" di as error "Specify analysis with -abnetwork/comparative/cbnetwork- option" exit _rc } } if ("`first'" != "" & "`by'" != "") & "`comparative'" != "" { cap assert ("`first'" != "`by'") & ("`output'" != "rr") if _rc != 0 { di as error "Remove the option by(`by') or specify a different by-variable" exit _rc } } } foreach var of local lcols { cap confirm var `var' if _rc!=0 { di in re "Variable `var' not in the dataset" exit _rc } } if "`lcols'" =="" { local lcols " " } if "`astext'" != "" { local astext "astext(`astext')" } if "`texts'" != "" { local texts "texts(`texts')" } local foptions `"`astext' `ciopt' `diamopt' `arrowopt' `double' `ovline' `stats' `olineopt' `plotstat' `pointopt' `subline' `texts' `xlabel' `xtick' `grid' `xline' `logscale' `graphsave' `options'"' return local outplot = "`outplot'" return local lcols ="`lcols'" return local foptions = `"`foptions'"' end /* SUPPORTING FUNCTIONS: FPLOT ++++++++++++++++++++++++++++++++++++++++++++++++ The forest plot +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ // Some re-used code from metaprop capture program drop fplot program define fplot version 14.1 #delimit ; syntax varlist [if] [in] [, /*Passed from top options*/ STudyid(varname) POWer(integer 0) DP(integer 2) Level(integer 95) /*passed from within*/ Groupvar(varname) /*passed via foptions*/ AStext(integer 50) ARRowopt(string) CIOpt(string) DIAMopt(string) DOUble LCols(varlist) noOVLine noSTATS OLineopt(string) OUTplot(string) PLOTstat(string asis) /*comma seperated*/ POINTopt(string) SUBLine TEXts(real 1.5) XLIne(string asis) XLAbel(string) XTick(string) GRID GRAPHSave(string asis) logscale abnetwork cbnetwork comparative independent * ]; #delimit cr local foptions `"`options'"' if strpos(`"`foptions'"', "graphregion") == 0 { local foptions `"graphregion(color(white)) `foptions'"' } tempvar effect lci uci use label tlabel id newid se df expand orig /// tokenize "`varlist'", parse(" ") qui { gen `effect'=`1'*(10^`power') gen `lci' =`2'*(10^`power') gen `uci' =`3'*(10^`power') gen byte `use'=`4' gen str `label'=`5' gen `df' = `6' gen `id' = `7' gen `se' = `8' if "`plotstat'"=="" { local outplot = strlower("`outplot'") if "`outplot'" == "rr" { local plotstatse "Relative Sensitivity" local plotstatsp "Relative Specificity" } else { local plotstatse "Sensitivity" local plotstatsp "Specificity" } } else { tokenize "`plotstat'", parse(",") local plotstatse "`1'" local plotstatsp "`3'" } qui summ `id' gen `expand' = 1 replace `expand' = 1 + 1*(`id'==r(min)) expand `expand' replace `id' = `id' + 1 if _n>2 replace `label' = "" if `id'==1 replace `use' = 0 if `id'==1 sort `id' `se' //studylables if "`abnetwork'" != "" & "`outplot'" == "rr" { local studylb: variable label `groupvar' if "`studylb'" == "" { label var `label' "`groupvar'" } else { label var `label' "`studylb'" } } else { local studylb: variable label `studyid' if "`studylb'" == "" { label var `label' "`studyid'" } else { label var `label' "`studylb'" } } if "`lcols'" == "" { local lcols "`label'" } else { local lcols "`label' `lcols'" } egen `newid' = group(`id') replace `id' = `newid' drop `newid' tempvar estText estTextse estTextsp index gen str `estText' = string(`effect', "%10.`=`dp''f") + " (" + string(`lci', "%10.`=`dp''f") + ", " + string(`uci', "%10.`=`dp''f") + ")" if (`use' == 1 | `use' == 2 | `use' == 3) // GET MIN AND MAX DISPLAY // SORT OUT TICKS- CODE PINCHED FROM MIKE AND FIRandomED. TURNS OUT I'VE BEEN USING SIMILAR NAMES... // AS SUGGESTED BY JS JUST ACCEPT ANYTHING AS TICKS AND RESPONSIBILITY IS TO USER! if "`logscale'" != "" { replace `effect' = ln(`effect') replace `lci' = ln(`lci') replace `uci' = ln(`uci') } qui summ `lci', detail local DXmin = r(min) qui summ `uci', detail local DXmax = r(max) if "`xlabel'" != "" { if "`logscale'" != "" { local DXmin = ln(min(`xlabel')) local DXmax = ln(max(`xlabel')) } else{ local DXmin = min(`xlabel') local DXmax = max(`xlabel') } } if "`xlabel'"=="" { local xlabel "0, `DXmax'" } local lblcmd "" tokenize "`xlabel'", parse(",") while "`1'" != ""{ if "`1'" != ","{ local lbl = string(`1',"%7.3g") if "`logscale'" != "" { if "`1'" == "0" { local val = ln(`=10^(-`dp')') } else { local val = ln(`1') } } else { local val = `1' } local lblcmd `lblcmd' `val' "`lbl'" } mac shift } if "`xtick'" == ""{ local xtick = "`xlabel'" } local xtick2 = "" tokenize "`xtick'", parse(",") while "`1'" != ""{ if "`1'" != ","{ if "`logscale'" != "" { if "`1'" == "0" { local val = ln(`=10^(-`dp')') } else { local val = ln(`1') } } else { local val = `1' } local xtick2 = "`xtick2' " + string(`val') } if "`1'" == ","{ local xtick2 = "`xtick2'`1'" } mac shift } local xtick = "`xtick2'" local DXmin1= (min(`xtick',`DXmin')) local DXmax1= (max(`xtick',`DXmax')) *local DXmin1= (min(`xlabel',`xtick',`DXmin')) *local DXmax1= (max(`xlabel',`xtick',`DXmax')) local DXwidth = `DXmax1'-`DXmin1' } // END QUI /*===============================================================================================*/ /*==================================== COLUMNS ================================================*/ /*===============================================================================================*/ qui { // KEEP QUIET UNTIL AFTER DIAMONDS local titleOff = 0 if "`lcols'" == "" { local lcols = "`label'" local titleOff = 1 } // DOUBLE LINE OPTION if "`double'" != "" & ("`lcols'" != "" | "`stats'" == ""){ *gen `orig' = `id' replace `expand' = 1 replace `expand' = 2 if `use' == 1 expand `expand' sort `id' `se' bys `id' `se': gen `index' = _n sort `se' `id' `index' egen `newid' = group(`id' `index') replace `id' = `newid' drop `newid' replace `use' = 1 if `index' == 2 replace `effect' = . if `index' == 2 replace `lci' = . if `index' == 2 replace `uci' = . if `index' == 2 replace `estText' = "" if `index' == 2 /* replace `id' = `id' + 0.75 if `id' == `id'[_n-1] & `se' == `se'[_n-1] & (`use' == 1) replace `use' = 1 if mod(`id',1) != 0 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 */ foreach var of varlist `lcols' { 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) & (`index' == 1) replace `var' = substr(`var',`splitwhere',length(`var')) if (`tosplit' == 1) & (`index' == 2) replace `var' = "" if (`tosplit' != 1) & (`index' == 2) & (`use' == 1) drop `length' `words' `tosplit' `splitwhere' `best' } if _rc != 0{ replace `var' = . if (`index' == 2) & (`use' == 1) } } } tempvar flag summ `id' local max = r(max) local new = r(N) + 4 set obs `new' gen `flag' = 0 replace `flag' = 1 if `id' == . forvalues i = 1/4 { // up to four lines for titles local Nnew`i' = r(N)+`i' } 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) | (`se' != 1) 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'' //`Nnew`line'' ONDOC 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' == -2 |`use' == 2 | `use' == 3) // put titles back in (overall, sub est etc.) if "`stats'" == "" { gen `estTextse' = `estText' if `se'== 1 gen `estTextsp' = `estText' + " " if `se'== 0 local rcols = "`estTextse' `estTextsp'" label var `estTextse' "`plotstatse' (`level'% CI)" label var `estTextsp' "`plotstatsp' (`level'% CI)" } else { gen `extra1' = " " gen `extra2' = " " label var `extra1' " " label var `extra2' " " local rcols = "`extra1' `extra2'" } 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' } count if !`flag' forvalues i = 1/`maxline' { // up to four lines for titles local multip = 1 local add = 0 local idNew`i' = `i' local Nnew`i' = r(N)+`i' local tmp = `Nnew`i'' replace `id' = `maxline' -`idNew`i'' + 1 in `tmp' replace `use' = 0 in `tmp' if `i' == `maxline' { local borderline = `idNew`i'' + 0.75 } } summ `id' if `flag' local max = ceil(r(max)) replace `id' = `id' + `max' if `flag'==0 replace `expand' = 1 replace `expand' = 2 if `flag' replace `se' = 0 if `se' == . count if `expand' > 1 local nnewlines = r(N) expand `expand' replace `se' = 1 in `=_N - `nnewlines' + 1'/`=_N' replace `use' = -2 if `flag' sort `id' `se' local skip = 1 if "`stats'" == "" { // sort out titles for stats and weight, if there local skip = 3 } if "`stats'" != "" { local skip = 2 } replace `rightLB1' = "" if (`se' == 0) replace `rightLB2' = "" if (`se' == 1) forvalues i = 1/`lcolsN'{ replace `leftLB`i'' = "" if (`se' == 0) } local leftWDtot = 0 local rightWDtot = 0 forvalues i = 1/`lcolsN'{ getWidth `leftLB`i'' `leftWD`i'' qui summ `leftWD`i'' local maxL = r(max) local leftWDtot = `leftWDtot' + `maxL' replace `leftWD`i'' = `maxL' local leftWD`i' = `maxL' } forvalues i = 1/`rcolsN'{ getWidth `rightLB`i'' `rightWD`i'' qui summ `rightWD`i'' replace `rightWD`i'' = r(max) local rightWD`i' = r(max) local rightWDtot = `rightWDtot' + r(max) } local LEFT_WD = `leftWDtot' local RIGHT_WD = `rightWDtot' local ratio = `astext' // USER SPECIFIED- % OF GRAPH TAKEN BY TEXT (ELSE NUM COLS CALC?) local textWD = ((2*`DXwidth')/(1-`ratio'/100)-(2*`DXwidth')) /(`leftWDtot'+`rightWDtot') local AXmin = `DXmin1' - `leftWDtot'*`textWD' local AXmax = `DXmax1' + `DXwidth' + `rightWDtot'*`textWD' local step 0 forvalues i = 1/`lcolsN'{ gen `left`i'' = `AXmin' + `step' local step = `leftWD`i''*`textWD' + `step' } local DXmin2 = `DXmax1' + `rightWD1'*`textWD' local DXmax2 = `DXmin2' + `DXwidth' gen `right1' = `DXmax1' gen `right2' = `DXmax2' *replace `effect' = `effect' + `DXmax1' - `DXmin1' + 0.5*`rightWDtot'*`textWD' if !`se' *replace `lci' = `lci' + `DXmax1' - `DXmin1' + 0.5*`rightWDtot'*`textWD' if !`se' *replace `uci' = `uci' + `DXmax1' - `DXmin1' + 0.5*`rightWDtot'*`textWD' if !`se' /* 16-09-2020 replace `effect' = `effect' + `DXmin2' if !`se' replace `lci' = `lci' + `DXmin2' if !`se' replace `uci' = `uci' + `DXmin2' if !`se' */ // DIAMONDS tempvar DIAMleftX DIAMrightX DIAMbottomX DIAMtopX DIAMleftY1 DIAMrightY1 DIAMleftY2 DIAMrightY2 DIAMbottomY DIAMtopY gen `DIAMleftX' = `lci' if `use' == 2 | `use' == 3 gen `DIAMleftY1' = `id' if (`use' == 2 | `use' == 3) gen `DIAMleftY2' = `id' if (`use' == 2 | `use' == 3) gen `DIAMrightX' = `uci' if (`use' == 2 | `use' == 3) gen `DIAMrightY1' = `id' if (`use' == 2 | `use' == 3) gen `DIAMrightY2' = `id' if (`use' == 2 | `use' == 3) gen `DIAMbottomY' = `id' - 0.4 if (`use' == 2 | `use' == 3) gen `DIAMtopY' = `id' + 0.4 if (`use' == 2 | `use' == 3) gen `DIAMtopX' = `effect' if (`use' == 2 | `use' == 3) replace `DIAMleftX' = `DXmin1' if (`lci' < `DXmin1' ) & (`use' == 2 | `use' == 3) replace `DIAMleftX' = . if (`effect' < `DXmin1' ) & (`use' == 2 | `use' == 3) //If one study, no diamond replace `DIAMleftX' = . if (`df' < 2) & (`use' == 2 | `use' == 3) replace `DIAMleftY1' = `id' + 0.4*(abs((`DXmin1' -`lci')/(`effect'-`lci'))) if (`lci' < `DXmin1' ) & (`use' == 2 | `use' == 3) replace `DIAMleftY1' = . if (`effect' < `DXmin1' ) & (`use' == 2 | `use' == 3) replace `DIAMleftY2' = `id' - 0.4*( abs((`DXmin1' -`lci')/(`effect'-`lci')) ) if (`lci' < `DXmin1' ) & (`use' == 2 | `use' == 3) replace `DIAMleftY2' = . if (`effect' < `DXmin1' ) & (`use' == 2 | `use' == 3) replace `DIAMrightX' = `DXmax`r'' if (`uci' > `DXmax1' ) & (`use' == 2 | `use' == 3) replace `DIAMrightX' = . if (`effect' > `DXmax1' ) & (`use' == 2 | `use' == 3) //If one study, no diamond replace `DIAMrightX' = . if (`df' == 1) & (`use' == 2 | `use' == 3) replace `DIAMrightY1' = `id' + 0.4*( abs((`uci'-`DXmax1' )/(`uci'-`effect')) ) if (`uci' > `DXmax1' ) & (`use' == 2 | `use' == 3) replace `DIAMrightY1' = . if (`effect' > `DXmax1' ) & (`use' == 2 | `use' == 3) replace `DIAMrightY2' = `id' - 0.4*( abs((`uci'-`DXmax1' )/(`uci'-`effect')) ) if (`uci' > `DXmax1' ) & (`use' == 2 | `use' == 3) replace `DIAMrightY2' = . if (`effect' > `DXmax1' ) & (`use' == 2 | `use' == 3) replace `DIAMbottomY' = `id' - 0.4*( abs((`uci'-`DXmin1' )/(`uci'-`effect')) ) if (`effect' < `DXmin1' ) & (`use' == 2 | `use' == 3) replace `DIAMbottomY' = `id' - 0.4*( abs((`DXmax1' -`lci')/(`effect'-`lci')) ) if (`effect' > `DXmax1' ) & (`use' == 2 | `use' == 3) replace `DIAMtopY' = `id' + 0.4*( abs((`uci'-`DXmin1' )/(`uci'-`effect')) ) if (`effect' < `DXmin1' ) & (`use' == 2 | `use' == 3) replace `DIAMtopY' = `id' + 0.4*( abs((`DXmax1' -`lci')/(`effect'-`lci')) ) if (`effect' > `DXmax1' ) & (`use' == 2 | `use' == 3) replace `DIAMtopX' = `DXmin1' if (`effect' < `DXmin1' ) & (`use' == 2 | `use' == 3) replace `DIAMtopX' = `DXmax1' if (`effect' > `DXmax1' ) & (`use' == 2 | `use' == 3) replace `DIAMtopX' = . if ((`uci' < `DXmin1' ) | (`lci' > `DXmax1' )) & (`use' == 2 | `use' == 3) gen `DIAMbottomX' = `DIAMtopX' } // END QUI forvalues i = 1/`lcolsN'{ local lcolCommands`i' "(scatter `id' `left`i'', msymbol(none) mlabel(`leftLB`i'') mlabcolor(black) mlabpos(3) mlabsize(`texts'))" } forvalues i = 1/`rcolsN' { local rcolCommands`i' "(scatter `id' `right`i'', msymbol(none) mlabel(`rightLB`i'') mlabcolor(black) mlabpos(3) mlabsize(`texts'))" } if `"`diamopt'"' == "" { local diamopt "lcolor("255 0 0")" } else { if strpos(`"`diamopt'"',"hor") != 0 | strpos(`"`diamopt'"',"vert") != 0 { di as error "Options horizontal/vertical not allowed in diamopt()" exit } if strpos(`"`diamopt'"',"con") != 0{ di as error "Option connect() not allowed in diamopt()" exit } if strpos(`"`diamopt'"',"lp") != 0{ di as error "Option lpattern() not allowed in diamopt()" exit } local diamopt `"`diamopt'"' } //Point options if `"`pointopt'"' != "" & strpos(`"`pointopt'"',"msy") == 0{ local pointopt = `"`pointopt' msymbol(O)"' } if `"`pointopt'"' != "" & strpos(`"`pointopt'"',"msi") == 0{ local pointopt = `"`pointopt' msize(vsmall)"' } if `"`pointopt'"' != "" & strpos(`"`pointopt'"',"mc") == 0{ local pointopt = `"`pointopt' mcolor(black)"' } if `"`pointopt'"' == ""{ local pointopt "msymbol(O) msize(vsmall) mcolor("0 0 0")" } else{ local pointopt `"`pointopt'"' } // CI options if `"`ciopt'"' == "" { local ciopt "lcolor("0 0 0")" } else { if strpos(`"`ciopt'"',"hor") != 0 | strpos(`"`ciopt'"',"ver") != 0{ di as error "Options horizontal/vertical not allowed in ciopt()" exit } if strpos(`"`ciopt'"',"con") != 0{ di as error "Option connect() not allowed in ciopt()" exit } if strpos(`"`ciopt'"',"lp") != 0{ di as error "Option lpattern() not allowed in ciopt()" exit } if `"`ciopt'"' != "" & strpos(`"`ciopt'"',"lc") == 0{ local ciopt = `"`ciopt' lcolor("0 0 0")"' } local ciopt `"`ciopt'"' } // Arrow options if `"`arrowopt'"' == "" { local arrowopt "mcolor("0 0 0") lstyle(none)" } else { local forbidden "connect horizontal vertical lpattern lwidth lcolor lsytle" foreach option of local forbidden { if strpos(`"`arrowopt'"',"`option'") != 0 { di as error "Option `option'() not allowed in arrowopt()" exit } } if `"`arrowopt'"' != "" & strpos(`"`arrowopt'"',"mc") == 0{ local arrowopt = `"`arrowopt' mcolor("0 0 0")"' } local arrowopt `"`arrowopt' lstyle(none)"' } // END GRAPH OPTS tempvar tempOv overrallLine ovMin ovMax h0Line if `"`olineopt'"' == "" { local olineopt "lwidth(thin) lcolor(red) lpattern(shortdash)" } if `"`vlineopt'"' == "" { local vlineopt "lwidth(thin) lcolor(black) lpattern(solid)" } qui summ `id' local DYmin = r(min) local DYmax = r(max)+2 forvalues r= 1(1)2 { qui summ `effect' if `use' == 3 & `se' == `=2 - `r'' local overall`r' = r(max) /*if `r'== 2 { local overall2 = `overall`r'' + `DXmin2' } local overallCommand`r' `" (pci `=`DYmax'-2' `overall`r'' `borderline' `overall`r'', `olineopt') "' */ if `overall`r'' > `DXmax1' | `overall`r'' < `DXmin1' | "`ovline'" != "" { // ditch if not on graph local overallCommand`r' "" } else { if `r'== 2 { local overall`r' = `overall`r'' + `DXmin2' - `DXmin1' } local overallCommand`r' `" (pci `=`DYmax'-2' `overall`r'' `borderline' `overall`r'', `olineopt') "' } if "`ovline'" != "" { local overallCommand`r' "" } if "`subline'" != "" & "`groupvar'" != "" { local sublineCommand`r' "" qui label list `groupvar' local nlevels = r(max) forvalues l = 1/`nlevels' { qui summ `effect' if `use' == 2 & `groupvar' == `l' & (`se' == `=`r' - 1') local tempSub`l' = r(mean) if `r'== 1 { local tempSub`l' = `tempSub`l'' + `DXmin2' - `DXmin1' } qui summ `id' if `use' == 1 & `groupvar' == `l' local subMax`l' = r(max) + 1 local subMin`l' = r(min) - 2 qui count if `use' == 1 & `groupvar' == `l' & (`se' == `=`r' - 1') if r(N) > 1 { local sublineCommand`r' `" `sublineCommand`r'' (pci `subMin`l'' `tempSub`l'' `subMax`l'' `tempSub`l'', `olineopt')"' } } } else { local sublineCommand`r' "" } } if `"`xline'"' != `""' { tokenize "`xline'", parse(",") if "`logscale'" != "" { if "`1'" == "0" { local xlineval = ln(`=10^(-`dp')') } else { local xlineval = ln(`1') } } else { local xlineval = `1' } if "`3'" != "" { local xlineopts = "`3'" } else { local xlineopts = "lcolor(black)" } local xlineval2 = `xlineval' + `DXmin2' - `DXmin1' local vlineCommand1 `" (pci `=`DYmax'-2' `xlineval' `borderline' `xlineval', `xlineopts') "' local vlineCommand2 `" (pci `=`DYmax'-2' `xlineval2' `borderline' `xlineval2', `xlineopts') "' if (`xlineval' > `DXmax1' | `xlineval' < `DXmin1') & "`xline'" != "" { // ditch if not on graph local vlineCommand1 "" local vlineCommand2 "" } } qui { //Generate indicator on direction of the off-scale arro tempvar rightarrow leftarrow biarrow noarrow rightlimit leftlimit offRhiY offRhiX offRloY offRloX offLloY offLloX offLhiY offLhiX gen `rightarrow' = 0 gen `leftarrow' = 0 gen `biarrow' = 0 gen `noarrow' = 0 replace `rightarrow' = 1 if /// (((round(`uci', 0.001) > round(`DXmax1' , 0.001)) & (round(`lci', 0.001) >= round(`DXmin1' , 0.001))) | /// ((round(`uci', 0.001) > round(`DXmax1' , 0.001)) & (round(`lci', 0.001) > round(`DXmax1' , 0.001)))) & /// (`use' == 1) & (`uci' != .) & (`lci' != .) replace `leftarrow' = 1 if /// (((round(`lci', 0.001) < round(`DXmin1' , 0.001)) & (round(`uci', 0.001) <= round(`DXmax1' , 0.001))) | /// ((round(`lci', 0.001) < round(`DXmin1' , 0.001)) & (round(`uci', 0.001) < round(`DXmin1' , 0.001)))) & /// (`use' == 1) & (`uci' != .) & (`lci' != .) replace `biarrow' = 1 if /// (round(`lci', 0.001) < round(`DXmin1' , 0.001)) & /// (round(`uci', 0.001) > round(`DXmax1' , 0.001)) & /// (`use' == 1) & (`uci' != .) & (`lci' != .) replace `noarrow' = 1 if /// (`leftarrow' != 1) & (`rightarrow' != 1) & (`biarrow' != 1) & /// (`use' == 1) & (`uci' != .) & (`lci' != .) replace `lci' = `DXmin1' if (round(`lci', 0.001) < round(`DXmin1' , 0.001)) & (`lci' !=.) & (`use' == 1) replace `uci' = `DXmax1' if (round(`uci', 0.001) > round(`DXmax1' , 0.001)) & (`uci' !=.) & (`use' == 1) replace `lci' = `DXmax1' - 0.00001 if (round(`lci', 0.001) > round(`DXmax1' , 0.001)) & (`lci' !=. ) & (`use' == 1) replace `uci' = `DXmin1' + 0.00001 if (round(`uci', 0.001) < round(`DXmin1' , 0.001)) & (`uci' !=. ) & (`use' == 1) *replace `lci' = . if (round(`lci', 0.001) > round(`DXmax1' , 0.001)) & (`lci' !=. ) & (`use' == 1) *replace `uci' = . if (round(`uci', 0.001) < round(`DXmin1' , 0.001)) & (`uci' !=. ) & (`use' == 1) replace `effect' = . if (round(`effect', 0.001) < round(`DXmin1' , 0.001)) & (`use' == 1) replace `effect' = . if (round(`effect', 0.001) > round(`DXmax1' , 0.001)) & (`use' == 1) summ `id' local xaxislineposition = r(max) local xaxis1 "(pci `xaxislineposition' `DXmin1' `xaxislineposition' `DXmax1', lwidth(thin) lcolor(black))" local xaxis2 "(pci `xaxislineposition' `DXmin2' `xaxislineposition' `DXmax2', lwidth(thin) lcolor(black))" /*Xaxis 1 title */ local xaxistitlex1 `=(`DXmax1' + `DXmin1')*0.5' local xaxistitlex2 `=(`DXmax2' + `DXmin2')*0.5' local xaxistitle1 (scatteri `=`xaxislineposition' + 2.25' `xaxistitlex1' "`plotstatse'", msymbol(i) mlabcolor(black) mlabpos(0) mlabsize(`texts')) local xaxistitle2 (scatteri `=`xaxislineposition' + 2.25' `xaxistitlex2' "`plotstatsp'", msymbol(i) mlabcolor(black) mlabpos(0) mlabsize(`texts')) /*xticks*/ local ticksx1 local ticksx2 tokenize "`xtick'", parse(",") while "`1'" != "" { if "`1'" != "," { forvalues r=1(1)2 { local where = `1' if `r' == 2 { local where = `1' + `DXmin2' - `DXmin1' } local ticksx`r' "`ticksx`r'' (pci `xaxislineposition' `where' `=`xaxislineposition'+.25' `where' , lwidth(thin) lcolor(black)) " } } macro shift } /*labels*/ local xaxislabels tokenize `lblcmd' while "`1'" != ""{ forvalues r = 1(1)2 { local where = `1' if `r' == 2 { *local where = `1' + `DXmax1' - `DXmin1' + 0.5*`rightWDtot'*`textWD' local where = `1' + `DXmin2' - `DXmin1' } local xaxislabels`r' "`xaxislabels`r'' (scatteri `=`xaxislineposition'+1' `where' "`2'", msymbol(i) mlabcolor(black) mlabpos(0) mlabsize(`texts'))" } macro shift 2 } if "`grid'" != "" { tempvar gridy gridxmax gridxmin gen `gridy' = `id' + 0.5 gen `gridxmax' = `AXmax' gen `gridxmin' = `left1' local betweengrids "(pcspike `gridy' `gridxmin' `gridy' `gridxmax' if `use' == 1 , lwidth(vvthin) lcolor(gs12))" } //Shift the position of sensitivitiy plot replace `lci' = `lci' + `DXmin2' - `DXmin1' if !`se' replace `uci' = `uci' + `DXmin2' - `DXmin1' if !`se' replace `effect' = `effect' + `DXmin2' - `DXmin1' if !`se' replace `DIAMbottomX' = `DIAMbottomX' + `DXmin2' - `DXmin1' if !`se' replace `DIAMtopX' = `DIAMtopX' + `DXmin2' - `DXmin1' if !`se' replace `DIAMleftX' = `DIAMleftX' + `DXmin2' - `DXmin1' if !`se' replace `DIAMrightX' = `DIAMrightX' + `DXmin2' - `DXmin1' if !`se' } // end qui /*===============================================================================================*/ /*==================================== GRAPH ================================================*/ /*===============================================================================================*/ #delimit ; twoway /*NOTE FOR RF, AND OVERALL LINES FIRST */ `notecmd' `overallCommand1' `sublineCommand1' `overallCommand2' `sublineCommand2' `hetGroupCmd' `xaxis1' `xaxistitle1' `ticksx1' `xaxislabels1' `xaxis2' `xaxistitle2' `ticksx2' `xaxislabels2' `vlineCommand1' `vlineCommand2' /*COLUMN VARIABLES */ `lcolCommands1' `lcolCommands2' `lcolCommands3' `lcolCommands4' `lcolCommands5' `lcolCommands6' `lcolCommands7' `lcolCommands8' `lcolCommands9' `lcolCommands10' `lcolCommands11' `lcolCommands12' `rcolCommands1' `rcolCommands2' /*PLOT EMPTY POINTS AND PUT ALL THE GRAPH OPTIONS IN THERE */ (scatter `id' `effect' if `use' == 1, msymbol(none) yscale(range(`DYmin' `DYmax') noline reverse) ylabel(none) ytitle("") xscale(range(`AXmin' `AXmax') noline) xlabel(none) yline(`borderline', lwidth(thin) lcolor(gs12)) xtitle("") legend(off) xtick("")) /*HERE ARE GRIDS */ `betweengrids' /*HERE ARE THE CONFIDENCE INTERVALS */ (pcspike `id' `lci' `id' `uci' if `use' == 1 , `ciopt') /*ADD ARROWS `ICICmd1' `ICICmd2' `ICICmd3'*/ (pcarrow `id' `uci' `id' `lci' if `leftarrow' == 1 , `arrowopt') (pcarrow `id' `lci' `id' `uci' if `rightarrow' == 1 , `arrowopt') (pcbarrow `id' `lci' `id' `uci' if `biarrow' == 1 , `arrowopt') /*DIAMONDS FOR SUMMARY ESTIMATES -START FROM 9 O'CLOCK */ (pcspike `DIAMleftY1' `DIAMleftX' `DIAMtopY' `DIAMtopX' if (`use' == 2 | `use' == 3) , `diamopt') (pcspike `DIAMtopY' `DIAMtopX' `DIAMrightY1' `DIAMrightX' if (`use' == 2 | `use' == 3) , `diamopt') (pcspike `DIAMrightY2' `DIAMrightX' `DIAMbottomY' `DIAMbottomX' if (`use' == 2 | `use' == 3) , `diamopt') (pcspike `DIAMbottomY' `DIAMbottomX' `DIAMleftY2' `DIAMleftX' if (`use' == 2 | `use' == 3) , `diamopt') /*LAST OF ALL PLOT EFFECT MARKERS TO CLARIFY */ (scatter `id' `effect' if `use' == 1 , `pointopt') ,`foptions' name(fplot, replace) ; #delimit cr if "$by_index_" != "" { qui graph dir local gnames = r(list) local gname: word $by_index_ of `gnames' tokenize `gname', parse(".") if "`3'" != "" { local ext =".`3'" } qui graph rename fplot`ext' fplot$by_index_`ext', replace } if `"`graphsave'"' != `""' { di _n noi graph save `graphsave', replace } end /*==================================== GETWIDTH ================================================*/ /*===============================================================================================*/ capture program drop getWidth program define getWidth version 14.0 //From metaprop 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 /*+++++++++++++++++++ SUPPORTING FUNCTIONS: SROC ++++++++++++++++++++++++++++++++++++ DRAW THE SROC CURVES, CROSSES, CONFIDENCE & PREDICTION REGION +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ cap program drop sroc program define sroc version 14.0 #delimit ; syntax varlist, selogodds(name) /*Log odds for se*/ splogodds(name) /*Log odds for sp*/ v(name) /*Var-cov for log odds se & se*/ bvar(name) /*Between study var-cov*/ model(string) /*model*/ [ groupvar(name) /*Grouping variable*/ p(integer 0) /*No of parameters in the regression equation*/ cimethod(string) /*How to compute the study-specific CI*/ LEVel(integer 95) /*Significance level*/ COLorpalette(string) /*soptions: Colors seperated by space*/ noPREDiction /*soptions:no Prediction region*/ noCURve /*soptions:no curve*/ Bubbles /*soptions: size of study by bubbles*/ BUBbleid /*soptions: Identify the bubbles by index*/ SPointopt(string) /*soptions: options study points*/ OPointopt(string) /*soptions: options the overall summary points*/ CUrveopt(string) /*soptions: options the CI points*/ CIopt(string) /*soptions: options the CI points*/ PREDCIopt(string) /*soptions: options the PREDCI points*/ BUBOpt(string) /*soptions: options the bubble points*/ BIDopt(string) /*soptions: options the bubble ID points*/ GRAPHSave(string asis) STRAtify /*Stratify*/ * /*soptions:Other two-way options*/ ] ; #delimit cr tempvar se selci seuci sp splci spuci csp Ni rowid Dis tp fp NDis fn tn mu gvar tempname vi bvari local soptions `"`options'"' tokenize `varlist' gen `tp' = `1' gen `fp' = `2' gen `fn' = `3' gen `tn' = `4' gen `Dis' = (`tp' + `fn') gen `NDis' = (`fp' + `tn') gen `Ni' = `Dis' +`NDis' gen `rowid' = _n if "`groupvar'" != "" { my_ncod `gvar', oldvar(`groupvar') // } //CI metadta_propci `Dis' `tp', p(`se') lowerci(`selci') upperci(`seuci') cimethod(`cimethod') level(`level') metadta_propci `NDis' `tn', p(`sp') lowerci(`splci') upperci(`spuci') cimethod(`cimethod') level(`level') gen `csp' = 1 - `sp' /*If categorical variable, obtain the sroc and the drawing parameters for each level*/ if "`groupvar'" != "" { qui label list `gvar' local nlevels = r(max) } else { gen `gvar' = 1 local nlevels = 1 } if "`colorpalette'" == "" { local colorpalette "black forest_green cranberry blue sienna orange emerald magenta dknavy gray purple" } else { local kcolors : word count `colorpalette' if (`kcolors' < `nlevels') { di as error "Please specify the colours to be used for all the `nlevels' levels of `gvar'" di as error "colours should be separated by space in the colorpalette() option" exit } } local index 0 local centre local kross local sroc local points local rings local idbubble local cregion local pregion local legendlabel local legendorder /*Options*/ // CI options if `"`ciopt'"' == "" { local ciopt "lpattern(dash)" } else { local forbidden "lcolor" foreach option of local forbidden { if strpos(`"`ciopt'"',"`option'") != 0 { di as error "Option `option'() not allowed in ciopt()" exit } } } // PREDCI options if `"`predciopt'"' == "" { local predciopt "lpattern(-.)" } else { local forbidden "lcolor" foreach option of local forbidden { if strpos(`"`predciopt'"',"`option'") != 0 { di as error "Option `option'() not allowed in predciopt()" exit } } if `"`predciopt'"' != "" & strpos(`"`predciopt'"',"lpattern") == 0{ local predciopt = `"`predciopt' lpattern(-.)"' } } // Overall Point options if `"`opointopt'"' == "" { local opointopt "msymbol(D)" } else { local forbidden "mcolor" foreach option of local forbidden { if strpos(`"`opointopt'"',"`option'") != 0 { di as error "Option `option'() not allowed in opointopt()" exit } } if `"`opointopt'"' != "" & strpos(`"`opointopt'"',"msymbol") == 0{ local opointopt = `"`opointopt' msymbol(D)"' } } // Study point options if `"`spointopt'"' == "" { local spointopt "msymbol(o)" } else { local forbidden "mcolor" foreach option of local forbidden { if strpos(`"`spointopt'"',"`option'") != 0 { di as error "Option `option'() not allowed in spointopt()" exit } } if `"`spointopt'"' != "" & strpos(`"`spointopt'"',"msymbol") == 0{ local spointopt = `"`spointopt' msymbol(o)"' } } // Curve options if `"`curveopt'"' != "" { local forbidden "lcolor" foreach option of local forbidden { if strpos(`"`curveopt'"',"`option'") != 0 { di as error "Option `option'() not allowed in curveopt()" exit } } } // Bubble options if `"`bubopt'"' == "" { local bubopt "msymbol(Oh)" } else { local forbidden "mcolor" foreach option of local forbidden { if strpos(`"`bubopt'"',"`option'") != 0 { di as error "Option `option'() not allowed in bubopt()" exit } } if `"`bubopt'"' != "" & strpos(`"`bubopt'"',"msymbol") == 0{ local bubopt = `"`bubopt' msymbol(Oh)"' } } //Bubble ID options if `"`bidopt'"' == "" { local bidopt "mlabsize(`texts') msymbol(i) mlabel(`rowid')" } else { local forbidden "mcolor mlabcolor " foreach option of local forbidden { if strpos(`"`bidopt'"',"`option'") != 0 { di as error "Option `option'() not allowed in bidopt()" exit } } if `"`bidopt'"' != "" & strpos(`"`bidopt'"',"mlabsize") == 0{ local bidopt = `"`bidopt' mlabsize(`texts')"' } if `"`bidopt'"' != "" & strpos(`"`bidopt'"',"msymbol") == 0{ local bidopt = `"`bidopt' msymbol(i)"' } if `"`bidopt'"' != "" & strpos(`"`bidopt'"',"mlabel") == 0{ local bidopt = `"`bidopt' mlabel(`rowid')"' } } qui { local already 0 if `p' > 1 { local nrows = rowsof(`splogodds') local ovindex = rowsof(`v') } mat `bvari' = (`bvar'[1 ,1], `bvar'[1, 7] \ `bvar'[1, 7], `bvar'[1, 3]) mat `vi' = `v' forvalues j=1/`nlevels' { *Take out the right matrix if "`stratify'" != "" { mat `bvari' = (`bvar'[`j' ,1], `bvar'[`j', 7] \ `bvar'[`j', 7], `bvar'[`j', 3]) mat `vi' = (`v'[`=2*`j' - 1' ,1], `v'[`=2*`j' - 1' ,2] \ `v'[`=2*`j'',1], `v'[`=2*`j'',2]) } local color:word `j' of `colorpalette' qui count if `gvar' == `j' //Centre if r(N) > 1 { if `p' > 1 { local mux`j' = 1 - invlogit(`splogodds'[`nrows', 1]) local muy`j' = invlogit(`selogodds'[`nrows', 1]) } else{ local mux`j' = 1 - invlogit(`splogodds'[`j', 1]) local muy`j' = invlogit(`selogodds'[`j', 1]) } } else { qui summ `sp' if `gvar' == `j' local mux`j' = 1 - r(mean) qui summ `se' if `gvar' == `j' local muy`j' = r(mean) } local centre `"`centre' (scatteri `muy`j'' `mux`j'', mcolor(`color') `opointopt')"' if `nlevels' == 1 { local ++index local legendlabel `"lab(`index' "Summary point") `legendlabel'"' local legendorder `"`index' `legendorder'"' } //Crosses if "`model'" == "fixed" | (r(N) < 3 & "`model'" == "random" ){ if r(N) > 1 { if `p' > 1 { local leftX`j' = 1 - invlogit(`splogodds'[`nrows', 6]) local leftY`j' = invlogit(`selogodds'[`nrows', 1]) local rightX`j' = 1 - invlogit(`splogodds'[`nrows', 5]) local rightY`j' = invlogit(`selogodds'[`nrows', 1]) local topX`j' = 1 - invlogit(`splogodds'[`nrows', 1]) local topY`j' = invlogit(`selogodds'[`nrows', 6]) local bottomX`j' = 1 - invlogit(`splogodds'[`nrows', 1]) local bottomY`j' = invlogit(`selogodds'[`nrows', 5]) } else{ local leftX`j' = 1 - invlogit(`splogodds'[`j', 6]) local leftY`j' = invlogit(`selogodds'[`j', 1]) local rightX`j' = 1 - invlogit(`splogodds'[`j', 5]) local rightY`j' = invlogit(`selogodds'[`j', 1]) local topX`j' = 1 - invlogit(`splogodds'[`j', 1]) local topY`j' = invlogit(`selogodds'[`j', 6]) local bottomX`j' = 1 - invlogit(`splogodds'[`j', 1]) local bottomY`j' = invlogit(`selogodds'[`j', 5]) } } else { qui summ `splci' if `gvar' == `j' local leftX`j' = 1 - r(mean) local leftY`j' = `muy`j'' qui summ `spuci' if `gvar' == `j' local rightX`j' = 1 - r(mean) local rightY`j' = `muy`j'' local topX`j' = `mux`j'' qui summ `selci' if `gvar' == `j' local topY`j' = r(mean) local bottomX`j' = `mux`j'' qui summ `seuci' if `gvar' == `j' local bottomY`j' = r(mean) } local kross `"`kross' (pci `leftY`j'' `leftX`j'' `rightY`j'' `rightX`j'', lcolor(`color') `ciopt') (pci `topY`j'' `topX`j'' `bottomY`j'' `bottomX`j'', lcolor(`color') `ciopt') "' if `nlevels' == 1 { local ++index local legendlabel `"lab(`index' "Confidence intervals") `legendlabel'"' local legendorder `"`index' `legendorder'"' local ++index } } else { //Confidence & prediction ellipses if !`already' { qui set obs 500 local already 1 } qui summ `csp' if `gvar' == `j' local max`j' = min(0.9999, r(min)) local min`j' = max(0.0001, r(max)) local N`j' = r(N) tempvar fpr`j' sp`j' se`j' xcregion`j' ycregion`j' xpregion`j' ypregion`j' range `fpr`j'' `min`j'' `max`j'' gen `sp`j'' = 1 - `fpr`j'' /*HsROC parameters*/ local b = (sqrt(`bvari'[2,2])/sqrt(`bvari'[1,1]))^0.5 local beta = ln(sqrt(`bvari'[2,2]) / sqrt(`bvari'[1,1])) if `p' > 1 { local lambda = `b' * `selogodds'[`nrows', 1] + `splogodds'[`nrows', 1] / `b' local theta = 0.5 * (`b' * `selogodds'[`nrows', 1] - `splogodds'[`nrows', 1] /`b') } else { local lambda = `b' * `selogodds'[`j', 1] + `splogodds'[`j', 1] / `b' local theta = 0.5 * (`b' * `selogodds'[`j', 1] - `splogodds'[`j', 1] /`b') } local var_accu = 2*( sqrt(`bvari'[2,2]*`bvari'[1,1]) + `bvari'[2,1]) local var_thresh = 0.5*( sqrt(`bvari'[2,2]*`bvari'[1,1]) - `bvari'[2,1]) /*The curves*/ if "`curve'" =="" { gen `se`j'' = invlogit(`lambda' * exp(-`beta' / 2) + exp(-`beta') * logit(`fpr`j'')) local sroc "`sroc' (line `se`j'' `fpr`j'', lcolor(`color') `curveopt')" if `nlevels' == 1 { local ++index local legendlabel `"lab(`index' "SROC curve") `legendlabel'"' local legendorder `"`index' `legendorder'"' } } /*Joint confidence region*/ local t = sqrt(2*invF(2, `=`N`j'' - 2', `level'/100)) local nlen = 500 if `p' > 1 { local rho = `vi'[`=2*`nrows'-1', `=2*`nrows'']/sqrt(`vi'[`=`ovindex'-1', `=`ovindex'-1']*`vi'[`=2*`nrows'', `=2*`nrows'']) } else { local rho = `vi'[`j', `=`nlevels' + `j'']/sqrt(`vi'[`j', `j']*`vi'[`=`nlevels' + `j'', `=`nlevels' + `j'']) } if "`stratify'" != "" { local rho = `vi'[1, 2]/sqrt(`vi'[1, 1]*`vi'[2, 2]) } tempvar a range `a' 0 2*_pi `nlen' if "`stratify'" != "" { gen `xcregion`j'' = 1 - invlogit(`splogodds'[`j', 1] + sqrt(`vi'[1, 1]) * `t' * cos(`a' + acos(`rho'))) gen `ycregion`j'' = invlogit(`selogodds'[`j', 1] + sqrt(`vi'[2, 2]) * `t' * cos(`a')) } else { if `p' > 1 { gen `xcregion`j'' = 1 - invlogit(`splogodds'[`nrows', 1] + sqrt(`vi'[`=`ovindex'-1', `=`ovindex'-1']) * `t' * cos(`a' + acos(`rho'))) gen `ycregion`j'' = invlogit(`selogodds'[`nrows', 1] + sqrt(`vi'[`ovindex', `ovindex']) * `t' * cos(`a')) } else { gen `xcregion`j'' = 1 - invlogit(`splogodds'[`j', 1] + sqrt(`vi'[`j', `j']) * `t' * cos(`a' + acos(`rho'))) gen `ycregion`j'' = invlogit(`selogodds'[`j', 1] + sqrt(`vi'[`=`nlevels' + `j'', `=`nlevels' + `j'']) * `t' * cos(`a')) } } local cregion `"`cregion' (line `ycregion`j'' `xcregion`j'', lcolor(`color') `ciopt')"' if `nlevels' == 1 { local ++index local legendlabel `"lab(`index' "Confidence region") `legendlabel'"' local legendorder `"`index' `legendorder'"' } /*Joint prediction region*/ if "`prediction'" == "" { if `p' > 1 { local rho = (`vi'[`=`ovindex'-1', `ovindex'] + `bvari'[1,2])/ sqrt((`vi'[`=`ovindex'-1', `=`ovindex'-1'] + `bvari'[2, 2]) * (`vi'[`ovindex', `ovindex'] + `bvari'[1, 1])) } else { local rho = (`vi'[`=`nlevels' + `j'', `j'] + `bvari'[1,2])/ sqrt((`vi'[`j', `j'] + `bvari'[2, 2]) * (`vi'[`=`nlevels' + `j'', `=`nlevels' + `j''] + `bvari'[1, 1])) } if "`stratify'" != "" { local rho = (`vi'[2, 1] + `bvari'[1,2])/ sqrt((`vi'[1, 1] + `bvari'[2, 2]) * (`vi'[2, 2] + `bvari'[1, 1])) } local d = acos(`rho') if "`stratify'" != "" { gen `xpregion`j'' = 1 - invlogit(`splogodds'[`j', 1] + `t' * sqrt(`v'[1, 1] + `bvari'[2, 2]) * cos(`a' + acos(`rho'))) gen `ypregion`j'' = invlogit(`selogodds'[`j', 1] + sqrt(`vi'[2, 2] + `bvari'[1, 1]) * `t' * cos(`a')) } else { if `p' > 1 { gen `xpregion`j'' = 1 - invlogit(`splogodds'[`nrows', 1] + `t' * sqrt(`vi'[`=`ovindex'-1', `=`ovindex'-1'] + `bvari'[2, 2]) * cos(`a' + acos(`rho'))) gen `ypregion`j'' = invlogit(`selogodds'[`nrows', 1] + sqrt(`vi'[`ovindex', `ovindex'] + `bvari'[1, 1]) * `t' * cos(`a')) } else { gen `xpregion`j'' = 1 - invlogit(`splogodds'[`j', 1] + `t' * sqrt(`v'[`j', `j'] + `bvari'[2, 2]) * cos(`a' + acos(`rho'))) gen `ypregion`j'' = invlogit(`selogodds'[`j', 1] + sqrt(`vi'[`=`nlevels' + `j'', `=`nlevels' + `j''] + `bvari'[1, 1]) * `t' * cos(`a')) } } local pregion `"`pregion' (line `ypregion`j'' `xpregion`j'', `predciopt' lcolor(`color'))"' if `nlevels' == 1 { local ++index local legendlabel `"lab(`index' "Prediction region") `legendlabel'"' local legendorder `"`index' `legendorder'"' } } } if "`summaryonly'" =="" { if "`bubbles'" != "" { //bubbles local rings `"`rings' (scatter `se' `csp' [fweight = `Ni'] if `gvar' == `j', mcolor(`color') `bubopt')"' if "`bubbleid'" != "" { local idbubble `"`idbubble' (scatter `se' `csp' if `gvar' == `j', mcolor(`color') mlabcolor(`color') `bidopt')"' } } else { //points local points `"`points' (scatter `se' `csp' if `gvar' == `j', mcolor(`color') `spointopt')"' } if `nlevels' == 1 { local ++index local legendlabel `"lab(`index' "Observed data") `legendlabel'"' local legendorder `"`index' `legendorder'"' } } if `nlevels' > 1 { local lab:label `gvar' `j' /*label*/ local legendlabel `"lab(`j' "`lab'") `legendlabel'"' local legendorder `"`j' `legendorder'"' } } } if strpos(`"`soptions'"', "legend") == 0 { local legendstr `"legend(order(`legendorder') `legendlabel' cols(1) ring(0) position(6))"' } if strpos(`"`soptions'"', "xscale") == 0 { local soptions `"xscale(range(0 1)) `soptions'"' } if strpos(`"`soptions'"', "yscale") == 0 { local soptions `"yscale(range(0 1)) `soptions'"' } if strpos(`"`soptions'"', "xtitle") == 0 { local soptions `"xtitle("1 - Specificity") `soptions'"' } if strpos(`"`soptions'"', "ytitle") == 0 { local soptions `"ytitle("Sensitivity") `soptions'"' } if strpos(`"`soptions'"', "xlabel") == 0 { local soptions `"xlabel(0(0.2)1) `soptions'"' } if strpos(`"`soptions'"', "ylabel") == 0 { local soptions `"ylabel(0(0.2)1, nogrid) `soptions'"' } if strpos(`"`soptions'"', "graphregion") == 0 { local soptions `"graphregion(color(white)) `soptions'"' } if strpos(`"`soptions'"', "plotregion") == 0 { local soptions `"plotregion(margin(medium)) `soptions'"' } if strpos(`"`soptions'"', "aspectratio") == 0 { local soptions `"aspectratio(1) `soptions'"' } if strpos(`"`soptions'"', "xsize") == 0 { local soptions `"xsize(5) `soptions'"' } if strpos(`"`soptions'"', "ysize") == 0 { local soptions `"ysize(5) `soptions'"' } #delimit ; graph tw `centre' `kross' `sroc' `cregion' `pregion' `points' `rings' `idbubble' , `legendstr' `soptions' name(sroc, replace) ; #delimit cr if "$by_index_" != "" { qui graph dir local gnames = r(list) local gname: word $by_index_ of `gnames' tokenize `gname', parse(".") if "`3'" != "" { local ext =".`3'" } qui graph rename sroc`ext' sroc$by_index_`ext', replace } if `"`graphsave'"' != `""' { di _n noi graph save `graphsave', replace } end