├── ex1.png ├── ex2.png ├── stata.toc ├── speccurve.pkg ├── README.md ├── speccurve_gendata.ado ├── speccurve.sthlp └── speccurve.ado /ex1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martin-andresen/speccurve/HEAD/ex1.png -------------------------------------------------------------------------------- /ex2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martin-andresen/speccurve/HEAD/ex2.png -------------------------------------------------------------------------------- /stata.toc: -------------------------------------------------------------------------------- 1 | v 1.1 2 | d Martin Eckhoff Andresen, Statistics Norway 3 | p speccurve Stata package for plotting specirfication curves 4 | -------------------------------------------------------------------------------- /speccurve.pkg: -------------------------------------------------------------------------------- 1 | v 1.1 2 | d speccurve Stata package to plot specification curves 3 | d 4 | d {bf:M. E. Andresenr, Research department, Statistics Norway} 5 | d Stata package to plot specification curves. After installation, see help {bf:mtefe}, help {bf:mtefeplot}. 6 | d 7 | d Distribution-Date: 20200826 8 | f speccurve.ado 9 | f speccurve.sthlp 10 | f speccurve_gendata.ado 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # speccurve 2 | Stata package to plot specification curves 3 | 4 | ![Speccurve example](https://github.com/martin-andresen/speccurve/blob/master/ex2.png) 5 | 6 | Currently only works in Stata 16, makes ugly plots in earlier versions. Looking at it. 7 | 8 | Bugs likely, do report to martin.eckhoff.andresen@gmail.com 9 | 10 |

Installing development version

