**mrobust version 1.0, January 2014 **An Estimator for Model Robustness **Cristobal Young and Kathy Kroeger //wrapper program to make sure we clean up mata afterwards program mrobust version 11.0 capture noisily mrobust_u `0' local rc = _rc capture mata: mata drop mrobust_varnames exit `rc' end //main program including all of the important subunits of the procedure program mrobust_u, rclass preserve if !replay() { syntax anything [if] [in] /// [, noplot influence normal nozero plotbs intervals /// saveas(passthru) replace *] local timer_1 98 timer clear `timer_1' timer on `timer_1' tempfile temp_model_results //If the input does not already contain a comma and options then add a comma if !strmatch(`"`0'"', "*,*") local 0 `"`0',"' MRobust `0' results_tempfile(`"`temp_model_results'"') // load file with bootstrap results and display output capture use `"`temp_model_results'"', clear if c(rc) { if inrange(c(rc),900,903) { di _newline as err /// "insufficient memory to load file with mrobust results" } error c(rc) } CalcSummaryStats, `saveas' `replace' timer off `timer_1' quietly timer list `timer_1' local time_taken = r(t`timer_1') local sig_only `"`r(sig_only)'"' DisplayResults, full `sig_only' `influence' `intervals' time_taken(`time_taken') if "`sig_only'" == "sig_only" local plot noplot if "`plot'" != "noplot" PlotMRobust , `rmal' `zero' `plotbs' bs(`r(bs_type)') } //replaying results else { syntax [, more full sig_only intervals nosig] if "`sig'" == "nosig" local full full if `"`r(cmd)'"' != "mrobust" { di as err "To replay mrobust results, the last command must be a successful run of " as result "mrobust" as err "." exit 198 } local varlist `"`r(varlist)'"' ParseVarlist `varlist', display_only local time_taken = `r(time_taken)' DisplayResults, `more' `full' `sig_only' `intervals' time_taken(`time_taken') } return local sig_only `"`sig_only'"' //return local saveas `"`saveas'"' return add restore end /* Parse the user input, estimate all of the possible models, store the results in the results_tempfile. */ program MRobust, rclass syntax anything (name = model_and_varlist id = "varlist") [if] [in] /// [, fe re be mle pa irr offset(passthru) exposure(passthru) absorb(passthru) vce(passthru) /// noplot plotbs savelist(string) saveas(string) alpha(passthru) /// sample(passthru) size(passthru) bs(passthru) pref(passthru) /// estonly compare normal nozero nolistwise /// other(passthru) /// noinfluence replace sig_only nosig bs_reps(integer 50) results_tempfile(string) intervals ] //set up data file to store estimation results tempname memhold tempfile temp_nonpar_bs_results local results `"`results_tempfile'"' gettoken model_namelist varlist : model_and_varlist, bind match(par) ParseModels `model_namelist' local nmodeltypes `"`r(nmodeltypes)'"' local model_namelist_uniq `"`r(model_namelist_uniq)'"' local model_idlist `"`r(model_idlist)'"' local model_namelist `"`r(model_namelist)'"' forvalues i = 1/`nmodeltypes' { local model`i'_name `"`r(model`i'_name)'"' local model`i'_opts `"`r(model`i'_opts)'"' local model`i'_id `"`r(model`i'_id)'"' } return local model `model_namelist' return local model_idlist `model_idlist' return local varlist `varlist' return scalar nmodeltypes = `nmodeltypes' //If the user has not specified a sig_only option then assign it based on the model choices if "`sig_only'" == "" & "`sig'" != "nosig" local sig_only `"`r(sig_only_models)'"' //Otherwise it will stay as "sig_only" or not as the user specified ParseVarlist `varlist' local nSets `"`r(nSets)'"' local n_var_combinations `"`r(n_var_combinations)'"' local allvarnames `"`r(allvarnames)'"' local allvarnames_clean `"`r(allvarnames_clean)'"' local b_always_in_clean `"`r(b_always_in_clean)'"' local always_in_varnames `"`r(always_in_varnames)'"' local intvarlist `"`r(intvarlist)'"' local depvarlist `"`r(depvarlist)'"' local intvarlist_noline = subinstr("`intvarlist'", "|"," ",.) local depvarlist_noline = subinstr("`depvarlist'", "|", " ", .) forvalues is = 1/`nSets' { local s`is'NSX `"`r(s`is'NSX)'"' forvalues isx = 1/`s`is'NSX' { local s`is'sx`isx'NT = `"`r(s`is'sx`isx'NT)'"' forvalues it = 1/`s`is'sx`isx'NT' { local s`is'sx`isx't`it'NTX = `"`r(s`is'sx`isx't`it'NTX)'"' forvalues itx = 1/`s`is'sx`isx't`it'NTX' { local s`is'sx`isx't`it'tx`itx' `"`r(s`is'sx`isx't`it'tx`itx')'"' } } } } if "`sig_only'" == "" { local nwarnings 0 local lists_to_check intvarlist depvarlist foreach list of local lists_to_check { if wordcount("``list'_noline'") > 1 { local list_with_commas = subinstr("``list''", "|",", ",.) local ++nwarnings di as text " Warning: Check that " as result `"`list_with_commas'"' as text " are on a comparable scale, " di as text " so that the meaning of a one unit change is consistent." } } if `nwarnings' > 0 di as text " If not, consider standardizing the variables or use the " as result "sig_only" as text " option." } ParseEstimationOptions `model_namelist_uniq' , `fe' `re' `be' `mle' `pa' `irr' /// `vce' `offset' `exposure' `absorb' `other' local opts_command `"`r(opts_command)'"' ParseMrobustOptions, nSets(`nSets') `sample' `size' `bs' `pref' `alpha' local threshold `"`r(threshold)'"' local sizemin `"`r(sizemin)'"' local sizemax `"`r(sizemax)'"' local bs_type `"`r(bs_type)'"' if "`intervals'" == "intervals" & "`bs_type'" == "" local bs_type par local bs_options_user `"`r(bs_options_user)'"' local bs_opts_all `"`r(bs_opts_all)'"' local prefb `"`r(prefb)'"' local prefse `"`r(prefse)'"' local alpha `"`r(alpha)'"' local opts_mrobust `"`r(opts_mrobust)'"' //identify observations meeting "if" and "in" criteria tempvar touse mark `touse' `if' `in' quietly count if `touse' local total_N = r(N) if `total_N' == 0 { error 2000 } /*Listwise deletion: mark out observations with any missing values. Alert the user to how many observations have been marked out */ if "`listwise'" != "nolistwise" { markout `touse' `allvarnames' quietly count if `touse' local N = r(N) if `N' == 0 { error 2000 } local nmissing = `total_N' - `N' if (`nmissing' > 0) { di as text "note: sample size varies across model specifications." di "Listwise deletion: " `nmissing' as text " out of " `total_N' as text " observations will not be used." } } else local N = `total_N' /*check on variables of interest for xtreg Fixed Effects*/ forvalues im = 1/`nmodeltypes' { local fe fe local estimation_options `"`model`im'_opts' `opts_command'"' if "`model`im'_name'" == "xtreg" & `: list fe in estimation_options' == 1 { foreach intvar of local intvarlist_noline { quietly xtsum `intvar' if `touse' if r(sd_w)==0 { di as err "Variable of interest `intvar0' has 0 within standard deviation." exit 198 } } } } //save the user's dataset as a tempfile with touse tempfile orig_touse quietly save `orig_touse' local nrotated = `nSets' - 2 local bs_reps = 50 /* Estimate the time required for the mrobust command by timing 3 regressions, averaging the time required and multiplying by the number of models. */ local timer_2 99 local timesum 0 local ntests 5 local time_multiplier 1.3 local curr_varlist "" forvalues is = 1/`nSets' { forvalues it = 1/`s`is'sx1NT' { local curr_varlist `"`curr_varlist' `s`is'sx1t`it'tx1'"' } } forvalues im = 1/`nmodeltypes' { local model `"`model`im'_name'"' forvalues iv = 1/`ntests' { timer clear `timer_2' timer on `timer_2' if "`bs_type'" == "nonpar" quietly bootstrap `bs_opts_all' : /// `model' `curr_varlist' if `touse', `opts_command' `opts`im'' else { quietly `model' `curr_varlist' if `touse', `opts_command' `opts`im'' forvalues ib = 1/`bs_reps' { local x = rnormal(0,1) } } timer off `timer_2' quietly timer list `timer_2' local timesum = `timesum' + r(t`timer_2') } } local time_avgreg = `time_multiplier'*`timesum'/(`ntests'*`nmodeltypes') di _newline _continue local nmodels = `n_var_combinations' * `nmodeltypes' local large_nmodels 0 local large_threshold 1000 /*Publish number of models to calculate and calculate estimated time. This is relevant only when the size option is not specified because have not figured out how to calculate the number of models of each size taking into account all of the potential for either/or sets. However, it could be useful to publish an estimate not taking into account either/or, just as an estimate, would definitely be closer than the full model space and more useful than no output? */ local extra_digits 3 local two_minutes 120 local sec_per_min 60 local sec_per_hr 3600 local sample_pct = `threshold' local dot_interval 1000 if "`size'" == "" { if `sample_pct' == 0 { local ndigits = ceil(log10(`nmodels')) + `extra_digits' di "Calculating " %-`ndigits'.0fc `nmodels' "models..." local time_est = `time_avgreg' * `nmodels' if `nmodels' > `large_threshold' local large_nmodels 1 } else { local nmodels_est = `nmodels'*`sample_pct' local ndigits = ceil(log10(`nmodels_est')) + `extra_digits' local time_est = `time_avgreg' * `nmodels_est' local nmodels_est = round(`nmodels_est') di "Calculating approximately " %-`ndigits'.0fc `nmodels_est' "models..." if `nmodels_est' > `large_threshold' local large_nmodels 1 } if `time_est' > `two_minutes' { local timeunit min local ndigits = ceil(log10(`time_est'/`sec_per_min')) + `extra_digits' di "Estimated time is " %-`ndigits'.0fc round(`time_est'/`sec_per_min', 1) " minutes (" round(`time_est'/`sec_per_hr', .1) " hours)." } else { local timeunit sec di "Estimated time is " round(`time_est', 1) " seconds (" round(`time_est'/`sec_per_min', .1) " minutes)." } } if `large_nmodels' == 1 di "Each dot represents `dot_interval' models calculated" local postnames `allvarnames_clean' r2 df b_intvar `b_always_in_clean' sig pos /// variance pvalue odds_ratio_ind str40 (depvar intvar model opts) i_model i_bs if "`bs_type'" != "" local postnames `"`postnames' b_bs sig_bs pos_bs "' quietly postfile `memhold' `postnames' using `results', replace //save the list of control term combinations in a text file if "`savelist'" != "" { quietly file open listfile using `savelist', write replace file write listfile "mrobust `model' `varlist'" file write listfile _newline "generated control term combinations:" } //indicator vector for which sets are present in the model, sets 1 and 2 (depvar and intvar) always in matrix curr_Sets_ind = J(1, `nSets', 0) local Sets_always_in 1 2 foreach Set of local Sets_always_in { matrix curr_Sets_ind[1, `Set'] = 1 } local first_rotated_set 3 local nrounds = 2^(`nSets'-2) local nmodels_actual 0 //Main loop forvalues i_model_type = 1/`nmodeltypes' { local model `model`i_model_type'_name' local model_id `model`i_model_type'_id' local opts_touse `"`opts_command' `model`i_model_type'_opts'"' local odds_ratio_ind 0 local irr irr if "`model'" == "logistic" | `: list irr in opts_touse' local odds_ratio_ind 1 forvalues i_round = 1/`nrounds' { /* Select the Sets present in this series of models by iterating through the binary numbers in the vector curr_Sets_ind */ forvalues i = `first_rotated_set'/`nSets' { if curr_Sets_ind[1, `i'] == 0 { matrix curr_Sets_ind[1, `i'] = 1 continue, break } else { matrix curr_Sets_ind[1, `i'] = 0 } } local curr_n_control_terms = 1 local curr_Setlist "`Sets_always_in'" //Track the number of SetX within each of the included sets local NSX_by_Set "`s1NSX' `s2NSX'" forvalues i_Set = `first_rotated_set'/`nSets' { if curr_Sets_ind[1, `i_Set'] == 1 { local ++curr_n_control_terms local curr_Setlist `"`curr_Setlist' `i_Set'"' local NSX_by_Set `NSX_by_Set' `s`i_Set'NSX' } } if `curr_n_control_terms' >= `sizemin' & `curr_n_control_terms' <= `sizemax' { local curr_n_sets = `curr_n_control_terms' + 1 /* Subroutine to generate the list of all possible combinations of either/or subsets of the included sets.*/ GenerateSetXList, nSX_by_Set(`NSX_by_Set') setXlist("") count_Xlists(0) /// setlist(`curr_Setlist') unit(Set) curr_length(0) local count_Xlists = r(count_Xlists) forvalues i = 1/`count_Xlists' { local SetXlist`i' = r(SetXlist`i') } clear results /* Iterate through each of the SetXlists and generate the list of all the terms of each of the included SetX */ forvalues i_SetXlist = 1/`count_Xlists' { local curr_Termlist "" forvalues i_Set = 1/`curr_n_sets' { local Set = word("`curr_Setlist'", `i_Set') local SetX = word("`SetXlist`i_SetXlist''", `i_Set') forvalues i_Term = 1/`s`Set'sx`SetX'NT' { local curr_Termlist "`curr_Termlist' s`Set'sx`SetX't`i_Term'" } } //track the number of Termx within each of the included Terms local NTX_by_Term "" foreach term of local curr_Termlist { local NTX_by_Term "`NTX_by_Term' ``term'NTX'" } /*Generate the list of all possible combinations of either/or variables of the included Terms */ GenerateSetXList, curr_length(0) setlist(`curr_Termlist') /// nSX_by_Set(`NTX_by_Term') setXlist("") count_Xlists(0) unit(Term) local count_TermXlists = r(count_Xlists) forvalues i = 1/`count_TermXlists' { local TermXlist`i' = r(TermXlist`i') } clear results forvalues i_TermXlist = 1/`count_TermXlists' { //Sample barrier local PassedSampleThreshold 0 if `threshold' == 0 local PassedSampleThreshold 1 else { local r = runiform() if `r' <= `threshold' local PassedSampleThreshold 1 } if `PassedSampleThreshold' == 1 { local curr_varlist "" foreach TermX of local TermXlist`i_TermXlist' { local curr_varlist "`curr_varlist' ``TermX''" } local ++nmodels_actual /* Create indicator to denote included variables in the dataset to post in the saved results file */ local curr_varlist_ind "" foreach varname of local allvarnames { local IsIncludedVariable : list varname & curr_varlist if "`IsIncludedVariable'" != "" { local curr_varlist_ind `curr_varlist_ind' (1) } else local curr_varlist_ind `curr_varlist_ind' (0) } if "`savelist'" != "" file write listfile _n "`curr_varlist'" local depvar = word("`curr_varlist'", 1) local intvar = word("`curr_varlist'", 2) //Estimate the model if "`bs_type'" == "nonpar" quietly bootstrap `bs_opts_all' /// saving(`temp_nonpar_bs_results', replace): /// `model' `curr_varlist' if `touse', `opts_touse' else quietly `model' `curr_varlist' if `touse', `opts_touse' //Save the results if "`model'" == "xtreg" local e_r2 = e(r2_o) else if inlist("`model'", "reg", "areg", "rreg") local e_r2 = e(r2) else local e_r2 = e(r2_p) local b = _b[`intvar'] local always_in_estimates "" foreach var of local always_in_varnames { capture local b_var = _b[`var'] if _rc == 0 local always_in_estimates `always_in_estimates' (`b_var') else local always_in_estimates `always_in_estimates' (.) } local variance = (_se[`intvar'])^2 if inlist("`e(model)'", "ols", "fe") & "`bs_type'" != "nonpar" /// local pval = (2*ttail(e(df_r), abs(_b[`intvar']/_se[`intvar']))) else local pval = 2*(1-normal(abs(_b[`intvar']/_se[`intvar']))) local sig = (`pval' <= `alpha') local pos = (`b' > 0) local topost `curr_varlist_ind' (`e_r2') (e(df_m)) (`b') /// `always_in_estimates' /// (`sig') (`pos') (`variance') (`pval') (`odds_ratio_ind') /// ("`depvar'") ("`intvar'") ("`model_id'") /// ("`model`i_model_type'_opts'") (`nmodels_actual') /* If user specified nonparametric bootstrap then open the saved results from the bootstrap routine and store them in the model results file */ if "`bs_type'" == "nonpar" { capture use `temp_nonpar_bs_results', clear if inlist("`model'", "probit", "logit", "logistic", "poisson") /// local _bvar `depvar'_b_`intvar' else local _bvar _b_`intvar' local n_bsreps = _N forvalues i_bs = 1/`n_bsreps' { if 2*(1-normal(abs(`_bvar'[`i_bs']/_se[`intvar']))) <= `alpha' /// local sig_bs 1 else local sig_bs 0 local pos_bs = ( `_bvar'[`i_bs'] > 0 ) post `memhold' `topost' (`i_bs') (`_bvar'[`i_bs']) (`sig_bs') (`pos_bs') } capture use `orig_touse', clear } /* Otherwise resample the coefficients with parametric bootstrap according to the normal distribution */ else if "`bs_type'" == "par"{ forvalues i_bs = 1/`bs_reps' { local bs_sample = rnormal(_b[`intvar'], _se[`intvar']) local pos_bs = ( `bs_sample' > 0 ) if inlist("`e(model)'", "ols", "fe") { if (2*ttail(e(df_r), abs(`bs_sample'/_se[`intvar']))) <= `alpha' /// local sig_bs 1 else local sig_bs 0 } else if 2*(1-normal(abs(`bs_sample'/_se[`intvar']))) <= `alpha' /// local sig_bs 1 else local sig_bs 0 post `memhold' `topost' (`i_bs') (`bs_sample') (`sig_bs') (`pos_bs') } } else post `memhold' `topost' (1) if `large_nmodels' == 1 { if mod(`nmodels_actual', `dot_interval') == 0 { di _continue "." } } } } } } } } postclose `memhold' if "`savelist'" != "" quietly file close listfile /*calculate multicollinearity of var of interest if there are >1 options for intvar, take the mean of the MCs*/ local mcsum = 0 local nintvar = wordcount("`intvarlist_noline'") local xvars : list allvarnames - depvarlist_noline local xvars : list xvars - intvarlist_noline foreach intvar of local intvarlist_noline { quietly reg `intvar' `xvars' if `touse' local mcsum = `mcsum' + e(r2) } return scalar mc = `mcsum'/`nintvar' /*calculate the SD of the variable of interest if there are >1 options for intvar, take the mean of the SDs */ local sd2intvar 0 foreach intvar of local intvarlist_noline { quietly summarize `intvar' if `touse' local sd2intvar = `sd2intvar' + (r(sd)^2) } return scalar sdintvar = sqrt(`sd2intvar') return local bs_type `"`bs_type'"' return local bs_opts `"`bs_options_user'"' return scalar bs_reps = `bs_reps' return scalar alpha = `alpha' if "`pref'" != "" { return scalar prefb = `prefb' return scalar prefse = `prefse' } return local sig_only "`sig_only'" return local savelist "`savelist'" return local opts_command `"`opts_command'"' return local opts_mrobust `"`opts_mrobust'"' forvalues im = 1/`nmodeltypes' { return local model`im'_name "`model`im'_name'" return local model`im'_opts "`model`im'_opts'" return local model`im'_id "`model`im'_id'" } return local intvar `"`intvarlist'"' return local depvar `"`depvarlist'"' return local model `"`model_namelist'"' return local cmd mrobust return scalar nmodels = `nmodels_actual' return scalar nterms = `nrotated' return scalar nvars = wordcount("`allvarnames'") - wordcount("`depvarlist_noline'") return scalar N = `N' end /* ParseModels function takes in the user's list of estimation commands and returns the following local macros: sig_only_models ("sig_only" if the user has specified two or more model types whose coefficient magnitudes are not directly comparable, "" else) nmodeltypes (number of different estimation commands specified) model_namelist (list of estimation commands) model_namelist_uniq (list of unique estimation commands) model_idlist (list of estimation commands, with repeated commands numbered) for each model in the user's input list: model`i'_name, model`i'_opts,model`i'_id */ program ParseModels, rclass syntax anything(name = model_list_input) local model_list_input = subinstr("`model_list_input'", "[", "(" , .) local model_list_input = subinstr("`model_list_input'", "]", ")" , .) local sig_only_list logit logistic probit nbreg poisson local allowed reg logit logistic probit poisson nbreg areg rreg xtreg local model_list_to_return "" //i counts the number of distinct model types local i 0 while `"`:list retok model_list_input'"' != "" { gettoken model_name model_list_input : model_list_input , bind parse(" |()") if !inlist("`model_name'","|","(",")") { if "`model_name'" == "regress" local model_name reg if `:list model_name in allowed' != 1 { di as err "Invalid model type. Model options are reg, logit, logistic, probit, poisson, nbreg, areg, rreg, xtreg." exit 198 } if `:list model_name in sig_only_list' == 1 { local sig_only sig_only } local ++i local model`i'_name `model_name' local model_list_to_return `model_list_to_return' `model_name' gettoken opts model_list_input : model_list_input, bind match(par) parse(" |") if "`par'" == "" { local model_list_input "`options' `model_list_input'" } else local model`i'_opts `opts' } } local model_list_to_return : list retok model_list_to_return local dups_model_namelist : list dups model_list_to_return foreach d of local dups_model_namelist { local n_repeats_`d' 0 } local model_idlist "" forvalues j = 1/`i' { local name `model`j'_name' if `:list name in dups_model_namelist' == 1 { local ++ n_repeats_`name' local model`j'_id `name'_`n_repeats_`name'' } else local model`j'_id `name' local model_idlist `"`model_idlist' `model`j'_id'"' } if `i' == 1 local sig_only "" return local sig_only_models "`sig_only'" return local nmodeltypes = `i' return local model_namelist_uniq : list uniq model_list_to_return return local model_idlist `model_idlist' return local model_namelist `model_list_to_return' forvalues j = 1/`i' { return local model`j'_name `model`j'_name' return local model`j'_opts `model`j'_opts' return local model`j'_id `model`j'_id' } end /* Parse the options that will be passed into the Stata command for every model */ program ParseEstimationOptions, rclass syntax anything(name = model_namelist_uniq) [, fe re be mle pa vce(string) /// offset(varname) exposure(varname) irr absorb(varname) other(string)] local opts_command "" local xtreg_option "`fe' `re' `be' `mle' `pa'" local xtreg_option : list retok xtreg_option if "`xtreg_option'" != "" { if "`model_namelist_uniq'" != "xtreg" { di as err "Invalid `xtreg_option'. `xtreg_option' only allowed with xtreg." exit 198 } } local absorb_option "" if "`model_namelist_uniq'" == "areg" { if ("`absorb'" == "") { di as err "option absorb() required" exit 198 } else { local absorb_option "absorb(`absorb')" } } else if ("`absorb'" != "") { di as err "Invalid absorb option. Absorb may only be used with areg." exit 198 } local est_options irr offset exposure local est_options_models_ok poisson nbreg foreach opt of local est_options { if "``opt''" != "" { if `: list model_namelist_uniq in est_options_models_ok' != 1 { di as err "Invalid `opt' option. `opt' should only be used " /// "with poisson or negative binomial regression." exit 198 } else if "`opt'" != "irr" local `opt'_option "`opt(``opt'')'" else local `opt'_option "`opt'" } } local vce_option "" if ("`vce'" != "") { if !regexm("`vce'","robust|cluster*") { di as err "Invalid vce option. Vce options are robust and cluster." exit 198 } local rreg rreg if `: list rreg in model_namelist_uniq' == 1 { di as err "option vce() not allowed with rreg." exit 198 } local vce_option "vce(`vce')" } local opts_command `xtreg_option' `absorb_option' `irr' `exposure_option' /// `offset_option' `vce_option' `other' local opts_command : list retok opts_command return local opts_command `"`opts_command'"' end /* Parse the options that are specific to mrobust functionality and will not be passed into the actual commands. */ program ParseMrobustOptions, rclass syntax [, nSets(integer 0) sample(integer 0) /// size(numlist integer >=0 missingokay max=2 min=1) /// alpha(real .05) bs(string) pref(numlist max=2 min=2) ] local opts_mrobust "" local sample_option "" if `sample' < 0 | `sample' > 100 { di as err "Sample percent should be an integer 1-99." exit 198 } if `sample' == 0 { local threshold = 1 } else { local threshold = `sample'/100 local sample_option sample(`sample') } local size_opt_optput "" if "`size'" == "" { local sizemin = 1 local sizemax = `nSets' } else { gettoken sizemin sizemax : size, parse(" ,") if trim("`sizemin'") == "." local sizemin = 1 if `sizemin' == 0 local sizemin = 1 if trim("`sizemax'") == "." local sizemax = `nSets' if "`sizemax'" == "" local sizemax = `sizemin' if `sizemax' > `nSets' local sizemax = `nSets' if (`sizemin' > `sizemax') | (`sizemin' >= `nSets') { di as err "invalid size option" exit 198 } if `sizemin' == 1 & `sizemax' == 1 { di as err "invalid size option" exit 198 } local size_opt_output size(`sizemin', `sizemax') } local alpha_opt_output "" if `alpha' < 0 | `alpha' >= 1 { di "note: invalid alpha. Default alpha = .05 will be used." local alpha = .05 } else if `alpha' != .05 local alpha_opt_output alpha(`alpha') local bs_types_allowed par nonpar local bs_options_default nodots local bs_options_user "" local bs_type "" local bs_opts_all "" local bs_opt_output "" if "`bs'" != "" { gettoken bs_type bs_options_user : bs, parse(" ,") if `: list bs_type in bs_types_allowed' != 1 { di as err "Allowed bs types are " as result "par" as text " or " as result "nonpar." exit 198 } if "`bs_type'" == "nonpar" { local bs_options_user `",`bs_options_user'"' if strmatch("`bs_options_user'", "saving(") { di as err "Bootstrap saving option not allowed." exit 198 } local bs_opt_output "bs(`bs')" local bs_opts_all : list bs_options_user | bs_options_default } else if "`bs_options_user'" != "" { di as err "Bootstrap options not allowed with parametric bootstrap." exit 198 } else local bs_opt_output "bs(`bs_type')" } if "`pref'" != "" { gettoken prefb prefse : pref if wordcount("`prefse'") != 1 { di as err "Invalid preferred option. Please enter one coefficient estimate and one standard error estimate." exit 198 } } return local threshold = `sample'/100 return local sizemin = `sizemin' return local sizemax = `sizemax' return local bs_type `"`bs_type'"' return local bs_options_user `"`bs_options_user'"' return local bs_opts_all `"`bs_opts_all'"' return local prefb `"`prefb'"' return local prefse `"`prefse'"' return local alpha = `alpha' local opts_mrobust `"`sample_option' `size_opt_output' `bs_opt_output' `alpha_opt_output'"' return local opts_mrobust : list retok opts_mrobust end /* Parse out the list of control terms. The user can enter 2 layers of grouped variables including either/or options. The outermost layer of grouped terms is called a Set. ex. Set 1 = ( (x1 (x2 | x3 )) | (x4 x5 ) ) Sets are split up into SetX by the either/or symbol "|". Only one SetX of each Set may appear in any given model. ex. Set 1, SetX 1 = (x1 (x2 | x3 )) Set 1, SetX 2 = (x4 x5 ) SetX are split up into Terms by parentheses. Each SetX contains one or more Terms. Given that a SetX appears in the model, all of the terms of the SetX must appear in that model. ex. Set 1, SetX1, Term 1 = x1 Set 1, SetX1, Term 2 = x2 | x3 Set 1, SetX2, Term 1 = x4 x5 Terms are split up into TermX by the either/or symbol "|". Only one TermX of each Term may appear in any given model. ex. Set 1, SetX1, Term 1, TermX 1 = x1 ex. Set 1, SetX1, Term 2, TermX 1 = x2 Set 1, SetX1, Term 2, TermX 1 = x3 Set 1, SetX2, Term 1, TermX 1 = x4 x5 ParseVarlist returns the following local macros: s`i'NSX = number of SetX in Set i s`i'sx`j'NT = number of Terms in Set i, SetX j s`i'sx`j't`k'NTX = number of TermX in Set i, SetX j, Term k s`i'sx`j't`k'tx`m' = the variable names that compose Set i, SetX j, Term k, TermX m nSets = total number of Sets (including dependent var set (#1) and interest var set (#2)) n_var_combinations = total number of variable combinations meeting the user's grouping and either/or criteria allvarnames = list of all of the variable names allvarnames_clean = list of all the variable names, with dependent and interest variables preceded by "__" and rotated variable names preceded by "r_" any extraneous characters e.g. "." converted to "_" always_in_varnames = list of all the always in variable names b_always_in_clean = list of all the always in variable names precede by "b_" with any extraneous characters e.g. "." converted to "_" intvarlist = variable(s) of interest name(s), separated by | depvarlist = dependent variable(s) name(s), separated by | If the display_only option is specified, ParseVarlist returns nothing. */ program ParseVarlist, rclass return add syntax anything (name = varlist_input) [ , display_only] local nSets 0 local depvarlist "" local intvarlist "" local allvarnames "" local allvarnames_clean "" local always_in_varnames "" local b_always_in_clean "" mata: mrobust_varnames = asarray_create() while `"`:list retok varlist_input'"' != "" { //Isolate the first grouped set of the varlist gettoken set varlist_input : varlist_input, bind match(par) if "`par'" == "" { if !strmatch("`set'", "*.*") unab set : `set' if `:word count `set'' != 1 { gettoken set rest : set local varlist_input `"`rest' `varlist_input'"' } } local ++nSets local nsx 1 while `"`:list retok set'"' != "" { //Build up setx by getting tokens until you reach | or the end of the set local setx "" gettoken setx0 set : set, bind parse(" |") while `"`setx0'"' != "|" & `"`setx0'"' != "" { local setx `"`setx' `setx0'"' gettoken setx0 set : set, bind parse(" |") } local nt 0 while `"`:list retok setx'"' != "" { gettoken term setx : setx, bind match(par) local ++nt local ntx 1 while `"`:list retok term'"' != "" { //There should be no more parentheses within the term so no need for bind option gettoken termx0 term : term, parse("|") if "`termx0'" == "|" { local ++ntx } else { local termx "" //Build up termx by getting tokens until the end of the term while `"`:list retok termx0'"' != "" { gettoken tk termx0 : termx0 if !strmatch("`tk'", "*.*") unab tk : `tk' local termx "`termx' `tk'" } //Build the depvarlist: Set #1, every setx, Term #1, Word 1 if `nSets' == 1 { if `nt' == 1 { local depvar0 = word("`termx'", 1) CheckIfSingleVariable `depvar0' if "`r(IsSingleVariable)'" == "false" { di as err "dependent variable must be a single variable" exit 198 } if "`depvarlist'" != "" local depvarlist "`depvarlist' | `depvar0'" else local depvarlist `depvar0' } //Each setx of Set 1 should contain only 1 term = 1 dependent variable. else { di as err "depvar must be a single variable." exit 198 } } //Build the intvarlist : Set #2, every setx, Term #1, Word 1 if `nSets' == 2 & `nt' == 1 { local intvar0 = word("`termx'", 1) CheckIfSingleVariable `intvar0' if "`r(IsSingleVariable)'" == "false" { di as err "variable of interest must be a single variable" exit 198 } if "`intvarlist'" != "" local intvarlist "`intvarlist' | `intvar0'" else local intvarlist `intvar0' } local s`nSets'sx`nsx't`nt'tx`ntx' `termx' local allvarnames "`allvarnames' `termx'" foreach varname of local termx { local varname_clean = strtoname("`varname'") if `nSets' == 2 { local always_in_varnames `"`always_in_varnames' `varname'"' local b_always_in_clean `"`b_always_in_clean' b_`varname_clean'"' } if `nSets' > 2 local varname_clean = `"r_`varname_clean'"' else local varname_clean `"__`varname_clean'"' local allvarnames_clean `"`allvarnames_clean' `varname_clean'"' mata: asarray(mrobust_varnames, "`varname_clean'", "`varname'") } } } //done parsing term: Record # of termx local s`nSets'sx`nsx't`nt'NTX `ntx' } //done parsing setx: Record # of term local s`nSets'sx`nsx'NT `nt' if "`setx0'" == "|" { local ++nsx } } //done parsing set: Record # of setx local s`nSets'NSX `nsx' } if "`display_only'" != "display_only" { forvalues is = 1/`nSets' { return local s`is'NSX = `s`is'NSX' forvalues isx = 1/`s`is'NSX' { return local s`is'sx`isx'NT = `s`is'sx`isx'NT' forvalues it = 1/`s`is'sx`isx'NT' { return local s`is'sx`isx't`it'NTX = `s`is'sx`isx't`it'NTX' forvalues itx = 1/`s`is'sx`isx't`it'NTX' { return local s`is'sx`isx't`it'tx`itx' `"`s`is'sx`isx't`it'tx`itx''"' //local TermX_varnames "`s`is'sx`isx't`it'tx`itx''" //local allvarnames "`allvarnames' `TermX_varnames'" } } } } local duplicates : list dups allvarnames if "`duplicates'" != "" { di as err "Duplicate variables: `duplicates'" di as err "Please enter each independent variable only once." exit 198 } local n_var_combinations 1 local nmodels 1 forvalues is = 1/`nSets' { local n_combos_this_Set 0 forvalues isx = 1/`s`is'NSX' { local n_combos_this_SetX 1 forvalues nt = 1/`s`is'sx`isx'NT' { local n_combos_this_SetX = `n_combos_this_SetX' * `s`is'sx`isx't`nt'NTX' } local n_combos_this_Set = `n_combos_this_Set' + `n_combos_this_SetX' } if `is' > 2 local ++n_combos_this_Set local n_var_combinations = `n_var_combinations' * `n_combos_this_Set' } return local nSets `nSets' return local n_var_combinations `n_var_combinations' return local allvarnames `"`allvarnames'"' return local allvarnames_clean `"`allvarnames_clean'"' return local b_always_in_clean `"`b_always_in_clean'"' return local always_in_varnames `"`always_in_varnames'"' return local intvarlist `"`intvarlist'"' return local depvarlist `"`depvarlist'"' } end /* Check the input of time-series operators to determine whether the name indicates one or more variables */ program CheckIfSingleVariable, rclass return add if strmatch("`0'", "*.*") & regexm("`0'","^([LlDdSsFf]+[0-9LlDdSsFf]*\.[^.]+)$") == 0 { return local IsSingleVariable false } else return local IsSingleVariable true end /* Recursive branching subroutine to generate the list of all possible combinations of of either/or SetX units of the included Sets. We want exactly one SetX for each of the included n Sets. The base case is we have assembled a SetXlist to return containing n SetX ( 1 SetX for each Set ). If the assembled SetXlist contains i SetX, i < n, then for each of the SetX j in the next Set i+1, we add j to the SetXlist and pass this incremented SetXList of length i+1 back into GenerateSetXList. Arguments: curr_length length of current SetXList to return Setlist is the list of Sets included NSX_by_set lists the number of setx in each of the included sets SetXlist lists the current assembled setxlist to return count_SetXlist holds the total number of generated setxlists so far unit indicates whether we are dealing with Sets or Terms Return values r(count_Xlists) is the total number of distinct SetXlists generated for values i = 1 to r(count_Xlists), r(SetXlist`i') is a string indicating the components of the ith SetXlist */ //program GenerateUnitList program GenerateSetXList, rclass syntax [, setlist(string) nSX_by_Set(numlist) setXlist(string) count_Xlists(integer 0) unit(string) curr_length(integer 0) ] if `curr_length' == wordcount("`setlist'") { local ++count_Xlists return local count_Xlists `count_Xlists' return local `unit'Xlist`count_Xlists' `setXlist' } else { local ++curr_length local curr_set = word("`setlist'", `curr_length') local nSX_this_set = word("`nSX_by_Set'", `curr_length') forvalues i = 1/`nSX_this_set' { if "`unit'" == "Set" local curr_SetXlist "`setXlist' `i'" else local curr_SetXlist "`setXlist' `curr_set'tx`i'" local count = r(count_Xlists) if "`count'" == "." local count = 0 GenerateSetXList, curr_length(`curr_length') setlist("`setlist'") nSX_by_Set(`nSX_by_Set') setXlist(`curr_SetXlist') count_Xlists(`count') unit(`unit') foreach mac in `:r(macros)' { return local `mac' `=r(`mac')' } } } end /* Use the saved results file to calculate the summary statistics. */ program CalcSummaryStats, rclass syntax [, saveas(string) replace] local model_namelist `"`r(model)'"' local model_idlist `"`r(model_idlist)'"' local nmodeltypes = wordcount("`model_namelist'") local depvarlist `"`r(depvar)'"' local intvarlist `"`r(intvar)'"' local intvarlist_noline = subinstr("`intvarlist'", "|"," ",.) local nintvar = wordcount("`intvarlist_noline'") local depvarlist_noline = subinstr("`depvarlist'", "|", " ", .) local ndepvar = wordcount("`depvarlist_noline'") local bs_type `"`r(bs_type)'"' local bs_opts `"`r(bs_opts)'"' local nterms `"`r(nterms)'"' local N `"`r(N)'"' local nmodels `"`r(nmodels)'"' local prefb `"`r(prefb)'"' local prefse `"`r(prefse)'"' local alpha `"`r(alpha)'"' local opts_command `"`r(opts_command)'"' local odds_ratio = r(odds_ratio) forvalues im = 1/`nmodeltypes' { local model`im'_name `"`r(model`im'_name)'"' local model`im'_opts `"`r(model`im'_opts)'"' } return add local results_to_return totalParLB totalParUB extremeLB extremeUB modeling95LB modeling95UB /// p1 p5 p10 p90 p95 p99 kurtosis skew /// rratio totalSE samplingSE modelingSE meanb meanr2 if "`bs_type'" != "" local results_to_return total95LB total95UB `results_to_return' if "`prefb'" != "" local results_to_return prefLB prefUB prefpctile preftotalSE `results_to_return' tempname `results_to_return' //Summarize the point estimates: modeling distribution quietly sum b_intvar if i_bs == 1, detail scalar `meanb' = r(mean) scalar `modelingSE' = r(sd) scalar `extremeLB' = r(min) scalar `extremeUB' = r(max) local percentiles 1 5 10 90 95 99 foreach pct of local percentiles { scalar `p`pct'' = r(p`pct') } scalar `skew' = r(skewness) scalar `kurtosis' = r(kurtosis) quietly centile b_intvar if i_bs == 1, centile(2.5 97.5) scalar `modeling95LB' = r(c_1) scalar `modeling95UB' = r(c_2) quietly summarize r2 if i_bs == 1 scalar `meanr2' = r(mean) quietly summarize variance if i_bs == 1, meanonly scalar `samplingSE' = sqrt(r(mean)) if "`bs_type'" != "" { quietly summarize b_bs scalar `totalSE' = r(sd) quietly centile b_bs, centile(2.5 97.5) scalar `total95LB' = r(c_1) scalar `total95UB' = r(c_2) } else scalar `totalSE' = sqrt(`modelingSE'^2 + `samplingSE'^2) scalar `rratio' = `meanb'/`totalSE' scalar `totalParLB' = `meanb' - 2*`totalSE' scalar `totalParUB' = `meanb' + 2*`totalSE' /* Calculate statistics for preferred estimate: percentile of modeling distribution, total standard error (= sqrt of sum of squares of preferred SE and modeling SE), semi-parametric confidence interval */ if "`prefb'" != "" { if `odds_ratio' == 1 local prefb = log(`prefb') quietly count if b_intvar <= `prefb' & i_bs == 1 scalar `prefpctile' = 100 * (r(N)/`nmodels') if `prefpctile' == 0 | `prefpctile' >= 100 scalar `prefpctile' = . scalar `preftotalSE' = sqrt(`prefse'^2 + `modelingSE'^2) scalar `prefLB' = `prefb' - 2*`preftotalSE' scalar `prefUB' = `prefb' + 2*`preftotalSE' } GenerateSigRatesMatrix, model_namelist("`model_idlist'") depvarlist("`depvarlist_noline'") intvarlist("`intvarlist_noline'") alpha(`alpha') return add GenerateInfStatsMatrix, modellist("`model_idlist'") depvarlist("`depvarlist_noline'") intvarlist("`intvarlist_noline'") return add generate se = sqrt(variance) drop variance quietly replace b_intvar= exp(b_intvar) if odds_ratio_ind == 1 quietly replace se = b_intvar*se if odds_ratio_ind == 1 quietly count if odds_ratio_ind == 0 if r(N) == 0 { local to_exp meanb totalParLB totalParUB modeling95LB modeling95UB extremeLB extremeUB if "`bs_type'" != "" local to_exp `"`to_exp' total95LB total95UB "' if "`prefb'" != "" local to_exp `"`to_exp' prefLB prefUB"' foreach t of local to_exp { scalar ``t'' = exp(``t'') } local to_multiply samplingSE modelingSE totalSE foreach t of local to_multiply { scalar ``t'' = `meanb'*``t'' } } if "`bs_type'" == "" { //quietly drop if i_bs != 1 quietly drop i_model i_bs } if `"`saveas'"' != "" quietly save `"`saveas'"' , `replace' return local saveas `"`saveas'"' //Return the regression type for display local reg_type "" forvalues im = 1/`nmodeltypes' { local m = "`model`im'_name'" if `im' > 1 local reg_type "`reg_type'/" if ("`m'" == "logit") local reg_type "`reg_type'Logit" else if ("`m'" == "logistic") local reg_type "`reg_type'Logistic" else if ("`m'" == "probit") local reg_type "`reg_type'Probit" else if ("`m'" == "poisson") local reg_type "`reg_type'Poisson" else if ("`m'" == "nbreg") local reg_type "`reg_type'Negative Binomial" else if ("`m'" == "areg") local reg_type "`reg_type'Absorbing" else if ("`m'" == "rreg") local reg_type "`reg_type'Robust" else if ("`m'" == "xtreg") { local m_opts = `"`opts_command' `opts`im'' "' local fe fe local be be if `: list fe in m_opts' == 1 local reg_type "`reg_type'Fixed-effects (within)" else if `: list be in m_opts' == 1 local reg_type "`reg_type'Between (group means)" else local reg_type "`reg_type'Random-effects GLS" } else local reg_type "`reg_type'Linear" } local reg_type "`reg_type' regression" local reg_type : list retok reg_type return local title `"`reg_type'"' foreach r of local results_to_return { return scalar `r' = ``r'' } end /* GenerateSigRatesMatrix calculates the rates of significant and positive coefficients for each functional form, depvar, and intvar, as well as the pooled rates, stores these numbers in the matrix sigrates and returns the matrix sigrates. */ program GenerateSigRatesMatrix, rclass syntax , model_namelist(string) depvarlist(string) intvarlist(string) alpha(real) //Create sigrates matrix local lists model_namelist depvarlist intvarlist local ncols 0 local colnames "all_models" foreach l of local lists { if wordcount(`"``l''"') > 1 { foreach entry of local `l' { local colnames `colnames' `entry' local ++ncols } } } local ++ncols local rownames NModels SignStability SignificanceRate Positive PositiveandSig Negative NegativeandSig local nrows = wordcount("`rownames'") matrix sigrates = J(`nrows', `ncols', 0) matrix colnames sigrates = `colnames' matrix rownames sigrates = `rownames' local i_row = 0 foreach rowname of local rownames { local ++i_row local `rowname' = `i_row' } //Calculate significance rates and store in matrix sigrates forvalues i_col = 1/`ncols' { local curr_colname = word("`colnames'", `i_col') if `:list curr_colname in model_namelist' == 1 local type model else if `:list curr_colname in depvarlist' == 1 local type depvar else local type intvar local ifcond "" if `i_col' > 1 local ifcond `"& `type' == "`curr_colname'""' quietly count if i_bs == 1 `ifcond' local curr_nmodels = r(N) matrix sigrates[`NModels', `i_col'] = `curr_nmodels' quietly count if pvalue <= `alpha' & i_bs == 1 `ifcond' matrix sigrates[`SignificanceRate', `i_col'] = 100*(r(N)/`curr_nmodels') quietly count if b_intvar> 0 & i_bs == 1 `ifcond' matrix sigrates[`Positive',`i_col'] = 100*(r(N)/`curr_nmodels') quietly count if b_intvar> 0 & pvalue <= `alpha' & i_bs == 1 `ifcond' matrix sigrates[`PositiveandSig',`i_col'] = 100*(r(N)/`curr_nmodels') quietly count if b_intvar< 0 & i_bs == 1`ifcond' matrix sigrates[`Negative', `i_col'] = 100*(r(N)/`curr_nmodels') quietly count if b_intvar < 0 & pvalue <= `alpha' & i_bs == 1 `ifcond' matrix sigrates[`NegativeandSig',`i_col'] = 100*(r(N)/`curr_nmodels') matrix sigrates[`SignStability',`i_col'] = max(sigrates[`Positive',`i_col'], sigrates[`Negative', `i_col']) } return matrix sigrates = sigrates end /* GenerateInfStatsMatrix */ program GenerateInfStatsMatrix, rclass syntax , modellist(string) depvarlist(string) intvarlist(string) quietly sum b_intvar if i_bs == 1 local meanb = r(mean) /*Build the varlist for the influence regression, including the rotated control variables and dummies for the functional form, depvar, and intvar. */ local inf_varnames "" unab rotatedvars : r_* local typelist model depvar intvar foreach type of local typelist { local n`type' = wordcount("``type'list'") if `n`type'' > 1 { forvalues i = 2/`n`type'' { local curr = word("``type'list'", `i') quietly gen byte `curr' = (`type' == "`curr'") local inf_varnames `"`inf_varnames' `curr'"' } } } local inf_varnames `"`inf_varnames' `rotatedvars'"' /* Run the regression for the marginal effect of variable inclusion on the estimate. Store results. */ quietly reg b_intvar `inf_varnames' if i_bs == 1 return scalar infr2_b = e(r2) return scalar infcons_b = _b[_cons] local ngroups = e(df_m) /* Create the 2 matrices: infstats_default, which stores the coefficients from the OLS influence regression, indicating the effect of inclusion of each variable or functional form on the magnitude of the estimate, and infstats_sig_only, which stores the odds ratios from the logistic influence regression, indicating the effect of inclusion of each variable or functional form on the likelihood of a positive or significant estimate. */ tempname infstats_default infstats_sig_only local colnames_default abs_coef coef pct_chg row_id local colnames_sig_only abs_coef_sig coef_sig coef_pos row_id_sig local ncols_default = wordcount("`colnames_default'") local ncols_sig_only = wordcount("`colnames_sig_only'") matrix `infstats_default' = J(`ngroups', `ncols_default', 0) matrix `infstats_sig_only' = J(`ngroups', `ncols_sig_only', 0) local groupleads "" /*If the term has meaningful influence coefficient then add it to the matrix. otherwise, check which variable it is grouped with and it to that group */ local lists colnames_default colnames_sig_only foreach namelist of local lists { local i_row 0 foreach name of local `namelist' { local ++i_row local `name' = `i_row' } } local i_row 0 foreach var of local inf_varnames { if _se[`var'] != 0 { local ++i_row local groupleads `"`groupleads' `var'"' local group_`var' "" matrix `infstats_default'[`i_row',`abs_coef'] = abs(_b[`var']) matrix `infstats_default'[`i_row',`coef'] = _b[`var'] matrix `infstats_default'[`i_row',`pct_chg'] = 100*(_b[`var']/`meanb') matrix `infstats_default'[`i_row',`row_id'] = `i_row' } else { foreach groupvar of local groupleads { quietly count if `var' != `groupvar' if r(N) == 0 { mata: st_local("displayname", asarray(mrobust_varnames, "`var'")) local group_`groupvar' `"`group_`groupvar'' `displayname'"' continue, break } } } } /* Run the regressions for marginal effect of variable inclusion on the probability of significant or positive estimate. Store results in the matrix. */ local dummies sig pos foreach d of local dummies { capture reg `d' `inf_varnames' if i_bs == 1 if _rc == 0 { local i_row 0 foreach var of local groupleads { local ++i_row matrix `infstats_sig_only'[`i_row', `coef_`d''] = _b[`var'] if "`d'" == "sig" { matrix `infstats_sig_only'[`i_row', `abs_coef_sig'] = abs(`infstats_sig_only'[`i_row', `coef_sig']) matrix `infstats_sig_only'[`i_row',`row_id_sig'] = `i_row' } } return scalar infr2_`d' = e(r2) return scalar infcons_`d' = _b[_cons] } return scalar `d'_rc = _rc } /* Sort the rows of each infstats matrix in descending order of absolute value of the influence coefficient. Iterate through the sorted row_ids and attach the rownames to matrices in the proper order */ local tosort default sig_only local sortcol = -1 foreach t of local tosort { mata: st_matrix("`infstats_`t''", sort(st_matrix("`infstats_`t''"), `sortcol')) local groupleads_sorted "" forvalues i_row = 1/`ngroups' { local nextgroup = word("`groupleads'", `infstats_`t''[`i_row', `ncols_`t'']) local groupleads_sorted `"`groupleads_sorted' `nextgroup'"' } matrix rownames `infstats_`t'' = `groupleads_sorted' matrix colnames `infstats_`t'' = `colnames_`t'' return matrix infstats_`t' = `infstats_`t'' } foreach var of local groupleads { return local group_`var' `"`group_`var''"' } end /* Display Results */ program DisplayResults, rclass syntax , time_taken(real) [ more full sig_only intervals influence saveas(string)] local prefb `"`r(prefb)'"' local model_namelist `"`r(model)'"' local model_idlist `"`r(model_idlist)'"' local depvarlist `"`r(depvar)'"' local intvarlist `"`r(intvar)'"' local nmodeltypes `"`r(nmodeltypes)'"' local bs_type `"`r(bs_type)'"' tempname sigrates matrix `sigrates' = r(sigrates) local ncols = colsof(`sigrates') local nrows = rowsof(`sigrates') di _newline _continue if "`sig_only'" == "sig_only" { local results_col 29 di as text `"`r(title)'"' ";" di as text "Variable of interest " _col(`results_col') as result r(intvar) di as text "Outcome variable " _col(`results_col') as result r(depvar) di as text "Possible control terms " _col(`results_col') as result r(nterms) di as text "Number of models" _col(`results_col') as result %-15.0fc r(nmodels) di as text "Number of observations" _col(`results_col') as result %-15.0fc r(N) di as text "{hline 35}" } if "`sig_only'" == "sig_only" | "`more'" == "more" { di as text "Significance Testing:" di _newline _continue local colstart 14 local colinterval 13 local line_length = round(`colstart' + (`ncols'+1) *`colinterval' ) if `ncols' > 1 { di _continue as text "Estimation Command: " local cnames : colfullnames `sigrates' local ic 0 foreach c of local cnames { local ++ic if "`c'" == "all_models" local c all models local display_colname `"`c'"' if `: list c in model_idlist' { MakeDisplayModelID `c' local display_colname `"`r(display_name)'"' } if `ic' == `ncols' local cont "" else local cont "_continue" local col_indent = `colstart' + `ic'*`colinterval' di `cont' as text _col(`col_indent') %12s abbrev("`display_colname'", 12) } di _continue as text "Number of Models: " local colstart 20 forvalues ic = 1/`ncols' { if `ic' == `ncols' local cont "" else local cont "_continue" local col_indent = `colstart' + `ic'*`colinterval' di `cont' as text _col(`col_indent') %5.0f `sigrates'[1,`ic'] } di as text "{hline `line_length'}" } local title2 "Sign Stability" local title3 "Significance Rate" local title4 "Positive" local title5 "Positive and Sig" local title6 "Negative" local title7 "Negative and Sig" forvalues ir = 2/`nrows' { local colstart 20 local pctsign "%" local asres "as result" if `ir' == 4 di as text "{hline `line_length'}" di _continue as text "`title`ir''" forvalues ic = 1/`ncols' { if `ic' == `ncols' local cont "" else local cont "_continue" local col_indent = `colstart' + `ic'*`colinterval' di `cont' as text _col(`col_indent') as result %5.0f `sigrates'[`ir',`ic'] "%" } } di as text "{hline `line_length'}" } /* Display all of the coefficient summary statistics */ else if "`full'" == "full" { di as text `"`r(title)'"' ";" di as text "Variable of interest " _col(29) as result r(intvar) di as text "Outcome variable " _col(29) as result abbrev(r(depvar), 15) _col(46) as text "Number of observations" _col(70) as result %9.0f r(N) di as text "Possible control terms " _col(29) as result r(nterms) _col(46) as text "Mean R-squared" _col(70) as result %9.2f r(meanr2) di as text "Number of models" _col(29) as result %-15.0fc r(nmodels) _col(46) as text "Multicollinearity" _col(70) as result %9.2f r(mc) di as text "{hline 79}" di "Model Robustness Statistics:" _col(46) "Significance Testing:" di _newline as text "Mean(b)" _col(19) as result %9.4f r(meanb) _col(46) as text "Sign Stability" _col (70) as result %9.0f `sigrates'[2,`ncols'] "%" di as text "Sampling SE" _col(19) as result %9.4f r(samplingSE) _col(46) as text "Significance rate" _col (70) as result %9.0f `sigrates'[3,`ncols'] "%" di as text "Modeling SE" _col(19) as result %9.4f r(modelingSE) _col(46) as text "{hline 34}" di as text "Total SE" _col(19) as result %9.4f r(totalSE) _col(46) as text "Positive" _col (70) as result %9.0f `sigrates'[4,`ncols'] "%" di as text "{hline 30}" _col(46) "Positive and Sig" _col (70) as result %9.0f `sigrates'[5,`ncols'] "%" di as text "Robustness Ratio:" _col(19) as result %9.4f r(rratio) _col(46) as text "Negative" _col (70) as result %9.0f `sigrates'[6,`ncols'] "%" di as text _col(46) as text "Negative and Sig" _col (70) as result %9.0f `sigrates'[7,`ncols'] "%" di as text "{hline 79}" if "`prefb'" != "" { di as text "Statistics for Preferred Estimate:" di _continue as text "Pref. est." _col(19) as result %9.4f r(prefb) di _col(41) as text "Percentile of modeling dist." _col(19) as result %5.0f r(prefpctile) as text "%" di as text "Sampling SE" _col(19) as result %9.4f r(prefse) di as text "Modeling SE" _col(19) as result %9.4f r(modelingSE) di as text "Total SE" _col(19) as result %9.4f r(preftotalSE) /*if "`model'" != "logistic" & "`irr'" != "irr" { di as text "Robustness Int." _col(21) "[" as result %-8.4f r(prefLB) as text "," /// as result %8.4f r(prefUB) as text "]" } */ di as text "{hline 79}" } } if "`influence'" != "noinfluence" & "`more'" != "more" { tempname infstats if "`sig_only'" == "sig_only" { di as text "Model Influence" _col(25) "{ul:Significance Regression}" _col(55) "{ul:Sign Regression}" matrix `infstats' = r(infstats_sig_only) di as text _col(29) "Marginal Effect" _col(55) "Marginal Effect" di as text _col(22) "on Significance Probability" _col(51) "on Positive Probability" local fmt_col2 "%9.4f" } else { di as text "Model Influence" matrix `infstats' = r(infstats_default) di as text _col(29) "Marginal Effect" _col(55) "Percent Change" di as text _col(26) "of Variable Inclusion" _col(56) "From Mean(b)" local fmt_col2 "%9.1f" } local col1 2 local col2 3 local infvars : rownames `infstats' local iv 0 foreach infvar of local infvars { local ++iv if strmatch("`infvar'", "r_*") mata: st_local("di`infvar'", asarray(mrobust_varnames, "`infvar'")) else if `: list infvar in model_idlist' { MakeDisplayModelID `infvar' local di`infvar' "model: `r(display_name)'" } else if `: list infvar in depvarlist' local di`infvar' "depvar: `infvar'" else local di`infvar' "intvar: `infvar'" if "`sig_only'" == "sig_only" { di _continue as text abbrev("`di`infvar''", 20) _col(29) as result %9.2f `infstats'[`iv', `col1'] di _col(55) as result %9.2f `infstats'[`iv',`col2'] as text } else { di _continue as text abbrev("`di`infvar''", 20) _col(29) as result %9.4f `infstats'[`iv', `col1'] di _col(55) as result %9.1f `infstats'[`iv',`col2'] as text "%" } local group_`infvar' `"`r(group_`infvar')'"' if "`group_`infvar''" != "" { foreach member of local group_`infvar' { mata: st_local("di`infvar'", asarray(mrobust_varnames, "`infvar'")) di as text abbrev("`member'", 20) _col(30) "(Grouped with " abbrev("`di`infvar''", 20) ")" } } } di _newline _continue if "`sig_only'" == "sig_only" { di as text "Constant" _col(29) as result %9.4f r(infcons_sig) _col(55) as result %9.4f r(infcons_pos) di as text "R-squared" _col(29) as result %9.4f r(infr2_sig) _col(55) as result %9.4f r(infr2_pos) } else { di as text "Constant" _col(29) as result %9.4f r(infcons_b) di as text "R-squared" _col(29) as result %9.4f r(infr2_b) } if "`sig_only'" == "sig_only" { if r(pos_rc) != 0 di as text "note: insufficient variation in sign for logistic regression" if r(sig_rc) != 0 di as text "note: insufficient variation in significance for logistic regression" } if `nmodeltypes' > 1 { local refmodel = word("`model_idlist'", 1) MakeDisplayModelID `refmodel' local refmodel `"`r(display_name)'"' di as text "note: `refmodel' is the reference model." } if wordcount("`intvarlist'") > 1 di as text "note: " word("`intvarlist'", 1) " is the reference interest variable." if wordcount("`depvarlist'") > 1 di as text "note: " word("`depvarlist'", 1) " is the reference dependent variable." di as text "{hline 79}" } if "`more'" != "more" { if "`intervals'" != "" & "`sig_only'" == "sig_only" di "note: intervals option not allowed with sig_only." else if "`intervals'" != "" | "`bs_type'" != "" { di as text "Robustness Intervals:" di _newline as text "Modeling 95%" _col(29) "[" as result %-8.3f r(modeling95LB) as text "," /// as result %8.3f r(modeling95UB) as text "]" di as text "Modeling Extreme Bounds" _col(29) "[" as result %-8.3f r(extremeLB) as text "," /// as result %8.3f r(extremeUB) as text "]" di as text "Total Parametric" _col(29) "[" as result %-8.3f r(totalParLB) as text "," as result %8.3f r(totalParUB) as text "]" di as text "Total 95%" _col(29) "[" as result %-8.3f r(total95LB) as text "," as result %8.3f r(total95UB) as text "]" } if `time_taken' > 120 { local ndigits = ceil(log10(`time_taken'/60)) + 3 di _newline as text "This command took " as result %-`ndigits'.0fc round(`time_taken'/60, 1) as text " minutes (" as result round(`time_taken'/3600, .1) as text " hours) to complete." } else di _newline as text "This command took " as result round(`time_taken', .1) as text " seconds (" as result round(`time_taken'/60, .1) as text " minutes) to complete." return scalar time_taken = `time_taken' if `"`r(saveas)'"' != "" di as text "Model data saved in data file " r(saveas) "." if `"`r(savelist)'"' != "" di as text "Model list saved in file " r(savelist) "." local options_used `"`r(opts_command)' `r(opts_mrobust)'"' if `nmodeltypes' > 1 { local opts_printed 0 if `"`: list retok options_used'"' != "" { local opts_printed 1 di as text "Options used: `options_used'" } forvalues im = 1/`nmodeltypes' { if `"`r(opts`im')'"' != "" { if `opts_printed' == 0 { di as text "Options used:" local opts_printed 1 } MakeDisplayModelID `model`im'_id' di as text "`r(display_name)': " r(model`im'_opts) } } if "`sig_only'" != "sig_only" & "`more'" != "more" { di as text "Type " as result "mrobust, more" as text " to see average results for each model type." } } else { local options_used `"`options_used' `r(opts1)'"' if `"`: list retok options_used'"' != "" di as text "Options used: `options_used'" } } return add end /* Translates model ids of the form "reg_1", "reg_2" etc. into "reg(1)", "reg(2)" etc. for display purposes. */ program MakeDisplayModelID, rclass return add syntax name (name = model_id) if regexm("`model_id'", "_[0-9]$") == 1 { local display_name = subinstr("`model_id'", "_", "(", .) return local display_name `"`display_name')"' } else return local display_name `"`model_id'"' end /* Plot the kdensity of the coefficient estimates according to user specifications. */ program PlotMRobust, rclass syntax [,normal nozero plotbs bs(string)] local intvar `"`r(intvar)'"' local prefb `"`r(prefb)'"' return add if "`bs'" == "" quietly sum b_intvar else quietly sum b_intvar if i_bs == 1 if "`rmal'" != "" local normal_opt || function normalden(x,`r(mean)',`r(sd)'), range(b_intvar) legend(label(2 "normal density")) else local normal_opt "" if "`zero'" != "" local zero_opt "" else local zero_opt xscale(range(0)) xlabel(#6) if "`prefb'" != "" local pref_opt xline(`prefb') else local pref_opt "" if "`plotbs'" != "" local kd_bs (kdensity b_bs) graph twoway `kd_bs' (kdensity b_intvar), xtitle("Coefficient on `intvar'") `normal_opt' `zero_opt' `pref_opt' end