*! version 1.2.0 03oct2014 Richard Williams, rwilliam@nd.edu * Simplify computing marginal effects after multiple outcome commands. * Saves mfx output from any command in format easily usable by * post-estimation table formatting commands like outreg2 and estout. * 1.0.0 - Original release - April 26, 2006 * 1.1.0 - Now works with the varlist option of mfx - June 18, 2007 * 1.2.0 - gologit29 added to the multiple outcome list program define mfx2, eclass version 8.2 local oktype = cond(`c(stata_version)' < 9, /// "integer `c(level)'", "cilevel") syntax [if] [in] [, Level(`oktype') Replace NOLabel NOSe /// stub(name) PREDict(string) NOLOG /// Multiple Single trustme * ] // trustme lets the user override what the program thinks is correct. // If the user is wrong though, fatal errors or incorrect results // may follow. if "`trustme'"=="" { local trust 0 } else { local trust 1 } local cmd `e(cmd)' if "`cmd'"=="" & !`trust' { display as error "last estimates not found" exit 301 } if "`multiple'"!="" & "`single'" != "" { display as error "You cannot specify both single and multiple" exit 198 } // These are programs that do not work with mfx2 local cursed asmprobit anova clogit nlogit gologit local banned: list cmd & cursed if "`banned'"!="" & !`trust' { display as error "Sorry. " as yellow "`cmd'" as error " is not currently supported by mfx2." display as error "Check the help for " as yellow "`cmd'" as error " to see if there are other alternatives." display as error "Or, use the " as yellow "trustme " as error "option if you want to try anyway." exit 198 display } // Play or Replay the results. The use of estimates replay // makes the program call itself for the replay. if replay() & "`e(cmd)'"=="mfx2" { Replay , level(`level') exit } // Classify programs as multiple-outcome or single/binary/count outcome. // Add to this list as more programs get classified local multprog ologit oprobit mlogit mprobit slogit local multprog `multprog' oglm gologit2 goprobit gologit29 // Classify programs as multinomial or single outcomes local multicmd: list cmd & multprog if "`multicmd'"!="" { local multinomial 1 } else { local multinomial 0 } // User can override if "`multiple'"!="" local multinomial 1 // Determine if a command not specified above produces multiple outcomes if "`single'"!="" local multinomial 0 if "`stub'"=="" { local stub `e(cmd)' } if "`nolog'" =="" { local quietly "noisily" } else { local quietly "quietly" } tempname coef _estimates hold `coef', restore copy * Save scalars local scalars : e(scalars) foreach el in `scalars' { tempname s`el' scalar `s`el'' = e(`el') } local y `e(depvar)' local xcmd `e(cmd)' tempname cat mfx_b mfx_se mfx_V mfx_X xmfx semfx mfx_se2 mfxx `quietly' { // For multinomial, get the categories of the DV; transpose matrix if `multinomial' { display display as yellow "Frequencies for `y'..." display tab `y' if e(sample), matrow(`cat') matrix `cat' = `cat'' local numcat = r(r) } if `multinomial' { local endcat `numcat' } else { local endcat 1 } // Compute marginal effects forval i = 1/`endcat' { if `multinomial' { local xcat = `cat'[1,`i'] display display as yellow "Computing marginal effects after `cmd' for `y' == `xcat'..." display mfx compute `if' `in', predict(`predict' o(`xcat')) level(`level') `options' `nose' } else { display display as yellow "Computing marginal effects after `cmd' for `y'..." display mfx compute `if' `in', predict(`predict') level(`level') `options' `nose' } local mfxtype `=e(Xmfx_type)' matrix `xmfx' = e(Xmfx_`mfxtype') matrix `mfxx' = e(Xmfx_X) if "`nose'"=="" { matrix `semfx' = e(Xmfx_se_`mfxtype') } else { matrix `semfx' = `xmfx' * 0 } if `multinomial' { matrix coleq `xmfx' = eq`i' matrix coleq `semfx' = eq`i' } matrix `mfx_b' = nullmat(`mfx_b'), `xmfx' matrix `mfx_se' = nullmat(`mfx_se'), `semfx' matrix `mfx_X' = nullmat(`mfx_X'), `mfxx' } display display as yellow "Preparing final results..." display } // Use Y value labels if requested for multiple-outcome commands if `multinomial' & "`nolabel'"=="" Use_Value_Labels, b(`mfx_b') se(`mfx_se') x(`mfx_X') cat(`cat') y(`y') // This code added in version 1.1 of mfx2 // Special code needed if some elements are missing, e.g. varlist option has been used if matmissing(`mfx_b') { tempname b_temp se_temp x_temp local ncols = colsof(`mfx_b') local names : colfullnames `mfx_b' forval i = 1/`ncols' { if !missing(el(`mfx_b', 1, `i')) { matrix `b_temp' = nullmat(`b_temp'), `mfx_b'[1, `i'] matrix `se_temp' = nullmat(`se_temp'), `mfx_se'[1, `i'] matrix `x_temp' = nullmat(`x_temp'), `mfx_X'[1, `i'] local xname: word `i' of `names' local xnames `xnames' `xname' } } matrix colnames `b_temp' = `xnames' matrix colnames `se_temp' = `xnames' matrix colnames `x_temp' = `xnames' matrix `mfx_b' = `b_temp' matrix `mfx_se' = `se_temp' matrix `mfx_X' = `x_temp' } // End of special code added in version 1.1 // Create V matrix. Note that only the variances are right. Missing elements will be // replaced by 0. tempname se2 matrix `se2' = `mfx_se' local missing = matmissing(`se2') if `missing' { forval i = 1/`=colsof(`se2')' { if `se2'[1,`i'] >= . matrix `se2'[1,`i'] = 0 } } matrix `mfx_V' = diag(`se2')' * diag(`se2') // Temporarily create new e(b), e(V) tempname b V x matrix `b' = `mfx_b' matrix `V' = `mfx_V' matrix `x' = `mfx_X' tempvar touse gen `touse' = e(sample) ereturn post `b' `V', esample(`touse') * Restore scalars foreach el in `scalars' { eret scalar `el' = `s`el'' } if `multinomial' ereturn matrix cat = `cat' ereturn local depvar `y' local title Marginal effects after `xcmd' ereturn matrix mfx_X `x' ereturn local cmd "mfx2" capture estimates drop `stub'_mfx estimates store `stub'_mfx, title(`title') _estimates unhold `coef' // Add mfx values ereturn matrix mfx_b_`mfxtype' `mfx_b' ereturn matrix mfx_se_`mfxtype' `mfx_se' ereturn matrix mfx_X `mfx_X' capture estimates drop `stub'_coef estimates store `stub'_coef if "`replace'"=="replace" { quietly estimates restore `stub'_mfx display display as yellow "mfx results are now active. Original results are stored as `stub'_coef." } else { display display as yellow "Original results are now active. mfx results are stored as `stub'_mfx." } // Print out results estimates replay `stub'_mfx, level(`level') end ************************ program Replay syntax, level(string) ereturn display , level(`level') end ************************ program Use_Value_Labels, eclass // Adapted from gologit2 syntax, b(name) se(name) x(name) cat(name) y(varname) // Use Y value labels if user has requested them local M = colsof(`cat') local Numeqs = `M' tempname Y_Values matrix `Y_Values' = `cat' *local eqnames `e(eqnames)' // Numx2 is same as Numx for marginal effects local Numx2 = colsof(`b')/ `M' // Retrieve the Y value labels, turn into equation names forval i = 1/`M' { local j = `Y_Values'[1,`i'] local vlabel: label(`y') `j' 32 // Replace characters that cause problems local vlabel: subinstr local vlabel "." "_", all local vlabel: subinstr local vlabel ":" "_", all local vlabel: subinstr local vlabel "$" "_", all local vlabel: subinstr local vlabel " " "_", all // base category (i.e. highest Y value) is separate // from the equation names if `i'==1 { local new_eqnames `"`"`vlabel'"'"' } else { local new_eqnames `"`new_eqnames' `"`vlabel'"'"' } } // Duplicate names screw up the printout local testnames: list dups new_eqnames if "`testnames'"!="" { display as error "When using value labels, each " /// "value label for the DV must be unique. " display as error "Equations will instead be " /// "labeled eq1, eq2, etc." exit } // Have to repeat equation name for every x forval i = 1/`Numeqs' { local vlabel: word `i' of `new_eqnames' forval j = 1/`Numx2' { local vlabels `"`vlabels' `"`vlabel'"'"' } } // Replace the current b, se matrices capture matrix coleq `b' = `vlabels' if _rc !=0 { display as error "There is a problem with your value labels." display as error "Equations will instead be labeled eq1, eq2, etc." exit } matrix coleq `se' = `vlabels' matrix coleq `x' = `vlabels' end