*! version 1.4.4 PR 10dec2007. /* History of ice 1.4.4 09nov2007 -vce()- option added 1.4.3 10oct2007 Update to perfect prediction (implemented in -uvis-) nopp option added to suppress avoidance of perfect prediction bug. nowarning option added to suppress warning messages. 1.4.2 03aug2007 Change to syntax for saving file (old syntax still works). 1.4.1 27mar2007 Checking for perfect prediction in logistic regression moved to -uvis-. 1.4.0 16mar2007 Handling of `using' filename simplified. 1.3.3 15nov2006 Check for collinearity among covariates when running -uvis- added. Ian White's -auglogit- fix for logistic models with perfect prediction added. orderasis option added. 1.3.2 01nov2006 mitools functionality removed, not needed with mim. Default impid and obsid changed from _j, _i to _mj, _mi (for mim). 1.3.1 11sep2006 Bug in interval censoring, affecting left censoring, fixed. 1.3.0 05jul2006 Interval censoring implemented via interval() option. Conditional imputation extended to include imputation for subpopulation. Dumping data when errror found in number of missing values on output. dropmissing option implemented. svy option removed (would require much care to implement correctly). 1.2.2 05jun2006 substitute() option improved by allowing implicit passive() 1.2.1 31may2006 round() option added listsort found to have obscure bug and replaced with listsort3 (Julie Siebens reported problem). 1.2.0 24may2006 Conditional imputation implemented (conditional() option). Survey regression imputation implemented (svy option). 1.1.4 21feb2006 Compound quotes needed around all temporary filenames 1.1.3 01feb2006 Saving original data to output file of imputations as _j = 0. 1.1.2 16jan2006 List of variables in on() option not displayed on output - fixed. 1.1.1 00nov2005 RELEASED IN SJ 5-4 1.1.1 23sep2005 Better error trapping for passive() and substitute() options. 1.1.0 23aug2005 Replace -draw- option with -match-. Default becomes draw. Trace option documented, now has argument for filename. Report number of rows with 0, 1, 2, ... missing values. Arrange variables in increasing order of missingness when imputing. Split ties at random when more than one observation satisfies the prediction matching criterion 1.0.4 21jul2005 Trap and report error when running uvis 1.0.3 08jun2005 Tidy up display of equations when have multiple lines (long equations) 1.0.3 03jun2005 Silence file load/save 1.0.2 20may2005 Changed files containing imputations to tempfiles (standalone mode) (Angela Wood reported problem). 1.0.1 04may2005 Added a trace to a file (undocumented in help file). 1.0.0 18apr2005 First release, based on mice. History of mice 1.0.3 13apr2005 Minor tidying up, including recode of ChkIn and deletion of strdel. Check if prediction equations have a variable on both sides. 1.0.2 17feb2005 Added code to take care of inherited missingness of passive variables robustly. 1.0.1 21jan2005 Added display of regression command in showing prediction equations. 1.0.0 20jan2005 First release, based on mvis2/_mvis2. History of mvis 1.1.0 18jan2005 categoric() option removed. New options dryrun, passive(), substitute(), eq() added. Improvements to output showing actual prediction equations. 1.0.5 19nov2004 Delete dummy variables for categoric() variables with no missing data from output file Found problem with bsample in Stata 7 with "if" clause and boot option. Revert to Stata 8 for mvis, _mvis and uvis. 1.0.4 18nov2004 Weights not working (syntax error), fixed. 1.0.3 16nov2004 Categoric() option added to deal with unordered categoric covariates, updated default handling of such variables 1.0.2 16oct2004 Saving, using etc of file safest with compound quotes, fixed. */ program define ice, rclass version 8 syntax varlist(min=2 numeric) [if] [in] [aweight fweight pweight iweight] [using/], /// [ BOot DROPmissing DRYrun m(int 1) MAtch ORDerasis nopp SAVing(string) replace Seed(int 0) noWARNing * ] * Check for _I* variables, could be created by xi: capture describe _I* if _rc==0 & "`warning'"!="nowarning" { di as err _n "Warning: _I* variables detected in the dataset - was xi: used?" di as inp "Use of xi: with ice is liable to give incorrect results." di as inp "If you wish to model categorical covariates with dummy" di as inp "variables, please recalculate the dummies via the passive() option" di as inp "and use the substitute() option to identify the dummies as predictors." _n } if `"`saving'"'!="" { if `"`using'"'!="" { di as err "cannot specify using and saving() together" exit 198 } tokenize `"`saving'"', parse(" ,") if `"`2'"'!="" { if `"`2'"'!="," | `"`4'"'!="" error 198 if `"`3'"'!="" { if `"`3'"'!="replace" error 198 local replace replace } } local using `1' } * Check if there are variables called boot and/or match if "`boot'"=="boot" { cap confirm var boot if _rc local options `options' boot(`varlist') else local options `options' boot(boot) } if "`match'"=="match" { cap confirm var match if _rc local options `options' match(`varlist') else local options `options' match(match) } if `seed'>0 set seed `seed' local first first tempname fn erules if "`dryrun'"!="" { if `"`using'"'=="" { local using `fn' } _ice `varlist' `if' `in' [`weight' `exp'] using `using', `options' `orderasis' first dryrun di as text _n "End of dry run. No imputations were done, no files were created." exit } if `m'<1 { di as err "number of imputations must be 1 or more" exit 198 } if `"`using'"'=="" { if "`dryrun'"=="" { di as err "saving() required" exit 100 } } else if "`replace'"=="" confirm new file `"`using'"' preserve if "`dropmissing'"!="" { * Drop obs not in the estimation sample i.e. that would not be imputed marksample touse, novarlist if "`cc'`on'"!="" { markout `touse' `cc' `on' } tempvar rmiss egen long `rmiss'=rmiss(`varlist') if `touse' local nv: word count `varlist' // #covariates qui count if `rmiss'==`nv' | `touse'==0 if r(N)>0 { di as txt _n "[dropping " r(N) " observations not in the estimation sample]" drop if `rmiss'==`nv' | `touse'==0 } drop `touse' `rmiss' } // Save original data, and create and save m imputed datasets local original original forvalues i=0/`m' { tempfile fn`i' _ice `varlist' `if' `in' [`weight' `exp'] using `"`fn`i''"', /// `options' `orderasis' `first' `original' `warning' `pp' local original if `i'>0 { local first di as text `i' ".." _cont } } // Join files of imputations vertically. Include original data as _mj=0 quietly { local J _mj forvalues j=0/`m' { * could automate this part use `"`fn`j''"', clear chkrowid local I `s(I)' if "`I'"=="" { * create row number local I _mi cap drop `I' gen long `I'=_n lab var `I' "obs. number" } cap drop `J' gen int `J'=`j' lab var `J' "imputation number" save `"`fn`j''"', replace } use `"`fn0'"', clear // load original data plus impid and obsid variables forvalues j=1/`m' { append using `"`fn`j''"' } char _dta[mi_id] `I' } save `"`using'"', `replace' end program define chkrowid, sclass version 8 local I: char _dta[mi_id] if "`I'"=="" exit cap confirm var `I' if _rc exit sret local I `I' end *! Based on _mvis2 version 1.0.2 PR 19jan2005. program define _ice, rclass version 8 syntax varlist(min=2 numeric) [if] [in] [aw fw pw iw] using/, /// [ BOot(varlist) CC(varlist) CMd(string) CYcles(int 10) noCONStant MAtch(varlist) /// CONDitional(string) DRYrun EQ(string) first Genmiss(string) Id(string) INTerval(string) noWARNing /// ON(varlist) ORDerasis original PASsive(string) nopp /// ROUnd(string) noSHoweq SUBstitute(string) TRace(string) vce(passthru) ] quietly if "`original'"!="" { * Save original data save `"`using'"', replace exit } local nvar: word count `varlist' if "`id'"!="" { confirm new var `id' } else local id _mi preserve tempvar touse order quietly { marksample touse, novarlist if "`cc'`on'"!="" { markout `touse' `cc' `on' } * Record sort order gen long `order'=_n lab var `order' "obs. number" * For standard operation (no `on' list), disregard any completely missing rows in varlist, among marked obs if "`on'"=="" { tempvar rmis egen int `rmis'=rmiss(`varlist') if `touse'==1 count if `rmis'==0 replace `touse'=0 if `rmis'==`nvar' replace `rmis'=. if `rmis'==`nvar' lab var `rmis' "#missing values" if "`first'"!="" & "`showeq'"=="" noi tab `rmis', missing drop `rmis' } * Deal with weights frac_wgt `"`exp'"' `touse' `"`weight'"' local wgt `r(wgt)' * Sort out cmds (not checking if each cmd is valid - any garbage may be entered) if "`cmd'"!="" { * local cmds "regress logistic logit ologit mlogit" detangle "`cmd'" cmd "`varlist'" forvalues i=1/`nvar' { if "${S_`i'}"!="" { local cmd`i' ${S_`i'} } } } * Rounding of imputed values if "`round'"!="" { detangle "`round'" round "`varlist'" forvalues i=1/`nvar' { if "${S_`i'}"!="" { local round`i' ${S_`i'} confirm num `round`i'' } } } * Default for all uvis operations is nomatch, meaning draw if "`match'"!="" { tokenize `match' while "`1'"!="" { ChkIn `1' "`varlist'" if `s(k)'>0 { local match`s(k)' match } mac shift } } if "`boot'"!="" { tokenize `boot' while "`1'"!="" { ChkIn `1' "`varlist'" if `s(k)'>0 { local boot`s(k)' boot } mac shift } } local anyerr 0 if `"`passive'"'!="" { tempvar passmiss /* Defines vars that are functions or transformations of others in varlist. They are (may be) "passively imputed". "\" is an expression separator. Default is comma. Comma may not always be appropriate (i.e. may appear in an expression). */ detangle "`passive'" passive "`varlist'" \ local haserr 0 forvalues i=1/`nvar' { if "${S_`i'}"!="" { local exp`i' ${S_`i'} ParsExp `exp`i'' local exclude `s(result)' if "`exclude'"!="" { * Count missingness of this passive variable egen int `passmiss'=rmiss(`exclude') if `touse' count if `passmiss'>0 & `touse'==1 local nimp`i'=r(N) if `nimp`i''==0 { local v: word `i' of `varlist' noi di as err "passive definition `v' = (${S_`i'}) redundant: `exclude' has no missing data." local ++haserr } drop `passmiss' } } } if `haserr'>0 { di as err "`haserr' error(s) found in option " as inp "passive(`passive')" local anyerr 1 } } if "`substitute'"!="" { * defines vars that are to be substituted in the recalc context detangle "`substitute'" substitute "`varlist'" local haserr 0 forvalues i=1/`nvar' { if "${S_`i'}"!="" { local sub`i' ${S_`i'} local v: word `i' of `varlist' count if missing(`v') & `touse'==1 if r(N)==0 { noi di as err "substitute for variable `v' redundant: `v' has no missing data." local ++haserr } * check for elements of sub`i' being already defined as passive unab sub`i': `sub`i'' tokenize `sub`i'' while "`1'"!="" { ChkIn `1' "`varlist'" local pass `s(k)' if "`exp`pass''"=="" { // not a passive var yet: define it as such sum `v' if `1'>0 & !missing(`1') & `touse'==1, meanonly local min=r(min) local max=r(max) if r(min)==r(max) local exp`pass' (`v'==`min') else local exp`pass' (`v'>=`min')&(`v'<=`max') } mac shift } } } if `haserr'>0 { noi di as err "`haserr' error(s) found in option " as inp "substitute(`substitute')" local anyerr 1 } } if "`conditional'"!="" { * defines vars that are to be conditioned on detangle "`conditional'" conditional "`varlist'" local haserr 0 forvalues i=1/`nvar' { if "${S_`i'}"!="" { local cond`i' ${S_`i'} gettoken cond cutoff: cond`i' local cutoff=trim("`cutoff'") local v: word `i' of `varlist' count if missing(`v') & `touse'==1 local m1=r(N) if `m1'==0 { noi di as err _n "conditioning for variable `v' redundant - no missing data." local ++haserr } if substr("`cutoff'",1,1)=="@" { replace `v'=. if `cond'==0 // guarantees fewer missing values in `cond' than in `cutoff' sum `v' if `cond'==1 & `touse'==1, meanonly local min`i'=r(min) // next value of v above cutoff } else { count if `touse'==1 & missing(`cond') local m2=r(N) if `m2'>=`m1' { noi di as err _n "conditioning variable (`cond') must have fewer" noi di as err "missing values than conditional variable (`v')" local ++haserr } } * Check validity of conditioning/conditioned variables count if `touse'==1 & missing(`cond') & !missing(`v') if r(N)>0 { noi di as err _n "conditioning variable (`cond') missing when conditional variable (`v') observed in " r(N) " cases" local ++haserr } } } if `haserr'>0 { if `haserr'>1 local s s else local s noi di as err _n "`haserr' error`s' found relating to option " as inp "conditional(`conditional')" local anyerr 1 } } if "`interval'"!="" { * defines interval censored vars in threes: ll, ul, y detangle "`interval'" interval "`varlist'" local haserr 0 forvalues i=1/`nvar' { if "${S_`i'}"!="" { local int`i' ${S_`i'} gettoken ll ul:int`i' count if `ll'>`ul' & !missing(`ll') & !missing(`ul') & `touse'==1 if r(N)>0 { noi di as err "invalid interval(), some values of `ll' > `ul'" exit 198 } local cmd`i' intreg * Mark ll and ul as interval limits ChkIn `ll' "`varlist'" local ll `s(k)' local islimit`ll' yes ChkIn `ul' "`varlist'" local ul `s(k)' local islimit`ul' yes } } if `haserr'>0 { noi di as err _n "`haserr' error(s) found in option " as inp "conditional(`conditional')" local anyerr 1 } } if `"`eq'"'!="" { * defines equations specified vars. detangle "`eq'" equation "`varlist'" forvalues i=1/`nvar' { if "${S_`i'}"!="" { local Eq`i' ${S_`i'} * Check that eq vars are in mainvarlist tokenize `Eq`i'' while "`1'"!="" { ChkIn `1' "`varlist'" mac shift } } } } if `anyerr' { di as err _n "specification error(s) found." exit 198 } count if `touse' local n=r(N) /* Count potentially imputable missing values for each variable, and where necessary create an equation for each */ local to_imp 0 // actual number of vars with missing values to be imputed local recalc 0 // number of passively imputed vars to be recalculated tempvar xtmp // temporary holding area local nimp // list of number of missing values for each variable forvalues i=1/`nvar' { local xvar: word `i' of `varlist' local x`i' `xvar' if "`int`i''"!="" { gettoken ll ul: int`i' local ismissing ((`ll'<`ul') | missing(`ll') | missing(`ul')) & `touse'==1 local labmissing 1 if `xvar' missing or `ll'<`ul', 0 otherwise } else { local ismissing missing(`xvar') & `touse'==1 local labmissing 1 if `xvar' missing, 0 otherwise } if "`genmiss'"!="" { tempvar mvar`i' gen byte `mvar`i''=`ismissing' lab var `mvar`i'' "`labmissing'" } * Create prediction equation for each active variable count if `ismissing' if r(N)>0 & `"`exp`i''"'=="" & "`islimit`i''"=="" { local nimp`i'=r(N) * active var: has missing obs or is interval censored, not passive local ++to_imp local main`i' 1 * Keep missingness of the original variable tempvar miss`i' if "`genmiss'"!="" gen byte `miss`i''=`mvar`i'' else gen byte `miss`i''=`ismissing' * Define equation for this variable - user definition from Eq() takes precedence if "`Eq`i''"!="" { local eq`i' `Eq`i'' } else { * Remove variable from mainvarlist local eq`i': list varlist - xvar } if "`cmd`i''"=="" { /* Assign default cmd for vars not so far accounted for. cmd is relevant only for vars requiring imputation, i.e. with >=1 missing values. Use logit if 2 distinct values, mlogit if 3-5, otherwise regress. */ inspect `xvar' if `touse' local nuniq=r(N_unique) if `nuniq'==1 { noi di as err "only 1 distinct value of `xvar' found" exit 2000 } if `nuniq'==2 { count if `xvar'==0 & `touse'==1 if r(N)==0 { noi di as err "variable `xvar' unsuitable for imputation," noi di as err "binary variables must include at least one 0 and one non-missing value" exit 198 } local cmd`i' logit } else if `nuniq'<=5 { local cmd`i' mlogit } else local cmd`i' regress } if "`cmd`i''"=="mlogit" { * With mlogit, if xvar carries a score label, * drop it since it causes prediction problems local xlab: value label `xvar' capture label drop `xlab' } if "`on'"=="" { * Initially fill missing obs if "`int`i''"!="" { replace `xvar'=. replace `xvar'=`ll' if !missing(`ll') & missing(`ul') replace `xvar'=`ul' if missing(`ll') & !missing(`ul') replace `xvar'=(`ll'+`ul')/2 if !missing(`ul') & !missing(`ll') } sampmis `xtmp'=`xvar' replace `xvar'=cond(`touse'==0, ., `xtmp') drop `xtmp' } else replace `xvar'=. if `touse'==0 local lab`i' `xvar' imput.`suffix' (`nimp`i'' values) } else { local main`i' 0 if "`nimp`i''"=="" { // may have been set earlier by consideration of ParsExp local nimp`i'=r(N) } if `"`exp`i''"'!="" { if "`Eq`i''"!="" { noi di as err "equation" as input " `xvar':`Eq`i'' " /// as err "invalid, `xvar' is passively imputed" exit 198 } local ++recalc } } local nimp `nimp' `nimp`i'' } if `to_imp'==0 { noi di as err _n "All relevant cases are complete, no imputation required." return scalar N=`n' return scalar imputed=0 exit 2000 } * Remove passivevars, intvars and conditionalvars from equations as necessary forvalues i=1/`nvar' { if `"`exp`i''"'!="" { ParsExp `exp`i'' local exclude `s(result)' * remove current passivevar from each relevant equation local passive `x`i'' tokenize `exclude' while "`1'"!="" { * identify which variable in mainvarlist we are looking at ChkIn `1' "`varlist'" local index `s(k)' * Remove `passive' from equation of variable * whose index in mainvarlist is `index' * (only allowed to be done if there is no * user equation Eq`' for var #`index') if "`eq`index''"!="" & "`Eq`index''"=="" /// local eq`index': list eq`index' - passive /* `1' with index `index' is identified as a source variable for `passive'. Is it cond on anything? If so, remove passive variable from equation for that something. */ if "`cond`index''"!="" { local cond: word 1 of `cond`index'' ChkIn `cond' "`varlist'" local cond `s(k)' if "`eq`cond''"!="" & "`Eq`cond''"=="" /// local eq`cond': list eq`cond' - passive } mac shift } } if `"`cond`i''"'!="" { * remove conditionalvar from each relevant prediction equation * identify which variable in mainvarlist we are looking at gettoken cond cutoff: cond`i' if substr(trim("`cutoff'"), 1, 1)!="@" { forvalues j=1/`nvar' { if "`Eq`j''"=="" & "`eq`j''"!="" { local eq`j': list eq`j' - cond } } } * identify conditioning var in mainvarlist ChkIn `cond' "`varlist'" local index `s(k)' * Remove conditioned var from equation for conditioning var if "`eq`index''"!="" & "`Eq`index''"=="" { local eq`index': list eq`index' - x`i' } } if "`int`i''"!="" { * remove ll and ul from each relevant prediction equation forvalues j=1/`nvar' { if "`Eq`j''"=="" & "`eq`j''"!="" { local eq`j': list eq`j' - int`i' } } * Remove equations for ll and ul gettoken ll ul: int`i' ChkIn `ll' "`varlist'" local ll `s(k)' if "`Eq`ll''"=="" local eq`ll' "[Lower bound for `x`i'']" ChkIn `ul' "`varlist'" local ul `s(k)' if "`Eq`ul''"=="" local eq`ul' "[Upper bound for `x`i'']" } } if "`substitute'"!="" { forvalues i=1/`nvar' { if `main`i'' & "`sub`i''"!="" { * substitute for this variable in all equations where it is a covariate forvalues j=1/`nvar' { if `main`j'' & (`j'!=`i') & "`Eq`j''"=="" { local res: list eq`j' - x`i' * substitute sub`i' if necessary i.e. if not already there tokenize `sub`i'' while "`1'"!="" { cap ChkIn `1' "`res'" if "`s(k)'"=="0" { local res `res' `1' } mac shift } local eq`j' `res' } } } } } * Show prediction equations at first imputation if "`first'"!="" { local errs 0 local longstring 55 // max display length of variables in equation local off 13 // blanks to col 13 on continuation lines if "`showeq'"=="" { noi di as text _n " Variable {c |} Command {c |} Prediction equation" _n /// "{hline 12}{c +}{hline 9}{c +}{hline `longstring'}" } forvalues i=1/`nvar' { if "`on'"!="" { local eq `on' local formatoutput 1 } else if "`exp`i''"!="" & `nimp`i''>0 { local eq "[Passively imputed from `exp`i'']" local formatoutput 0 } else if "`eq`i''"=="" { local eq "[No missing data in estimation sample]" local formatoutput 0 } else { local eq `eq`i'' local formatoutput 1 } if "`showeq'"=="" { if `formatoutput' { formatline, n(`eq') maxlen(`longstring') local nlines=r(lines) forvalues j=1/`nlines' { if `j'==1 noi di as text %11s abbrev("`x`i''",11) /// " {c |} " %-8s "`cmd`i''" "{c |} `r(line`j')'" else noi di as text _col(`off') /// "{c |}" _col(23) "{c |} `r(line`j')'" } } else noi di as text %11s abbrev("`x`i''",11) /// " {c |} " %-8s "`cmd`i''" "{c |} `eq'" } // Check for invalid equation - xvar on both sides if "`eq`i''"!="" { if `: list x`i' in eq`i'' { noi di as err "Error!" as inp " `x`i''" /// as err " found on both sides of prediction equation" local ++errs } } } if "`showeq'"=="" { noi di as text "{hline 12}{c BT}{hline 9}{c BT}{hline `longstring'}" } if "`on'"!="" noi di as text "Note: on() option selected" if `errs' { di as err _n `errs' " error(s) found. Consider using the passive() option to fix the problem" exit 198 } if "`dryrun'"!="" { exit } noi di as text _n "Imputing " _cont } if `to_imp'==1 | "`on'"!="" { local cycles 1 } * Update recalculated variables if `"`passive'"'!="" & `recalc'>0 { forvalues i=1/`nvar' { if "`exp`i''"!="" { replace `x`i''=`exp`i'' } } } * Impute sequentially `cycles' times by regression switching (van Buuren et al) tempvar y imputed /* Sort variables on number of missing values, from low to high numbers. Of benefit to the mice algorithm since vars with less missingness get imputed first. */ if "`orderasis'"!="" { // Use vars in order entered by user forvalues i=1/`nvar' { local r`i' `i' } } else { listsort3 "`nimp'" forvalues i=1/`nvar' { local r`i' `s(index`i')' } } if `"`trace'"'!="" { tempname tmp * create names local postvl cycle forvalues r=1/`nvar' { local i `r`r'' // antirank: vars with small #missing come first if `main`i'' local postvl `postvl' `x`i''_mean } postfile `tmp' `postvl' using `"`trace'"', replace } if "`conditional'"!="" tempvar imputed2 forvalues j=1/`cycles' { if `"`trace'"'!="" local posts (`j') forvalues r=1/`nvar' { local i `r`r'' // antirank, ensuring vars with small #missing come first if `main`i'' { * Each var is reimputed based on imputed values of other vars local type: type `x`i'' gen `type' `y'=`x`i'' if `miss`i''==0 & `touse'==1 if "`on'"=="" local vars `eq`i'' else local vars `on' // Check and report collinearity in `vars' _rmcoll `vars' if `touse' local vars2 `r(varlist)' local dropped: list vars - vars2 if "`dropped'"!="" { noi di as err _n "[Note: in regression for " /// as inp "`x`i''" as err ", dropping " /// as inp "`dropped'" as err " due to collinearity]" local vars `vars2' } if "`int`i''"!="" local yvarlist `int`i'' else local yvarlist `y' * uvis is derived from uvisamp4.ado if "`cond`i''"!="" { /* For conditioning var=0/1, draw for y and reject if y does not satisfy the <> cutoff requirement; cutoff value or var is in second word of cond`i' */ gen `imputed'=. gettoken cond cutoff: cond`i' local cutoff=trim("`cutoff'") if substr("`cutoff'",1,1)=="@" { local cutoff=substr("`cutoff'", 2, .) cap uvis `cmd`i'' `y' `vars' `cond' `wgt' if `touse'==1 & `cond'==1, /// gen(`imputed2') `boot`i'' `match`i'' `constant' `pp' `vce' local haserr=_rc local pp_cmd `r(pp_cmd)' if `haserr'==0 { replace `imputed'=cond(`cond'==0, `cutoff', `imputed2') replace `imputed'=`min`i'' if `cond'==1 &`imputed2'<=`cutoff' drop `imputed2' } } else { local done 0 while !`done' { cap uvis `cmd`i'' `y' `vars' `wgt' if `touse'==1, /// gen(`imputed2') `boot`i'' `match`i'' `constant' `pp' `vce' local haserr=_rc local pp_cmd `r(pp_cmd)' if `haserr'==0 { replace `imputed'=`imputed2' if `touse'==1 /// & ((`cond'==0 & `imputed2'<=`cutoff') | /// (`cond'==1 & `imputed2'>`cutoff')) count if `touse'==1 & missing(`imputed') if r(N)==0 local done 1 drop `imputed2' } } } } else { cap uvis `cmd`i'' `yvarlist' `vars' `wgt' if `touse', /// gen(`imputed') `boot`i'' `match`i'' `constant' `pp' `vce' local haserr=_rc local pp_cmd `r(pp_cmd)' /* (perfect prediction check moved to pp_check in uvis) */ } if "`pp_cmd'"!="" & "`warning'"!="nowarning" { noi di as txt _n(2) "[Perfect prediction detected:" _c if "`pp'"!="nopp" noi di as txt " using aug`pp_cmd' to impute " as res "`x`i''" as txt "]" else noi di as txt " no action taken]" } if `haserr' { noi di as err _n(2) "Error #`haserr' encountered while running -uvis-" noi di as err "I detected a problem with running uvis with command `cmd`i'' on response `x`i''" noi di as err "and covariates `vars'." noi di as err _n "The offending command resembled:" noi di as err `"uvis `cmd`i'' `x`i'' `vars' `wgt', `boot`i'' `match`i'' `constant' `pp' `vce'"' _n if "`cmd`i''"=="mlogit" { noi di as inp "With mlogit, try combining categories of `x`i'', or if appropriate, use ologit" _n } error `haserr' } if "`round`i''"!="" replace `imputed'=round(`imputed',`round`i'') if missing(`y') & `touse'==1 if `"`trace'"'!="" { summarize `imputed' if missing(`y') & `touse'==1 local mean=r(mean) local posts `posts' (`mean') } replace `x`i''=`imputed' count if `touse'==1 & missing(`imputed') if r(N)>0 { if "`warning'"!="nowarning" { noi di as err _n(2) "Warning: while I was executing the following command:" noi di as err `"uvis `cmd`i'' `x`i'' `vars' `wgt' if `touse', /// gen(imputed) `boot`i'' `match`i'' `constant' `pp' `vce'"' noi di as err "I found that " r(N) " missing value(s) of `x`i'' that should have been" noi di as err "imputed were missing at cycle " as inp `j' noi di as err "Replacing value(s) with mean, dumping current data to file _ice_dump.dta and continuing." } save _ice_dump, replace sum `imputed' if `touse'==1 replace `x`i''=r(mean) if `touse'==1 & missing(`imputed') } drop `y' `imputed' } } if `"`trace'"'!="" post `tmp' `posts' if `recalc'>0 { // update covariates needing recalculation forvalues i=1/`nvar' { if "`exp`i''"!="" & `nimp`i''>0 { replace `x`i''=`exp`i'' } } } if `to_imp'==1 & "`first'"!="" & "`warning'"!="nowarning"{ noi di as text _n "[Only 1 variable to be imputed, therefore no cycling needed]" } } } if `"`trace'"'!="" postclose `tmp' * Save to file with cases in original order quietly { local impvl /* list of newvars containing imputations */ sort `order' forvalues i=1/`nvar' { return scalar ni`i'=`nimp`i'' if "`genmiss'"!="" { cap drop `genmiss'`x`i'' rename `mvar`i'' `genmiss'`x`i'' } local impvl `impvl' `x`i'' if `main`i'' { lab var `x`i'' "`lab`i''" cap drop `miss`i'' } } // Save imputation model to char _dta[mi_ivar] char _dta[mi_ivar] `impvl' // char _dta[mi_id] `id' rename `order' `id' return local impvl `impvl' return scalar imputed=`to_imp' drop `touse' save `"`using'"', replace } end *! v 1.0.0 PR 01Jun2001. program define sampmis version 7 * Duplicates nonmissing obs of `exp' into missing ones, in random order. * This routine always reproduces the same sort order among the missings. * Note technique to avoid Stata creating arbitrary sort order for missing * observations of `exp'; affects entire reproducibility of mvi sampling. syntax newvarname =/exp quietly { tempvar u * Sort non-missing data at random, sort missing data systematically gen double `u'=cond(missing(`exp'), _n, uniform()) sort `u' count if !missing(`exp') local nonmis=r(N) drop `u' local type: type `exp' gen `type' `varlist'=`exp' local blocks=int((_N-1)/`nonmis') forvalues i=1/`blocks' { local j=`nonmis'*`i' local j1=`j'+1 local j2=min(`j'+`nonmis',_N) replace `varlist'=`exp'[_n-`j'] in `j1'/`j2' } } end program define ChkIn, sclass version 7 * Returns s(k) = index # of target variable v in varlist, or 0 if not found. args v varlist sret clear local k: list posof "`v'" in varlist sret local k `k' if `s(k)'==0 { di as err "`v' is not a valid covariate" exit 198 } end *! version 1.0.0 PR 20dec2004. program define ParsExp, sclass version 8 tokenize `"`*'"', parse(" +-/^()[]{}.*=<>!$%&|~`'") local vl while "`1'"!="" { cap confirm var `1' if _rc==0 { if index("`vl'", "`1'")==0 { local vl `vl' `1' } } mac shift } sreturn local result `vl' end program define detangle version 8 /* Disentangle varlist:string clusters---e.g. for DF. Returns values in $S_*. If `4' is null, `3' is assumed to contain rhs and lowest and highest value checking is disabled. Heavily based on frac_dis.ado, but "=" disallowed as separator and "\" allowed (for use by passive()). */ args target tname rhs separator if "`separator'"=="" { local separator "," } unab rhs:`rhs' local nx: word count `rhs' forvalues j=1/`nx' { local n`j': word `j' of `rhs' } tokenize "`target'", parse("`separator'") local ncl 0 /* # of separator-delimited clusters */ while "`1'"!="" { if "`1'"=="`separator'" { mac shift } local ncl=`ncl'+1 local clust`ncl' "`1'" mac shift } if "`clust`ncl''"=="" { local --ncl } if `ncl'>`nx' { di as err "too many `tname'() values specified" exit 198 } /* Disentangle each varlist:string cluster */ forvalues i=1/`ncl' { tokenize "`clust`i''", parse(":") if "`2'"!=":" { if `i'>1 { noi di as err "invalid `clust`i'' in `tname'() (syntax error)" exit 198 } local 2 ":" local 3 `1' local 1 forvalues j=1/`nx' { local 1 `1' `n`j'' } } local arg3 `3' unab arg1:`1' tokenize `arg1' while "`1'"!="" { ChkIn `1' "`rhs'" local v`s(k)' `arg3' mac shift } } forvalues j=1/`nx' { if "`v`j''"!="" { global S_`j' `v`j'' } else global S_`j' } end *! Based on artformatnos.ado v 1.0.0 PR 26Feb2004 program define formatline, rclass version 8 syntax, N(string) Maxlen(int) [ Format(string) Leading(int 1) Separator(string) ] if `leading'<0 { di as err "invalid leading()" exit 198 } if "`separator'"!="" { tokenize "`n'", parse("`separator'") } else tokenize "`n'" local n 0 while "`1'"!="" { if "`1'"!="`separator'" { local ++n local n`n' `1' } macro shift } local j 0 local length 0 forvalues i=1/`n' { if "`format'"!="" { capture local out: display `format' `n`i'' if _rc { di as err "invalid format attempted for: " `"`n`i''"' exit 198 } } else local out `n`i'' if `leading'>0 { local out " `out'" } local l1=length("`out'") local l2=`length'+`l1' if `l2'>`maxlen' { local ++j return local line`j'="`line'" local line "`out'" local length `l1' } else { local length `l2' local line "`line'`out'" } } local ++j return local line`j'="`line'" return scalar lines=`j' end *! version 1.0.0 PR 30may2006. program define listsort3, sclass sortpreserve version 8 gettoken p 0 : 0, parse(" ,") if `"`p'"'=="" exit sret clear syntax , [ Reverse Lexicographic ] local lex="`lexicog'"!="" if "`reverse'"!="" local comp < else local comp > local np: word count `p' tempvar c rank qui gen `c'=. forvalues i=1/`np' { local pi: word `i' of `p' if !`lex' confirm number `pi' qui replace `c'=`pi' in `i' } qui egen long `rank'=rank(`c'), unique forvalues i=1/`np' { /* Find original position (antirank) of each rank */ local j 0 while `j'<`np' { local ++j if `i'==`rank'[`j'] { local index`i' `j' local j `np' } } } local p forvalues i=1/`np' { sret local index`i' `index`i'' local pi=`c'[`i'] local p `p' `pi' local index `index' `index`i'' } sret local list `p' sret local index `index' end