11 | net install speccurve, from("https://raw.githubusercontent.com/martin-andresen/speccurve/master") 12 | -------------------------------------------------------------------------------- /speccurve_gendata.ado: -------------------------------------------------------------------------------- 1 | cap program drop speccurve_gendata 2 | { 3 | program define speccurve_gendata 4 | version 15.0 5 | 6 | syntax 7 | 8 | preserve 9 | 10 | cap which estadd 11 | if _rc!=0 { 12 | di in red "You need the estout package to add scalars to stored estimates, see -ssc install estout-." 13 | exit 301 14 | } 15 | clear all 16 | sysuse auto, clear 17 | 18 | cap rm estiv.ster 19 | cap rm estfs.ster 20 | cap rm estmod.ster 21 | 22 | //Linear regression models 23 | 24 | loc no=0 25 | qui foreach m in "" "mpg" { 26 | foreach h in "" "headroom" { 27 | foreach t in "" "trunk" { 28 | foreach g in "" "gear_ratio" { 29 | foreach l in "" "length" { 30 | foreach tu in "" "turn" { 31 | foreach r in "" "rep78" "i.rep78" { 32 | loc ++no 33 | reg price weight `m' `h' `t' `g' `l' `tu' `d' `r' 34 | estadd scalar no=`no' 35 | loc numc: word count `controls' 36 | estadd scalar numcontrols=`numc' 37 | eststo ols`no' 38 | } 39 | } 40 | } 41 | } 42 | } 43 | } 44 | } 45 | 46 | 47 | 48 | //IV models 49 | tokenize mpg headroom trunk gear_ratio turn displacement 50 | 51 | cap rm estfs.ster 52 | cap rm estiv.ster 53 | 54 | loc no=0 55 | qui forvalues mpg=0/1 { 56 | forvalues headroom=0/1 { 57 | forvalues trunk=0/1 { 58 | forvalues gear_ratio=0/1{ 59 | forvalues turn=0/1 { 60 | forvalues displacement=0/1 { 61 | loc controls 62 | forvalues i=1/6 { 63 | if ```i'''==1 loc controls `controls' ``i'' 64 | } 65 | loc ++no 66 | ivregress 2sls price (weight=length) `controls' 67 | estadd scalar no=`no' 68 | loc numc: word count `controls' 69 | estadd scalar numcontrols=`numc' 70 | estimates title: iv`no' 71 | est save estiv, append 72 | 73 | reg weight length `controls' 74 | estadd scalar no=`no' 75 | loc numc: word count `controls' 76 | estadd scalar numcontrols=`numc' 77 | estimates title: fs`no' 78 | est save estfs, append 79 | } 80 | } 81 | } 82 | } 83 | } 84 | } 85 | 86 | //Discrete choice models 87 | tokenize mpg headroom trunk gear_ratio 88 | 89 | cap rm estmod.ster 90 | 91 | loc no=0 92 | 93 | foreach mod in regress logit probit { 94 | qui forvalues mpg=0/1 { 95 | forvalues headroom=0/1 { 96 | forvalues trunk=0/1 { 97 | forvalues gear_ratio=0/1 { 98 | loc controls 99 | forvalues i=1/4 { 100 | if ```i'''==1 loc controls `controls' ``i'' 101 | } 102 | loc ++no 103 | `mod' foreign weight `controls' 104 | if "`mod'"!="regress" margins, dydx(*) post 105 | estadd scalar no=`no' 106 | if "`mod'"=="regress" estadd scalar lpm=1 107 | else estadd scalar lpm=0 108 | if "`mod'"=="probit" estadd scalar probit=1 109 | else estadd scalar probit=0 110 | if "`mod'"=="logit" estadd scalar logit=1 111 | else estadd scalar logit=0 112 | loc numc: word count `controls' 113 | estadd scalar numcontrols=`numc' 114 | estimates title: mod`no' 115 | est save estmod, append 116 | } 117 | } 118 | } 119 | } 120 | } 121 | 122 | //SUBGROUP EXAMPLES 123 | tokenize mpg headroom trunk gear_ratio length turn displacement 124 | 125 | cap rm subgroups.ster 126 | 127 | loc no=0 128 | qui forvalues mpg=0/1 { 129 | forvalues headroom=0/1 { 130 | forvalues trunk=0/1 { 131 | forvalues gear_ratio=0/1{ 132 | forvalues length=0/1 { 133 | forvalues turn=0/1 { 134 | forvalues displacement=0/1 { 135 | 136 | loc controls 137 | forvalues i=1/7 { 138 | if ```i'''==1 loc controls `controls' ``i'' 139 | } 140 | 141 | forvalues f=0/1 { 142 | loc ++no 143 | reg price weight `controls' if foreign==`f' 144 | estadd scalar no=`no' 145 | estadd scalar foreign=`f' 146 | estadd scalar domestic=`=1-`f'' 147 | loc numc: word count `controls' 148 | estadd scalar numcontrols=`numc' 149 | estimates title: subgroup`no' 150 | est save subgroups, append 151 | } 152 | loc ++no 153 | reg price weight `controls' 154 | estadd scalar no=`no' 155 | estadd scalar foreign=1 156 | estadd scalar domestic=1 157 | loc numc: word count `controls' 158 | estadd scalar numcontrols=`numc' 159 | estimates title: subgroup`no' 160 | est save subgroups, append 161 | } 162 | } 163 | } 164 | } 165 | } 166 | } 167 | } 168 | 169 | 170 | 171 | restore 172 | end 173 | } 174 | -------------------------------------------------------------------------------- /speccurve.sthlp: -------------------------------------------------------------------------------- 1 | {smcl} 2 | {cmd:help speccurve} 3 | {hline} 4 | 5 | {title:Title} 6 | 7 | {p2colset 5 20 22 2}{...} 8 | {p2col:{cmd:speccurve} {hline 2}} Plot specification curve for estimates in memory or on disk.{p_end} 9 | {p2colreset}{...} 10 | 11 | 12 | {marker syntax}{...} 13 | {title:Syntax} 14 | 15 | {p 8 11 2} 16 | {cmd:speccurve} [namelist] [using] {cmd:,} param(name) [main(name) panel(namelist, {it:panel_opts}) controls[({it:panel_opts})] addplot([namelist] 17 | [samemodel] [using], param(name) {it:panel_opts}) level(numlist) keep(numlist) sort(name|none) fill graphopts(string) title(string) save(name)] 18 | 19 | {synoptset 25 tabbed}{...} 20 | {synopthdr} 21 | {synoptline} 22 | {syntab:Main} 23 | {synopt:{opt param(name)}} exact name of the coefficient of interest.{p_end} 24 | {synopt:{opt main(name)}} name of the main specification, which will be marked in the figure.{p_end} 25 | {synopt:{opt panel(namelist, [panel_opts])}} specification of a panel of characteristics for each estimate, 26 | see availabe options below. This option may be repeated multiple times for more panels. The order they are 27 | specified determines plot order.{p_end} 28 | {synopt:{opt controls([panel_opts])}} specifies that a specification panel is drawn, plotting which 29 | covariates (excluding the parameter of interest) is present in each specification. Options (which may 30 | be specified within parentheses) are detailed below. Order of the specification of this panel relative 31 | to the other specifications determines plot order.{p_end} 32 | {synopt:{opt addcoef([samemodel] [namelist] [using], param(name) [panel_opts])}} specifies that an additional plot is drawn, plotting 33 | the distribution of another coefficient of interest. param(name)-suboption is required, other options detailed 34 | below. Estimates are sorted according to the sort order in the main panel.{p_end} 35 | {synopt:{opt addscalar(name, [panel_opts])}} specifies that an additional scalar from e() is plotted in a separate panel. Estimates are sorted according to the sort order in the main panel.{p_end} 36 | {synopt:{opt level(numlist)}} level for confidence intervals, maximum 2. Default is 90 and 95% intervals.{p_end} 37 | {synopt:{opt keep(numlist)}} Must contain 3 nonnegative integers. Only the #1 smallest point estimates and the #3 largest point estimates are plotted, together with #2 other randomly drawn estimates.{p_end} 38 | {synopt:{opt graphopts(string)}} twoway options added to the main coefficient panel, use with caution.{p_end} 39 | {synopt:{opt sort(none|varname)}} changes sorting behavior. "none" does not sort estimates at all, "varname" sorts by specified variable. Default behaviour is to sort by estimate size.{p_end} 40 | {synopt:{opt title}} specifies the title of the main coefficient panel.{p_end} 41 | {synopt:{opt fill}} sets missing data for scalars specified in panel to 0.{p_end} 42 | {synopt:{opt save(name)}} saves the dataset in {it:name}, replacing any existing file by that name, before plotting and applying keep().{p_end} 43 | {synopt:{opt ytitle(string)}} names the main y-axis. Default is "coefficient on <>", where param is the main parameter of interest.{p_end} 44 | 45 | {syntab:panel_opts} 46 | {synopt:{opt labels(string)}} one-word labels used for each characteristic in the panel. Default: Name of scalar. {p_end} 47 | {synopt:{opt title(string)}} title of panel.{p_end} 48 | {synopt:{opt graphopts(string)}} other graph options, parsed directly to the twoway command that draws the panel.{p_end} 49 | {synoptline} 50 | 51 | 52 | {marker description}{...} 53 | {title:Description} 54 | 55 | {pstd} 56 | {cmd:speccurve} plots specifcation curves using stored or saved estimates of the same parameter with various specifications.{p_end} 57 | 58 | {pstd} 59 | If {it:using} is not specified, speccurve takes estimates from memory. If {it:using} is specified, speccurve takes estimates from the file 60 | using.ster. Either way, if {it:namelist} is specified, only estimates specified in namelist are used. Abbreviations and wildcards are allowed. 61 | If {it:namelist} is unspecified, all estimates in memory or in using.ster are used. When saving estimates, store one-word estimate names 62 | in estimates title using estimates title: yourname before saving if you want to use namelist to refer to some of them.{p_end} 63 | 64 | {pstd} 65 | Speccurve by default plots all estimates of the parameter of interest specified in param(name) with confidence bands.{p_end} 66 | 67 | {pstd} 68 | If the controls[()] option is specified (without parentheses allowed if not using suboptions), speccurve additionally plots a specification 69 | panel indicating with dots which covariates are included in each specification. Subobtions allowed, see below. If fixed effects are included, this 70 | option adds one line to this panel for each value of the fixed effects. Alternatively, users may manually specify an indicator for each set of fixed 71 | effects using the panel() option.{p_end} 72 | 73 | {pstd}If panel() is specified, speccurve additionally plots a user-specified specification panel using data specified in the panel() option. 74 | Panel() requires a namelist consisting of the names of scalars stored with each estimate to be plotted. This option may be repeated multiple 75 | times, and the order in which they are specified (and their relation to controls()-panel, if using) determines the order in which they are plotted. 76 | Subptions allowed, see below.{p_end} 77 | 78 | {pstd} 79 | If addcoef() is specified, speccurve plots an additional set of coefficients in a separate panel. param(name) suboption is required, specifying 80 | the name of the additional parameter of interest. If samemodel is specified in this option, speccurve looks for this coefficient in the same 81 | models already specified, in essence plotting a control variable. Alternatively, using may be specified to take these estimate from models stored 82 | in using.ster. If neither using nor samemodel is specified speccurve takes estimates from models in memory. Either way, only estimates specified 83 | in namelist is used, allowing wildcards, so that you may specify a subset of the estimates in memory or using.ster be used. Unless taking estimates 84 | from the same model as the main panel using samemodel, speccurve will assume that the order in which the models appear are the same as specified 85 | for the main coefficient of interest. Addplot allows further suboptions, see below.{p_end} 86 | 87 | {pstd} 88 | Addscalar() works similarly as addcoef, but plots an additional panel with a scatter plot of a scalar stored in e(). 89 | {p_end} 90 | 91 | {pstd}Panel(), controls() and addplot() options all allow the suboptions title() and graphopts(), where the first specifies a title of the relevant 92 | panel and the latter specifies other {helpb twoway_options} to be added to the panel - use with caution because these may interfere with the panel 93 | spacing and alignment. Panel() and controls() also allows labels(), where the user can specify alternative one-word labels for each entry in the panel 94 | that appear on the y axis.{p_end} 95 | 96 | {pstd}Main() also allows the suboption graphopts(), which can similarly be used specify {helpb twoway_options} modifying its style. 97 | 98 | {marker preparation}{...} 99 | {title:Saving or storing your models before using speccurve} 100 | 101 | {pstd} 102 | {cmd:speccurve} takes results from saved (if specifying {it:using}) or stored (if not) models. Stata can store a maximum of 512 models in memory, so 103 | if the number of specifications exceed this, you need to save the estimates.{p_end} 104 | 105 | {pstd} 106 | First estimate your model using any e-class command that stores b and V in e(). If you want to use custom specification panels, you need to store 107 | scalars with the estimates indicating the details of the specification using estadd. As an example, add a scalar indicating that the "foreign" 108 | subsample was used by specifying estadd scalar "foreign=1" or add the number of the polynomial of some control using "estadd scalar polynomial=2". 109 | estadd is part of the estout package, see ssc -install estout-. If you are comparing estiamtes using different functional forms, 110 | you may wish to compute marginal effects using -margins, dydx(*) post- to store the marginal effects in e() before running speccurve.{p_end} 111 | 112 | {pstd} 113 | After all details of the specification is added to the estimate, either store the estimate using "eststo name", or add a title and save the estimate 114 | using "estimates title: name" (one word names only) followed by "estimates save filename, append", where append assures that you add the model to 115 | "filename" in addition to any models already stored there - all models must be saved in the same file.{p_end} 116 | 117 | {pstd} 118 | If you want a custom control panel, where for example fixed effects are indicated with a single line for a full set of fixed effect rather than a 119 | line for each level of the fixed effect, you'll need to add indicators for this manually, for example using estadd scalar county_fe=1 and later 120 | panel(county_fe) in speccurve to specify the panel manually.{p_end} 121 | 122 | {pstd} 123 | After doing this for all your specifications, you are ready to plot specification curves.{p_end} 124 | 125 | {marker confint}{...} 126 | {title:An note on confidence intervals} 127 | 128 | {pstd} 129 | Speccurve plots pointwise confidence intervals, meaning confidence intervals on the parameter of interest estimated for 130 | each model separately. This is in line with what is typically reported in robustness tables and figures. Note, however, 131 | that when formally testing the hypothesis that a coefficient from an alternative model is different from the coefficient from 132 | the baseline model, correct inference requires the models to be estimated simultaneously rather than simply comparing whether 133 | the pointwise confidence intervals overlap. Speccurve is purely a plotting tool and so does not estimate the models, 134 | but users are encouraged to estimate models simultaneously when formally testing hypotheses. 135 | 136 | 137 | {marker examples}{...} 138 | {title:Examples} 139 | 140 | {pstd} 141 | Examples models are estimated in speccurve_gendata: 142 | 143 | {pstd} 144 | Estimate a bunch of (arguably silly) models to use for examples. Load auto data and estimate 128 regressions of price on weight, controlling for all combinations 145 | of mpg, headroom, trunk, gear_ratio, length, turn and displacement. Store these in memory, calling them ols1-ols128. Add a dummy variable indicating the inclusion 146 | of each control, and also a numerical variable indicating the number of control variables used. 147 | 148 | {pstd} 149 | Estimate the same models again, but absorb fixed effects for rep78 using xtreg. Add a dummy for whether these factor variables was controlled for. 150 | 151 | {ptsd} 152 | Also estimate 48 2SLS models of price on weight with various controls, using length as an instrument for weight, and store these in 153 | estiv.ster, and the associated first stage regressions, storing them in estfs.ster. 154 | 155 | {ptsd} 156 | Estimate linear probability, logit and probit models of the probability that a car is foreign based on weight 157 | and various controls, store these in estmod.ster. {p_end} 158 | 159 | {ptsd} 160 | Finally estimate all the linear regressions models separately for foreign and domestic cars, and add a dummy indicating what sample was used. 161 | 162 | {ptsd} 163 | 164 | {phang2}({stata "speccurve_gendata":{it:click to run})}{p_end} 165 | 166 | {pstd} 167 | Specification plot with automatic control panel, highlight main specification {p_end} 168 | {phang2}{cmd:. speccurve, param(weight) controls main(ols1)}{p_end} 169 | {phang2}({stata "speccurve, param(weight) controls main(ols1)":{it:click to run}}){p_end} 170 | 171 | {pstd} 172 | Unfortunately this figure is a bit cluttered. Therefore just plot the smallest and largest 20 estimates and 20 random estimates in the middle:{p_end} 173 | {phang2}{cmd:. speccurve, param(weight) controls main(ols1) keep(20 20 20)} {p_end} 174 | {phang2}({stata "speccurve, param(weight) controls main(ols1) keep(20 20 20)":{it:click to run}}) {p_end} 175 | 176 | {pstd} 177 | Plot a panel indicating just the number of controls instead {p_end} 178 | {phang2}{cmd:. speccurve, param(weight) panel(numcontrols) main(ols1)}{p_end} 179 | {phang2}({stata "speccurve, param(weight) panel(numcontrols) main(ols1)":{it:click to run}}){p_end} 180 | 181 | 182 | {pstd} 183 | Use the addcoef() option to plot the values of the control variable length{p_end} 184 | {phang2}{cmd:. speccurve, param(weight) main(ols1) keep(20 20 20) controls addcoef(samemodel, param(length))} {p_end} 185 | {phang2}({stata "speccurve, param(weight) main(ols1) keep(20 20 20) controls addcoef(samemodel, param(length))":{it:click to run}}) {p_end} 186 | 187 | {pstd} 188 | Use the addscalar() option to plot a separate plot of R^2 from each model.{p_end} 189 | {phang2}{cmd:. speccurve, param(weight) main(ols1) keep(20 20 20) controls addscalar(r2, graphopts(ytitle(R squared)))} {p_end} 190 | {phang2}({stata "speccurve, param(weight) main(ols1) keep(20 20 20) controls addscalar(r2, graphopts(ytitle(R squared)))":{it:click to run}}) {p_end} 191 | 192 | 193 | {pstd} 194 | Plot the IV estimates from estiv.ster, a control panel and use the addcoef() option to plot the associated first stage estimates for each model, taking them from estfs.ster:{p_end} 195 | {phang2}{cmd:. speccurve using estiv, param(weight) main(iv1) keep(20 20 20) controls addcoef(using estfs, param(length) graphopts(ytitle(first stage estimates))) graphopts(ytitle(IV estimates))} {p_end} 196 | {phang2}({stata "speccurve using estiv, param(weight) main(iv1) keep(20 20 20) controls addcoef(using estfs, param(length) graphopts(ytitle(first stage estimates))) graphopts(ytitle(IV estimates))":{it:click to run}}) {p_end} 197 | 198 | {pstd} 199 | Plot the marginal effects of weight on the probability that a car is foreign from linera probability, logit and probit models with various controls, from estmod.ster:{p_end} 200 | {phang2}{cmd:. speccurve using estmod, param(weight) controls panel(lpm logit probit) graphopts(ytitle(marginal effect of weight))} {p_end} 201 | {phang2}({stata "speccurve using estmod, param(weight) controls panel(lpm logit probit) graphopts(ytitle(marginal effect of weight))":{it:click to run}}) {p_end} 202 | 203 | {pstd} 204 | Plot subgroup specific models (foreign vs domestic cars vs. both), adding a panel that indicates the subgroup:{p_end} 205 | {phang2}{cmd:. speccurve using subgroups, param(weight) panel(foreign domestic,title(subgroup)) controls(title(controls)) keep(20 20 20)} {p_end} 206 | {phang2}({stata "speccurve using subgroups, param(weight) panel(foreign domestic,title(subgroup)) controls(title(controls)) keep(20 20 20)":{it:click to run}}) {p_end} 207 | 208 | {marker saved_results}{...} 209 | {title:Stored results} 210 | 211 | {pstd} 212 | {cmd:speccurve} stores the following in {cmd:r()}: 213 | 214 | {synoptset 20 tabbed}{...} 215 | {synopt:{cmd:r(table)}}Matrix of plotted data{p_end} 216 | 217 | {marker Author}{...} 218 | {title:Author} 219 | 220 | {pstd}Martin Eckhoff Andresen{p_end} 221 | {pstd}University of Oslo & Statistics Norway{p_end} 222 | {pstd}Oslo, NO{p_end} 223 | {pstd}martin.eckhoff.andresen@gmail.com{p_end} 224 | {pstd}bugs, comments, feedback and feature requests welcome{p_end} 225 | 226 | {marker Thanks}{...} 227 | {title:Thanks to} 228 | {pstd}Uri Simonsohn, Joseph Simmons and Leif D. Nelson for their paper "Specification Curve Analysis" 229 | (Nat Hum Behav, 2020, and previous working paper), first (?) suggesting the specification curve.{p_end} 230 | 231 | {pstd}Hans H. Sievertsen for posting a graph that inspired this program on {browse "twitter.com/hhsievertsen/status/1188780383736909825":Twitter} {p_end} 232 | -------------------------------------------------------------------------------- /speccurve.ado: -------------------------------------------------------------------------------- 1 | *! speccurve v1.2, 20240513 2 | * Author: Martin Eckhoff Andresen 3 | 4 | cap program drop speccurve panelparse speccurverun sortpreserve addcoefparse savecoefs 5 | 6 | program define speccurve 7 | version 16.0 8 | 9 | preserve 10 | 11 | loc panelno=0 12 | loc controlpanelno=0 13 | while `"`0'"' ! = "" { 14 | gettoken mac 0: 0, parse(" ") bind 15 | if substr("`mac'",1,5)=="panel" { 16 | loc panels `panels' `mac' 17 | loc ++panelno 18 | } 19 | else if substr("`mac'",1,8)=="controls" { 20 | if "`controladded'"!="" { 21 | noi di in red "Do not repeat the controls option" 22 | exit 301 23 | } 24 | loc controladded=1 25 | 26 | loc ++panelno 27 | loc controlpanelno=`panelno' 28 | if strpos("`mac'","(")>0 loc content `=substr("`mac'",strpos("`mac'","(")+1,`=strlen("`mac'")-strpos("`mac'","(")-1')' 29 | while "`content'"!="" { 30 | gettoken tok content: content, parse(" ") bind 31 | if substr("`tok'",1,6)=="labels" loc controllabels `=substr("`tok'",8,`=strlen("`tok'")-8')' 32 | else if substr("`tok'",1,5)=="title" loc controltitle `=substr("`tok'",7,`=strlen("`tok'")-7')' 33 | else if substr("`tok'",1,9)=="graphopts" loc controlgraphopts `=substr("`tok'",11,`=strlen("`tok'")-11')' 34 | else { 35 | noi di in red "Suboption `tok' not allowed in controls()." 36 | exit 301 37 | } 38 | } 39 | } 40 | else loc syntax `syntax' `mac' 41 | } 42 | 43 | speccurverun `syntax' panels(`panels') controlpanelno(`controlpanelno') controlgraphopts(`controlgraphopts') controltitle(`controltitle') controllabels(`controllabels') 44 | 45 | 46 | end 47 | 48 | 49 | * Author: Martin Eckhoff Andresen 50 | * Inspired by Uri Simonsohn, Joseph Simmons and Leif D. Nelson's paper on the specification curve and Hans H. Sievertsen @ Twitter 51 | 52 | program define speccurverun, rclass 53 | version 15.0 54 | 55 | syntax [anything] [using/] , param(name) [controlpanelno(integer 0) addcoef(string) controlpanel graphopts(string) controltitle(string) controllabels(string) controlgraphopts(string) main(string) panels(string) keep(numlist min=3 max=3 >=0 integer) level(numlist min=1 max=2 sort integer >0 <100) title(string) sort(name) save(name) fill addscalar(string) drop(string) ytitle(string)] 56 | 57 | 58 | qui { 59 | tempvar spec coefs r keepvar bin 60 | tempfile output 61 | 62 | loc numpanels=0 63 | while "`panels'"!="" { 64 | loc ++numpanels 65 | if `numpanels'==`controlpanelno' continue 66 | gettoken panel panels: panels, bind 67 | panelparse `=substr("`panel'",7,strlen("`panel'")-7)' 68 | loc panelvars`numpanels' `=s(panelvars)' 69 | loc numvars`numpanels'=s(numvars) 70 | loc labels`numpanels' `=s(labels)' 71 | if "`=s(graphopts)'"!="." loc graphopts`numpanels' `=s(graphopts)' 72 | if "`=s(title)'"!="." loc title`numpanels' `=s(title)' 73 | loc panelvars `panelvars' `panelvars`numpanels'' 74 | } 75 | if `controlpanelno'>`numpanels' loc ++numpanels 76 | 77 | //sort option 78 | if !inlist("`sort'","none","") { 79 | loc sortvar `sort' 80 | } 81 | 82 | //control main opt 83 | if "`main'"!="" { 84 | mainparse `main' 85 | loc mainspec `s(mainspec)' 86 | loc maingraphopts `s(graphopts)' 87 | } 88 | 89 | //levels option 90 | if "`level'"=="" loc level 90 95 91 | loc numlevel: word count `level' 92 | loc i=0 93 | foreach lev in `level' { 94 | loc ++i 95 | loc level`i'=`lev' 96 | } 97 | 98 | //Parse addcoef() option 99 | if "`addcoef'"!="" { 100 | addcoefparse `addcoef' 101 | loc namelistaddcoef `s(namelist)' 102 | loc paramaddcoef `s(param)' 103 | loc addcoeftitle `s(title)' 104 | loc samemodel `s(samemodel)' 105 | loc addcoefusing `s(using)' 106 | loc addcoefscalar `s(scalar)' 107 | loc addcoefgraphopts `s(graphopts)' 108 | tempfile addcoefdata 109 | } 110 | 111 | //parse addscalar() option 112 | 113 | if "`addscalar'"!="" { 114 | addscalarparse `addscalar' 115 | loc addscalar `s(namelist)' 116 | loc addscalartitle `s(title)' 117 | loc addscalargraphopts `s(graphopts)' 118 | } 119 | 120 | 121 | //Create dataset from estimates 122 | clear 123 | foreach var in estimate `panelvars' `sortvar' `addscalar' modelno { 124 | cap gen `var'=. 125 | } 126 | gen parm="" 127 | gen name="" 128 | foreach lev in `level' { 129 | gen min`lev'=. 130 | gen max`lev'=. 131 | } 132 | 133 | if "`using'"!="" { //if taking data from .ster file 134 | cap estimates describe using `using' 135 | if _rc!=0 { 136 | noi di in red "Using file `using'.ster not found." 137 | exit 301 138 | } 139 | forvalues i=1/`=r(nestresults)' { 140 | estimates use `using', number(`i') 141 | loc modname `e(estimates_title)' 142 | if "`anything'"!="" { 143 | loc numwordstitle: word count `modname' 144 | if `numwordstitle'>1 { 145 | noi di in red "Use only one-word names for estimates titles when storing them on disk and specifying namelist. Estimate `modname' contains more than one word." 146 | exit 301 147 | } 148 | } 149 | if `controlpanelno'!=0 loc colnames: colnames e(b) 150 | else if "`addcoef'"!="" loc colnames `param' `paramaddcoef' 151 | else loc colnames `param' 152 | savecoefs `colnames', level(`level') scalars(`panelvars' `sortvar' `addscalar') 153 | replace modelno=`i' if modelno==. 154 | replace name="`modname'" if name=="" 155 | } 156 | if "`anything'"!="" { 157 | gen `keepvar'==0 158 | foreach okname in `anything' { 159 | replace `keepvar'=1 if strmatch(name,"`okname'")==1 160 | } 161 | drop if `keepvar'==0 162 | drop `keepvar' 163 | levelsof name, local(namelist) 164 | } 165 | 166 | } 167 | 168 | else { //when taking estimates from memory 169 | est dir `anything' 170 | loc namelist `=r(names)' 171 | loc i=0 172 | foreach est in `namelist' { 173 | loc ++i 174 | est restore `est' 175 | if `controlpanelno'!=0 loc colnames: colnames e(b) 176 | else if "`addcoef'"!=""&"`samemodel'"!="" loc colnames `param' `paramaddcoef' 177 | else loc colnames `param' 178 | savecoefs `colnames', level(`level') scalars(`panelvars' `sortvar' `addscalar') 179 | replace name="`est'" if name=="" 180 | replace modelno=`i' if modelno==. 181 | } 182 | } 183 | 184 | drop if parm=="_cons" 185 | if "`drop'"!="" { 186 | foreach dr in `drop' { 187 | drop if strpos(parm,"`dr'")>0 188 | } 189 | } 190 | 191 | 192 | //automatic control panel 193 | if `controlpanelno'!=0 { 194 | 195 | replace parm=regexr(parm,"(\d+)\.","i.") if parm!="`param'"&parm!="`paramaddcoef'" 196 | replace parm=regexr(parm,"(\d+)b\.","i.") if parm!="`param'"&parm!="`paramaddcoef'" 197 | 198 | bys name parm: drop if _n>1 199 | 200 | if "`samemodel'"=="" levelsof parm if parm!="`param'", local(parmlist) clean 201 | else levelsof parm if parm!="`param'"&parm!="`paramaddcoef'", local(parmlist) clean 202 | 203 | gen byte `bin'=. 204 | loc c=0 205 | foreach parm in `parmlist' { 206 | loc ++c 207 | replace `bin'=parm=="`parm'" 208 | tempname var`c' 209 | bys name: egen `var`c''=max(`bin') 210 | loc panelvars`controlpanelno' `panelvars`controlpanelno'' `var`c'' 211 | } 212 | local numvars`controlpanelno': word count `panelvars`controlpanelno'' 213 | if "`controltitle"!="" loc title`controlpanelno' `controltitle' 214 | if "`controllabels'"!="" loc labels`controlpanelno' `controllabels' 215 | else loc labels`controlpanelno' `parmlist' 216 | loc graphopts`controlpanelno' `controlgraphopts' 217 | 218 | 219 | } 220 | 221 | if "`samemodel'"!="" keep if inlist(parm,"`param'","`paramaddcoef'") 222 | else keep if parm=="`param'" 223 | 224 | //addcoef option 225 | if "`addcoef'"!="" { 226 | if "`samemodel'"=="" { 227 | if "`addcoefusing'"!="" { //when taking addcoef estimates from file 228 | cap estimates describe using `addcoefusing' 229 | if _rc!=0 { 230 | noi di in red "Using file `addcoefusing'.ster not found." 231 | exit 301 232 | } 233 | forvalues i=1/`=r(nestresults)' { 234 | estimates use `addcoefusing', number(`i') 235 | loc modname `e(estimates_title)' 236 | if "`namelistaddcoef'"!="" { 237 | loc numwordstitle: word count `e(estimates_title)' 238 | if `numwordstitle'>1 { 239 | noi di in red "Use only one-word names for estimates titles when storing them on disk and specifying namelist. Estimate `modname' contains more than one word." 240 | exit 301 241 | } 242 | } 243 | savecoefs `paramaddcoef', level(`level') 244 | replace modelno=`i' if modelno==. 245 | replace name="`modname'" if name=="" 246 | } 247 | if "`namelistaddcoef'"!="" { 248 | gen `keepvar'=0 249 | foreach okname in `anything' { 250 | replace `keepvar'=1 if strmatch(name,"`okname'")==1 251 | } 252 | drop if `keepvar'==0 253 | drop `keepvar' 254 | } 255 | 256 | } 257 | 258 | else { //when taking estimates from memory 259 | est dir `namelistaddcoef' 260 | loc namelistaddcoef `=r(names)' 261 | loc i=0 262 | foreach est in `namelistaddcoef' { 263 | loc ++i 264 | est restore `est' 265 | savecoefs `paramaddcoef', level(`level') 266 | replace name="`est'" if name=="" 267 | replace modelno=`i' if modelno==. 268 | } 269 | } 270 | } 271 | 272 | keep if inlist(parm,"`param'","`paramaddcoef'") 273 | gen n=1 if parm=="`param'" 274 | replace n=2 if parm=="`paramaddcoef'" 275 | keep min* max* estimate modelno name n parm `panelvars' `sortvar' `panelvars`controlpanelno'' 276 | reshape wide estimate min* max* parm name `panelvars' `sortvar' `panelvars`controlpanelno'', i(modelno) j(n) 277 | foreach var in name `panelvars' `sortvar' `panelvars`controlpanelno'' { 278 | drop `var'2 279 | rename `var'1 `var' 280 | } 281 | fvexpand min* max* estimate* parm* 282 | foreach var in `r(varlist)' { 283 | if substr("`var'",-1,1)=="1" rename `var' `=substr("`var'",1,strlen("`var'")-1)' 284 | else rename `var' `=substr("`var'",1,strlen("`var'")-1)'_a 285 | } 286 | } 287 | 288 | 289 | //finalize dataset 290 | if "`sort'"!="none" { 291 | if "`sort'"=="" sort estimate 292 | else sort `sortvar' 293 | } 294 | gen `spec'=_n 295 | 296 | loc Nspec=_N 297 | if "`save'"!="" save `save', replace 298 | 299 | //DROP ESTIMATES IF KEEP() option specified 300 | if "`keep'"!="" { 301 | if "`sort'"!="" { 302 | noi di in red "Do not combine sort() and keep() options - keep requires default sorting on estimate size." 303 | exit 301 304 | } 305 | gettoken keep1 keep: keep 306 | gettoken keep2 keep3: keep 307 | if `keep1'+`keep2'+`keep3'>=`Nspec' { 308 | noi di as text "Sum of number of specifications to keep specified in keep() is larger than or equal to the total number of specifications. Option keep() ignored". 309 | loc keep 310 | } 311 | else { 312 | tempvar dum run 313 | gen `r'=runiform() 314 | gen `dum'=name=="`mainspec'" 315 | sort `dum' `spec' 316 | bys `dum': gen `run'=_n 317 | gen `keepvar'=(`dum'==1|`run'<=`keep1'|`run'>=_N-`keep3') 318 | sort `keepvar' `r' 319 | bys `keepvar': replace `keepvar'=1 if _n<=`keep2'&`keepvar'==0 320 | drop if `keepvar'!=1 321 | sort estimate 322 | replace `spec'=_n 323 | su `spec' 324 | loc Nspec=_N 325 | if "`mainspec'"!="" { 326 | su `spec' if name=="`mainspec'" 327 | if r(mean)<=`=`keep1'+`keep2'' { 328 | loc xline2=`keep1'+`keep2'+1.5 329 | if r(mean)<=`keep1' loc xline1=`keep1'+1.5 330 | } 331 | } 332 | if "`mainspec'"==""|"`xline1'"=="" loc xline1=`keep1'+0.5 333 | if "`mainspec'"==""|"`xline2'"=="" loc xline2=`Nspec'-`keep3'+0.5 334 | loc xlines xline(`xline1', lpattern(dot)) xline(`xline2', lpattern(dot)) 335 | } 336 | } 337 | 338 | //Determine appropriate scatter symbol size 339 | loc msize `=4.5/`Nspec'' 340 | loc labsize `=min(`=7/`Nspec'',0.25)'in 341 | 342 | //Determine appropriate text size 343 | loc cols=`numlevel'+1 344 | if "`mainspec'"!="" { 345 | loc ++cols 346 | loc notifmain if name!="`mainspec'" 347 | loc scattermain (scatter estimate `spec' if name=="`mainspec'", mcolor(maroon) msize(`msize'in) msymbol(diamond) `maingraphopts') 348 | if "`addcoef'"!="" loc scattermain_a (scatter estimate_a `spec' if name=="`mainspec'", mcolor(maroon) msize(`msize'in) msymbol(diamond)`maingraphopts') 349 | loc labmain label(`=`numlevel'+2' "main") 350 | loc mainorder=`numlevel'+2 351 | } 352 | 353 | su min`level`numlevel'' 354 | loc ymin=r(min) 355 | 356 | if "`addcoef'"!="" { 357 | su min`level`numlevel''_a 358 | loc ymin_a=r(min) 359 | } 360 | 361 | if "`addscalar'"!="" { 362 | su `addscalar' 363 | loc minscalar=r(min) 364 | } 365 | 366 | if `numlevel'==2 loc orderci 1 2 367 | else loc orderci 1 368 | 369 | //Phantom labels, to align panels 370 | if `numpanels'>0 { 371 | forvalues pan=1/`numpanels' { 372 | foreach i in `labels`pan'' { 373 | loc phantomlabs `phantomlabs' `ymin' "`i'" 374 | loc phantomlabsscat `phantomlabsscat' 1 "`i'" 375 | if "`addcoef'"!="" loc phantomlabs_a `phantomlabs_a' `ymin_a' "`i'" 376 | if "`addscalar'"!="" loc phantomlabs_s `phantomlabs_s' `minscalar' "`i'" 377 | } 378 | } 379 | 380 | 381 | loc extraylabs ylabel(`phantomlabs', add custom labcolor(white%0) labsize(`labsize') angle(horizontal) tlcolor(white%0)) 382 | if "`addcoef'"!="" loc extraylabs_a ylabel(`phantomlabs_a', add custom labcolor(white%0) labsize(`labsize') angle(horizontal) tlcolor(white%0)) 383 | if "`addscalar'"!="" loc extraylabs_s ylabel(`phantomlabs_s', add custom labcolor(white%0) labsize(`labsize') angle(horizontal) tlcolor(white%0)) 384 | loc extraylabsscat ylabel(`phantomlabsscat', add custom labcolor(white%0) labsize(`labsize') angle(horizontal) tlcolor(white%0)) 385 | } 386 | 387 | loc j=0 388 | forvalues i=`numlevel'(-1)1 { 389 | loc ++j 390 | loc rbars `rbars' (rbar min`level`i'' max`level`i'' `spec', color(gs`=10+2*`i''%50) lwidth(none)) 391 | if "`addcoef'"!="" loc rbarsaddcoef (rbar min`level`i''_a max`level`i''_a `spec', color(gs`=10+2*`i''%50) lwidth(none)) `rbarsaddcoef' 392 | loc labels `labels' label(`j' "`level`i''% CI") 393 | loc varnamesci `varnamesci' min`level`i'' max`level`i'' 394 | } 395 | 396 | //Determine relative sizes of panels 397 | if "`title'"!="" loc ysizemain=4.3 398 | else loc ysizemain=4 399 | loc ysize=`ysizemain' 400 | if `numpanels'>0 { 401 | forvalues pan=1/`numpanels' { 402 | if "`title`pan''"=="" loc ysize`pan'=`msize'*(`numvars`pan''+1)+0.1 403 | else loc ysize`pan'=`msize'*(`numvars`pan''+1)+0.4 404 | loc ysize=`ysize'+`ysize`pan'' 405 | } 406 | } 407 | if "`addcoef'"!="" { 408 | if "`addcoeftitle'"!="" loc ysizeaddcoef =4.3 409 | else loc ysizeaddcoef=4 410 | loc ysize=`ysize'+`ysizeaddcoef' 411 | } 412 | 413 | if "`addscalar'"!="" { 414 | if "`addscalartitle'"=="" loc ysizeaddscalar=4 415 | else loc ysizeaddscalar=4.3 416 | loc ysize=`ysize'+`ysizeaddscalar' 417 | } 418 | 419 | //Plot estimates 420 | if "`ytitle'"=="" loc ytitle coefficient on `param' 421 | if `numpanels'>0|"`addcoef'"!=""|"`addscalar'"!="" { 422 | loc nodraw nodraw 423 | loc coefname `coefs' 424 | loc margins 0 0 0 0 425 | } 426 | else loc coefname speccurve 427 | twoway `rbars' (scatter estimate `spec' `notifmain', mcolor(black) msize(`msize'in) msymbol(circle)) `scattermain' /// 428 | , name(`coefname', replace) scheme(s2mono) xscale(range(0.5 `=`Nspec'+0.5')) /// 429 | xlabel(none) xtitle("") graphregion(color(white)) plotregion(lcolor(black)) `xlines' /// 430 | title(`title', size(0.3in)) ylabel(#6, nogrid) ytitle("`ytitle'") /// 431 | `extraylabs' `nodraw' plotregion(margin(0.5 0.5 0.5 0.5)) graphregion(margin(`margins')) /// 432 | yline(0, lpattern(dash)) fysize(`=150*`ysizemain'/`ysize'') legend(`labels' label(`=`numlevel'+1' "estimates") `labmain' cols(`cols') order(`mainorder' `=`numlevel'+1' `orderci') position(5) ring(0)) `graphopts' 433 | 434 | 435 | //plot specification panel(s) + control panel 436 | if `numpanels'>0 { 437 | if strpos("`graphopts'","ytitle("")")>0&strpos("`addcoefgraphopts'","ytitle("")")>0&strpos("`addscalargraphopts'","ytitle("")")>0 loc pad 438 | else loc pad pad 439 | 440 | loc j=0 441 | forvalues pan=1/`numpanels' { 442 | 443 | loc labels 444 | loc i=0 445 | foreach lab in `labels`pan'' { 446 | loc ++i 447 | loc labels `labels' `=`numvars`pan''+1-`i'' "`lab'" 448 | } 449 | 450 | loc two 451 | loc k=0 452 | foreach i in `panelvars`pan'' { 453 | loc ++j 454 | loc ++k 455 | cap gen y`j'=`=`numvars`pan''+1-`k'' 456 | levelsof `i', local(tmp) 457 | loc vals: word count `tmp' 458 | loc bin=0 459 | foreach val in `tmp' { 460 | if inlist(`val',0,1) loc bin=`bin'+1 461 | } 462 | if "`fill'"!="" loc fillstr |`i'==. 463 | if (`bin'==2&`vals'==2)|(`bin'==1&`vals'==1) { //plot scatters with dots 464 | loc two `two' (scatter y`j' `spec' if `i'==1&name!="`mainspec'", msymbol(circle) mcolor(black) msize(`msize'in) mlwidth(vthin)) /// 465 | (scatter y`j' `spec' if `i'==0`fillstr'&name!="`mainspec'", msymbol(circle_hollow) mlcolor(gs0) mcolor(white) msize(`msize'in) mlwidth(vthin)) /// 466 | (scatter y`j' `spec' if `i'==1&name=="`mainspec'", msymbol(circle) mcolor(maroon) msize(`msize'in) mlwidth(vthin)) /// 467 | (scatter y`j' `spec' if `i'==0`fillstr'&name=="`mainspec'", msymbol(circle_hollow) mlcolor(maroon) mcolor(white) msize(`msize'in) mlwidth(vthin)) 468 | } 469 | else { //plot scatters with numbers/values 470 | loc two `two' (scatter y`j' `spec' if name!="`mainspec'"&`i'!=., mlabel(`i') mlabpos(0) msymbol(i) mlabsize(`msize'in)) /// 471 | (scatter y`j' `spec' if name=="`mainspec'"&`i'!=., mlabel(`i') mlabpos(0) msymbol(i) mlabcolor(maroon) mlabsize(`msize'in)) 472 | } 473 | } 474 | 475 | tempvar plot`pan' 476 | twoway `two', nodraw ylabels(`labels', angle(horizontal) labsize(`labsize') nogrid) `extraylabsscat' /// 477 | scheme(s2mono) xscale(range(0.5 `=`Nspec'+0.5')) yscale(range(0.5 `=`numvars`pan''+0.5')) /// 478 | xlabel(none) xtitle("") legend(off) title(`title`pan'', size(0.3in)) /// 479 | plotregion(margin(0.5 0.5 0.5 0.5)) graphregion(margin(0 0 0 0)) /// 480 | graphregion(color(white)) plotregion(lcolor(black)) yscale(range(0.5 `=`numvars`pan''+0.5')) name(`plot`pan'', replace) fysize(`=150*`ysize`pan''/`ysize'') ysize(`ysize`pan'') ytitle("`pad'", color(white%0) ) `graphopts`pan'' 481 | 482 | loc scatters `scatters' `plot`pan'' 483 | } 484 | 485 | 486 | } 487 | 488 | //Plot additional coefficients or scalars from addcoef() or addscalar( 489 | if "`addcoef'"!="" { 490 | tempname addcoef1 491 | twoway `rbarsaddcoef' (scatter estimate_a `spec' `notifmain', mcolor(black) msize(`msize'in) msymbol(circle)) `scattermain_a' /// 492 | , name(`addcoef1', replace) scheme(s2mono) xscale(range(0.5 `=`Nspec'+0.5')) /// 493 | title(`addcoeftitle', size(0.3in)) xlabel(none) xtitle("") graphregion(color(white)) plotregion(lcolor(black)) `xlines' /// 494 | ylabel(#6, nogrid) ytitle("coefficient on `paramaddcoef'") plotregion(margin(0.5 0.5 0.5 0.5)) graphregion(margin(0 0 0 0)) /// 495 | `extraylabs_a' `nodraw' /// 496 | yline(0, lpattern(dash)) legend(off) fysize(`=150*`ysizeaddcoef'/`ysize'') `addcoefgraphopts' 497 | } 498 | 499 | if "`addscalar'"!="" { 500 | tempname addscalar1 501 | if "`mainspec'"!="" loc scattermain_s (scatter `addscalar' `spec' if name=="`mainspec'", mcolor(maroon) msize(`msize'in) msymbol(diamond)) 502 | twoway (scatter `addscalar' `spec' `notifmain', mcolor(black) msize(`msize'in) msymbol(circle)) `scattermain_s' /// 503 | , name(`addscalar1', replace) scheme(s2mono) xscale(range(0.5 `=`Nspec'+0.5')) /// 504 | title(`addscalartitle', size(0.3in)) xlabel(none) xtitle("") graphregion(color(white)) plotregion(lcolor(black)) `xlines' /// 505 | ylabel(#6, nogrid) ytitle("`addscalar'") plotregion(margin(0.5 0.5 0.5 0.5)) graphregion(margin(0 0 0 0)) `nodraw' /// 506 | yline(0, lpattern(dash)) legend(off) fysize(`=150*`ysizeaddscalar'/`ysize'') `addscalargraphopts' `extraylabs_s' 507 | } 508 | 509 | if `numpanels'>0|"`addcoef'"!=""|"`addscalar'"!="" graph combine `coefs' `scatters' `addcoef1' `addscalar1', cols(1) graphregion(color(white)) imargin(0 0 0 0) ysize(`ysize') name(speccurve, replace) 510 | 511 | 512 | 513 | //Add resulting table to r(table) 514 | rename `spec' specno 515 | tempname table 516 | mkmat specno modelno estimate `varnamesci' `panelvars' `panelvars`controlpanelno'' `addscalar', matrix(`table') rownames(name) 517 | return matrix table=`table' 518 | 519 | } 520 | 521 | end 522 | 523 | 524 | //panelparse version 0.1 525 | 526 | program panelparse, sclass 527 | syntax namelist, [labels(string)) title(string) graphopts(string)] 528 | loc numvars: word count `namelist' 529 | sret local numvars=`numvars' 530 | sret local panelvars `namelist' 531 | while `"`labels'"'!="" { 532 | gettoken lab labels: labels 533 | loc labs `labs' `lab' 534 | gettoken name namelist: namelist 535 | } 536 | while "`namelist'"!="" { 537 | gettoken name namelist: namelist 538 | loc labs `labs' `name' 539 | } 540 | sret local labels `labs' 541 | sret local title `title' 542 | 543 | sret local graphopts `graphopts' 544 | end 545 | 546 | //mainparse 547 | 548 | program mainparse, sclass 549 | syntax namelist, [graphopts(string)] 550 | loc nummain: word count `namelist' 551 | if `nummain'>1 { 552 | noi di in red "Specify only one word or number in main(), corresponding to the name or number of the main specification." 553 | exit 301 554 | } 555 | sret local mainspec `namelist' 556 | sret local graphopts `graphopts' 557 | end 558 | 559 | //addcoefparse 560 | 561 | program addcoefparse, sclass 562 | syntax [anything] [using/], param(name) [title(string) graphopts(string) scalar(name)] 563 | if strpos("`anything'","samemodel")>0 { 564 | loc numnames: word count `anything' 565 | if `numnames'>1 { 566 | noi di in red "When specifying samemodel in addcoef(), do not specify a namelist - addcoef() coefficients are `param' from main models." 567 | exit 301 568 | } 569 | loc namelist 570 | loc samemodel samemodel 571 | } 572 | sret local param `param' 573 | sret local namelist `anything' 574 | sret local title `title' 575 | sret local graphopts `graphopts' 576 | sret local samemodel `samemodel' 577 | sret local using `using' 578 | sret local scalar `scalar' 579 | 580 | end 581 | 582 | //addscalarparse 583 | 584 | program addscalarparse, sclass 585 | syntax name, [title(string) graphopts(string)] 586 | foreach str in namelist title graphopts { 587 | sret local `str' ``str'' 588 | } 589 | 590 | end 591 | 592 | 593 | 594 | //savecoefs 595 | program savecoefs 596 | syntax [anything], level(numlist min=1 max=2 integer >0 <100) [scalars(namelist)] 597 | loc k=0 598 | foreach lev in `level' { 599 | loc ++k 600 | tempname r`lev' 601 | eret di, level(`lev') 602 | mat `r`lev''=r(table) 603 | loc level`k'=`lev' 604 | } 605 | 606 | if "`anything'"=="" loc anything: colnames e(b) 607 | loc nparms: word count `anything' 608 | loc N=_N 609 | set obs `=_N+`nparms'' 610 | foreach var in `scalars' { 611 | replace `var'=e(`var') if `var'==. 612 | } 613 | loc j=0 614 | foreach parm in `anything' { 615 | loc ++j 616 | cap replace parm="`parm'" in `=`N'+`j'' 617 | cap replace estimate=`r`level1''[`=rownumb(`r`level1'',"b")',`=colnumb(`r`level1'',"`parm'")'] in `=`N'+`j'' 618 | cap replace min`level1'=`r`level1''[`=rownumb(`r`level1'',"ll")',`=colnumb(`r`level1'',"`parm'")'] in `=`N'+`j'' 619 | cap replace max`level1'=`r`level1''[`=rownumb(`r`level1'',"ul")',`=colnumb(`r`level1'',"`parm'")'] in `=`N'+`j'' 620 | if `k'==2 { 621 | cap replace min`level2'=`r`level2''[`=rownumb(`r`level2'',"ll")',`=colnumb(`r`level2'',"`parm'")'] in `=`N'+`j'' 622 | cap replace max`level2'=`r`level2''[`=rownumb(`r`level2'',"ul")',`=colnumb(`r`level2'',"`parm'")'] in `=`N'+`j'' 623 | } 624 | } 625 | 626 | end 627 | 628 | 629 | 630 | 631 | --------------------------------------------------------------------------------