*PERFORMS TWO-FOLD FULLY CONDITIONAL SPECIFICATION MULTIPLE IMPUATION ALGORITHM *VERSION 4: RUNS MI IMPUTE CHAINED ONCE TO PRODUCE IMPUTATIONS *Uses the mi impute chained options seed, savetrace, dryrun and force *Change imputation order for variables with missing data if necessary for conditional statements *Runs if some time points are completely observed or completely missing program define twofold local default_seed=int(uniform()*100) syntax, [WIDTH(integer 1) BA(integer 10) M(integer 5) BW(integer 5) CAT(string ) BASE(string ) INDOBS(string ) DEPOBS(string ) INDMIS(string ) DEPMIS(string ) OUTCOME(string ) CONDITIONON(string ) CONDVAR(string ) CONDVAL(string ) SAVING(string ) SAVETRACE(string asis) CLEAR TABLE IM KEEPOUTSIDE SEED(integer `default_seed') DRYRUN FORCE ] TIMEIN(string) TIMEOUT(string) qui cap { version 12 *DO NOT RUN IF DATA ALREADY IMPUTED cap confirm var _mi_m if !_rc { tab _mi_m if r(r)>0 { nois di as error "Data already imputed" error 1000 } } *CREATE START AND END TIME POINTS cap confirm var `timein' if _rc { nois di as error "Variable `timein' in timein() option not recognoised" error 111 } else { sum `timein' local start=r(min) } cap confirm var `timeout' if _rc { nois di as error "Variable `timeout' in timeout() option not recognoised" error 111 } else { sum `timeout' local end=r(max) } *CHECK SAVING OPTIONS ARE CORRECTLY SPECIFIED if "`saving'"=="" & "`clear'"=="" { nois di as err "saving() and/or clear required" error 100 } if `"`saving'"'!="" { tokenize `"`saving'"', parse(",") if `"`2'"'!="" { if `"`2'"'!="," | `"`4'"'!="" error 198 if `"`3'"'!="" { if `"`3'"'!="replace" error 198 local replace replace } } local using `1' if "`replace'"=="" { if substr(`"`using'"', -4, .) != ".dta" confirm new file `"`using'.dta"' else confirm new file `"`using'"' } } if `"`savetrace'"'!="" { tokenize `"`savetrace'"', parse(",") if `"`2'"'!="" { if `"`2'"'!="," | `"`4'"'!="" error 198 if `"`3'"'!="" { if strpos(`"`3'"',"replace")>0 local replace_savetrace replace } } local using_savetrace `1' local option_savetrace `3' if "`replace_savetrace'"=="" { if substr(`"`using_savetrace'"', -4, .) != ".dta" confirm new file `"`using_savetrace'.dta"' else confirm new file `"`using_savetrace'"' } if substr(`"`using_savetrace'"', -4, .) != ".dta" local using_savetrace `"`using_savetrace'.dta"' } if "`indmis'"=="" & "`depmis'"=="" { nois di as error "No variables with missing values specified using indmis or depmis" error 198 } if "`indmis'"!="" & "`base'"=="" { nois di as error "Please specify a baseline time using base when imputing time-independent variables using indmis" error 198 } foreach i in indobs depmis depobs { if "``i''"!="" & "`indmis'"!="" { foreach v of varlist `indmis' { foreach x in ``i'' { if "`v'"=="`x'" { nois di as error "`v' is specified in indmis() and `i'()" error 198 } } } } } if "`indobs'"!="" | "`depmis'"!="" { foreach i in indobs depmis { if "``i''"!="" & "`depobs'"!="" { foreach v in `depobs' { foreach x in ``i'' { if "`v'"=="`x'" { nois di as error "`v' is specified in `i'() and depobs()" error 198 } } } } } foreach v in `indobs' { foreach x in `depmis' { if "`v'"=="`x'" { nois di as error "`v' is specified in indobs() and depmis()" error 198 } } } } if `end'<=`start' { nois di as error "The last time point, specified by `timeout', is before the first time point, specified by `timein'" error 198 } if `width'>=`end'-`start'+1 { nois di as error "The specified window width is the same/greater than the number of time points" nois di as error "Consider using an imputation model which includes measurements at each" nois di as error "time point as separate variables in the model or reduce the window width" error 198 } *CONFIRM EACH OF THE SPECIFIED VARIABLES EXIST if "`indmis'"!="" | "`indobs'"!="" { foreach v of varlist `indmis' `indobs' { set output error confirm variable `v' } } if "`depmis'"!="" | "`depobs'"!="" { foreach v in `depmis' `depobs' { forvalues i=`start'/`end' { set output error confirm variable `v'`i' } } } if "`base'"!="" { count if `base'<`timein' | `base'>`timeout' if r(N)>0 { nois di as error "The baseline time points specified for some/all records are outside the follow-up time" error 198 } } *CHECK ALL COMPLETELY OBSERVED VARIABLES DO NOT HAVE ANY MISSING DATA if "`indobs'"!="" { foreach v of varlist `indobs' { count if missing(`v') if r(N)>0 { nois di as error "`v' is defined as a completely observed variable but has missing values" error 198 } } } if "`depobs'"!="" { foreach v in `depobs' { forvalues i=`start'/`end' { count if missing(`v'`i') & inrange(`i',`timein',`timeout') if r(N)>0 { nois di as error "`v'`i' is defined as a completely observed variable but has missing values" error 198 } } } } if `start'<0 { nois di as error "All time points must be positive" error 111 } *CREATE TABLE OF MISSING VALUES if "`table'"!="" { qui { if "`indmis'"!="" { nois di _n in gr "Time-independent {c |}" nois di in gr "variables {c |}" nois di in gr "with missing values {c |} Percentage of missing values" nois di in gr "{hline 20}{c +}{hline 30}" foreach v of varlist `indmis' { count if missing(`v') nois di in smcl in gr %19s abbrev("`v'",19) " {c |} " in ye %10.1f `"`=string(r(N)*100/`=_N',"%10.1f")'"' } } if "`depmis'"!="" { nois di _n in gr "Time-dependent {c |} Percentage of missing values" nois di in gr "variables {c |} at each time point" local timepoints local i 1 forvalues j=`start'/`end' { local timepoints `timepoints' _col(`=(`i'*8)+20') `j' local ++i } nois di in gr "with missing values {c |} "`timepoints' noi di in gr "{hline 20}{c +}{hline `=8*(`end'-`start'+1.5)'}" foreach v in `depmis' { local output local i 1 forvalues j=`start'/`end' { count if missing(`v'`j') local output `output' _col(`=(`i'*8)+20') `"`=string(r(N)*100/`=_N',"%5.1f")'"' local ++i } noi di in smcl in gr %19s abbrev("`v'",19) " {c |} " in ye `output' _n } } } } *THE SAME NUMBER OF PARAMETERS MUST BE SPECIFIED FOR *CONDITIONON CONDVAR CONDVAL if wordcount("`conditionon'")!=wordcount("`condvar'") | wordcount("`conditionon'")!=wordcount("`condval'") | wordcount("`condval'")!=wordcount("`condvar'") { nois di as error "Please specifiy each condition option separately" nois di as error "There should be the same number of parameters for condvar, condval & conditionon" error 198 } *CHECK VARIABLES IN CONDVAR ALSO SPECIFIED IN INDMIS AND DEPMIS if "`condvar'"!="" | "`indmis'"!="" | "`depmis'"!="" { foreach cvar in `condvar' { local stop 0 foreach i in `indmis' `depmis'{ if "`i'"=="`cvar'" local stop 1 } if `stop'==0 { nois di as error "Variables defined using condvar not specified by indmis or depmis" error 198 } } } *CREATE IF STATEMENT FOR TIME-INDEPENDENT VARIABLES WITH CONDITION if "`condvar'"!="" & "`condval'"!="" & "`conditionon'"!="" { local word=wordcount("`condvar'") forvalues k=1/`word' { tokenize `condvar' local cvar ``k'' *CHECK CONDVAR IS A TIME-INDEPENDENT VARIABLE (because time-dependent stub won't exist as a variable) cap confirm variable `cvar' if _rc { continue } tokenize `condval' local cval ``k'' tokenize `conditionon' local z ``k'' local tostop 0 if "`cat'"!="" { foreach c in `cat' { if strpos("`z'","`c'")>0 { *if conditioning variable is time independent foreach var in `indmis' `indobs' { if "`var'"=="`z'" { local if_`cvar' if `z'==`cval' local omit_`cvar' i.`z' local tostop 1 continue, break } } *if conditioning variable is time dependent and time point not specified foreach var in `depmis' `depobs' { if "`var'"=="`z'" { nois di as error "Please Specify time point for variable `var' in condition option" error 111 } } *if conditioning variable is time dependent and time point not specified foreach var in `depmis' `depobs' { forvalues j=1/`bw' { if "`var'`j'"=="`z'" { local if_`cvar' if `z'_`bw'==`cval' local omit_`cvar' i.`z'_`bw' local tostop 1 continue, break } } } } } } if "`cat'"=="" | `tostop'==0 { *if conditioning variable is time independent foreach var in `indmis' `indobs' { if "`var'"=="`z'" { local if_`cvar' if `z'==`cval' local omit_`cvar' `z' local tostop 1 continue, break } } *if conditioning variable is time dependent and time point not specified foreach var in `depmis' `depobs' { if "`var'"=="`z'" { if "`var'"=="`z'" { nois di as error "Please Specify time point for variable `var' in condition option" error 111 } } } *if conditioning variable is time dependent and time point not specified foreach var in `depmis' `depobs' { forvalues t=1/`bw' { if "`var'`t'"=="`z'" { local if_`cvar' if `z'_`bw'==`cval' local omit_`cvar' `z'_`bw' local tostop 1 continue, break } } } } } } forvalues i=`start'/`end' { *CREATE IF STATEMENT FOR TIME-DEPENDENT VARIABLES WITH CONDITION if "`condvar'"!="" & "`condval'"!="" & "`conditionon'"!="" { local word=wordcount("`condvar'") forvalues k=1/`word' { tokenize `condvar' local cvar ``k'' tokenize `condval' local cval ``k'' tokenize `conditionon' local z ``k'' forvalues j=1/`bw' { local tostop 0 if "`cat'"!="" { foreach c in `cat' { if strpos("`z'","`c'")>0 { *if conditioning variable is time independent foreach var in `indmis' `indobs' { if "`var'"=="`z'" { local if_`cvar'`i'_`j' if `z'==`cval' local omit_`cvar'`i'_`j' i.`z' local tostop 1 continue, break } } *if conditioning variable is time dependent and time point not specified foreach var in `depmis' `depobs' { if "`var'"=="`z'" { nois di as error "Please Specify time point for variable `var' in condition option" error 111 } } *if conditioning variable is time dependent and time point not specified foreach var in `depmis' `depobs' { forvalues t=1/`bw' { nois di as error "Please Specify time point for variable `var' in condition option" error 111 } } } } if "`cat'"=="" | `tostop'==0 { cap confirm variable `z' if !_rc { local omit_`cvar'`i'_`j' `omit_`cvar'`i'_`j'' `z' local if_`cvar'`i' if `z'==`cval' } else { if local omit_`cvar'`i' `omit_`cvar'`i'_`j'' `z'`i' local if_`cvar'`i'_`j' if `z'`i'==`cval' } } } if "`cat'"=="" | `tostop'==0 { *if conditioning variable is time independent foreach var in `indmis' `indobs' { if "`var'"=="`z'" { local if_`cvar'`i'_`j' if `z'==`cval' local omit_`cvar'`i'_`j' `z' local tostop 1 continue, break } } *if conditioning variable is time dependent and time point not specified foreach var in `depmis' `depobs' { if "`var'"=="`z'" { nois di as error "Please Specify time point for `var' in condition option" error 111 } } *if conditioning variable is time dependent and time point not specified foreach var in `depmis' `depobs' { forvalues t=1/`bw' { if "`var'`t'"=="`z'" { local if_`cvar'`i'_`j' if `z'_`bw'==`cval' local omit_`cvar'`i'_`j' `z'_`bw' local tostop 1 continue, break } } } } } } } } *CREATE VARIABLES forvalues t=`start'/`end' { forvalues i=1/`bw' { foreach var in `depmis' { gen `var'`t'_`i'=`var'`t' } } } *TIME-INDEPENDENT VARIABLES CONDITION ON BASELINE VARIABLES ONLY local missingvars foreach i in `indmis' { local missingvars `missingvars' `i' foreach j in `depmis' { forvalues t=`start'/`end' { local tostop 0 if "`cat'"!="" { foreach c in `cat' { if "`j'"=="`c'" { if `t'==`base' local incl_`i' `incl_`i'' i.`j'`t'_`bw' local tostop 1 continue, break } } } if "`cat'"=="" | `tostop'==0 { if `t'==`base' local incl_`i' `incl_`i'' `j'`t'_`bw' } } } foreach j in `depobs' { forvalues t=`start'/`end' { local tostop 0 if "`cat'"!="" { foreach c in `cat' { if "`j'"=="`c'" { if `t'!=`base' local omit_`i' `omit_`i'' i.`j'`t' local tostop 1 continue, break } } } if "`cat'"=="" | `tostop'==0 { if `t'!=`base' local omit_`i' `omit_`i'' `j'`t' } } } *THE SYNTAX INCLUDES TOO MANY VARIABLES IF THEY ARE NOT MISSING *CODE BELOW REMOVES EXCESS VARIABLE foreach v in `depmis' { forvalues t=`start'/`end' { qui count if missing(`v'`t') if r(N)==0 { local tostop 0 forvalues b = 1/`bw' { if "`cat'"!="" { foreach c in `cat' { if "`v'"=="`c'" { if local omit_`i' `omit_`i'' i.`v'`t'_`b' local tostop 1 continue, break } } } if "`cat'"=="" | `tostop'==0 { local omit_`i' `omit_`i'' `v'`t'_`b' } } } } } } local depvar forvalues t=`start'/`end' { *IDENTIFY WHICH TIME POINTS FOR TIME-DEPENDENT VARIABLES ARE INCLUDED IN IMPUTATION MODEL local counter 1 forvalues i=1/`bw' { foreach var in `depmis' { local depvar `depvar' `var'`t'_`i' local missingvars `missingvars' `var'`t'_`i' local tostop 0 if "`cat'"!="" { foreach c in `cat' { if "`var'"=="`c'" { count if missing(`var'`t'_`i') if r(N)!=_N local number_`counter' i.`var'`t'_`i' continue, break } else { count if missing(`var'`t'_`i') if r(N)!=_N local number_`counter' `var'`t'_`i' continue, break } } } foreach j in `depmis' { forvalues x=`start'/`end' { local tostop 0 if "`cat'"!="" { foreach c in `cat' { if "`j'"=="`c'" { if `x'<=`t'+`width' & `x'>=`t'-`width' & `x'!=`t' { count if missing(`j'`x'_`bw') if r(N)!=_N & r(N)!=0 { local incl_`var'`t'_`i' `incl_`var'`t'_`i'' i.`j'`x'_`bw' local tostop 1 continue, break } } count if missing(`j'`x'_1) if `x'==`t' & `i'==1 & "`var'"!="`j'" & r(N)!=_N & r(N) { local incl_`var'`t'_`i' `incl_`var'`t'_`i'' i.`j'`x'_1 local tostop 1 continue, break } } } } if "`cat'"=="" | `tostop'==0 { if `x'<=`t'+`width' & `x'>=`t'-`width' & `x'!=`t' { count if missing(`j'`x'_`bw') if r(N)!=_N & r(N)!=0 { local incl_`var'`t'_`i' `incl_`var'`t'_`i'' `j'`x'_`bw' continue, break } } count if missing(`j'`x'_1) if `x'==`t' & `i'==1 & "`var'"!="`j'" & r(N)!=_N & r(N)!=0 { local incl_`var'`t'_`i' `incl_`var'`t'_`i'' `j'`x'_1 continue, break } } } } local varnum wordcount("`depmis'") forvalues v=1/`=`varnum'-1' { if `i'>1 local incl_`var'`t'_`i' `incl_`var'`t'_`i'' `number_`=`counter'-`v''' } foreach v in `depobs' { local tostop 0 forvalues x=`start'/`end' { if "`cat'"!="" { foreach c in `cat' { if "`v'"=="`c'" { if (`x'>`t'+`width' & `x'<=`end')|(`x'<`t'-`width' & `x'>=`start') local omit_`var'`t'_`i' `omit_`var'`t'_`i'' i.`v'`x' local tostop 1 continue, break } } } if "`cat'"=="" | `tostop'==0 { if (`x'>`t'+`width' & `x'<=`end')|(`x'<`t'-`width' & `x'>=`start') local omit_`var'`t'_`i' `omit_`var'`t'_`i'' `v'`x' } } } foreach v in `indmis' { local tostop 0 if "`cat'"!="" { foreach c in `cat' { if "`v'"=="`c'" { local incl_`var'`t'_`i' `incl_`var'`t'_`i'' i.`v' local tostop 1 continue, break } } } if "`cat'"=="" | `tostop'==0 { local incl_`var'`t'_`i' `incl_`var'`t'_`i'' `v' } } local counter=`counter'+1 *nois di "`var'`t'_`i': `incl_`var'`t'_`i''" } } foreach v in `depobs' { local tostop 0 if "`cat'"!="" { foreach c in `cat' { if "`v'"=="`c'" { local depvar `depvar' i.`v'`t' local tostop 1 continue, break } } } if "`cat'"=="" | `tostop'==0 { local depvar `depvar' `v'`t' } } } *THE SYNTAX INCLUDES TOO MANY VARIABLES IF THEY ARE NOT MISSING *CODE BELOW REMOVES EXCESS VARIABLES foreach i in `depvar' { foreach v in `depmis' { forvalues t=`start'/`end' { qui count if missing(`v'`t') if r(N)==0 { local tostop 0 forvalues b = 1/`bw' { if "`cat'"!="" { foreach c in `cat' { if "`v'"=="`c'" { local omit_`i' `omit_`i'' i.`v'`t'_`b' local tostop 1 continue, break } } } if "`cat'"=="" | `tostop'==0 { local omit_`i' `omit_`i'' `v'`t'_`b' } forvalues w=-`width'/`width' { if "`i'"=="`v'`=`t'+`w''_`b'" { local tostop 0 if "`cat'"!="" { foreach c in `cat' { if "`v'"=="`c'" { local incl_`i' `incl_`i'' i.`v'`t' local tostop 1 continue, break } } } if "`cat'"=="" | `tostop'==0 { local incl_`i' `incl_`i'' `v'`t' continue, break } } } } } } } } *** CREATE SYNTAX FOR MI IMPUTE CHAINED COMMAND *** local vars_syntax local auxvarsind foreach var in `indmis' `depvar' `indobs' `outcome' { if substr("`var'",1,2)=="i." { local sub=substr("`var'",3,.) count if missing(`sub') local miss=r(N) } else count if missing(`var') local miss=r(N) *DONT IMPUTATE IF COMPLETELY MISSING OR COMPLETELY OBSERVED if `miss'>0 & `miss'!=_N { local tostop 0 if "`cat'"!="" { foreach c in `cat' { forvalues i=`start'/`end' { forvalues x=1/`bw' { if ("`var'"=="`c'" | "`var'"=="`c'`i'" | "`var'"=="`c'`i'_`x'" | "`var'"=="before`c'`i'" | "`var'"=="after`c'`i'") & `tostop'==0 { qui tab `var' `if_`var'' if r(r)==2 { local vars_syntax `vars_syntax' (logit, cond(`if_`var'') omit(`omit_`var'') include(`incl_`var'') augment) `var' local tostop 1 continue, break } else { local vars_syntax `vars_syntax' (mlogit, cond(`if_`var'') omit(`omit_`var'') include(`incl_`var'') augment) `var' local tostop 1 continue, break } } } } } } if "`cat'"=="" | `tostop'==0 { local vars_syntax `vars_syntax' (regress, cond(`if_`var'') omit(`omit_`var'') include(`incl_`var'')) `var' } } else if r(N)==0 { local tostop 0 if "`cat'"!="" { foreach c in `cat' { forvalues i=`start'/`end' { forvalues x=1/`bw' { if ("`var'"=="`c'" | "`var'"=="`c'`i'" | "`var'"=="`c'`i'_`x'") & `tostop'==0 { local auxvarsind `auxvarsind' i.`var' local tostop 1 } } } } if `tostop'==0 { local auxvarsind `auxvarsind' `var' local tostop 1 } } else { local auxvarsind `auxvarsind' `var' } } } mi set flong cap mi register imputed `missingvars' if _rc { nois di as err "The variables defined in depmis and/or indmis have no missing values" error 198 nois di "`=_rc'" } if "`im'"!="" nois di _n "mi impute chained `vars_syntax' = `auxvarsind', burnin(`ba') orderasis chaindots add(`m') noimputed force rseed(`seed') `dryrun' `force'" if "`im'"!="" & `"`savetrace'"'!="" nois di "savetrace(`using_savetrace', `option_savetrace') " if `"`savetrace'"'=="" nois mi impute chained `vars_syntax' = `auxvarsind', burnin(`ba') orderasis chaindots add(`m') noimputed force rseed(`seed') `dryrun' `force' else nois mi impute chained `vars_syntax' = `auxvarsind', burnin(`ba') orderasis chaindots add(`m') noimputed force rseed(`seed') `dryrun' `force' savetrace(`"`using_savetrace'"', `option_savetrace') if "`dryrun'"!="" { mi extract 0, clear foreach i in `depmis' { forvalues j=`start'/`end' { forvalues w=1/`bw'{ cap drop `i'`j'_`w' } } } } else { foreach i in `depmis' { forvalues j=`start'/`end' { replace `i'`j'=`i'`j'_`bw' if _mi_m>0 & missing(`i'`j') forvalues w=1/`bw'{ drop `i'`j'_`w' } } } if "`using'"!="" & "`clear'"=="" { save `"`using'"', `replace' mi extract 0, clear } else if "`clear'"!="" & "`using'"=="" { *Keep the imputed dataset in memory nois di as txt _n _n "[note: imputed dataset now loaded in memory]" } else { save `"`using'"', `replace' nois di as txt _n _n "[note: imputed dataset now loaded in memory]" } } } if _rc!=0 { local rc `=_rc' *RESTORE ORIGINAL DATASET if `rc'!=1000 cap mi extract 0, clear foreach i in `depmis' { forvalues j=`start'/`end' { forvalues w=1/`bw'{ cap drop `i'`j'_`w' } } } error `rc' } end