📄 stsplit.ado
字号:
}
drop `ilast'
compress `firstn' `nrec' `ifirst'
recast double _t0
recast double _t
noi SaveExpand = `nrec' if `touse'
sort `xid'
* mark episodes first=1, last=2, inbetween=0
by `xid' : gen byte `markfl' = 1*(_n==1) + 2*(_n==_N) if `nrec' > 1
* mark episode with nr of time being split on
by `xid' : gen long `evid' = `ifirst' + _n - 1
* all splitted episodes are ended by non-failure, marked -1 for now
by `xid' : replace _d = -1 if _n < _N & `touse'
* Update _t0, _t, and the episode marker vname
* --------------------------------------------
* use variable T to efficiently implement indexed access to at()
* sometimes _N may be smaller than -nat- !
if `nat' > _N {
* extra cases !
local extra 1
local Nplus1 = _N+1
expand =`nat'-`Nplus1'+2 in l
replace `touse' = 0 in `Nplus1'/l
}
tempvar T
gen `T' = .
forv i = 1/`nat' {
replace `T' = `at`i'' in `i'
}
* replace _t0 in all but the first record within xid
replace _t0 = -`Base'+`T'[`evid'-1] if (`markfl'==0 | `markfl'==2) & `touse'
* replace _t in all but the last record within xid
replace _t = -`Base'+`T'[`evid'] if (`markfl'==0 | `markfl'==1) & `touse'
* episode marker
gen `vtype' `vname' = cond(`at1'>0,0,`at1'-1) if `evid'==1 & `touse'
replace `vname' = `T'[`evid'-1] if `evid'!=1 & `touse'
replace `vname' = `T'[`nat'] if `evid'>=. & `touse'
if "`extra'" != "" {
drop in `Nplus1'/l
}
* modify user variables, reset _d
ModifyUserVars, `codesplit'
compress _t0 _t `vname'
* trim (set _st to 0) epsiodes before at_min and after at_max
* ----
if "`trim'" != "" {
local atmin `at1'
local atmax `at`nat''
count if float(`vname') < float(`atmin') & `touse'
local lb = r(N)
count if float(`vname') == float(`atmax') & `touse'
local ub = r(N)
if `lb' | `ub' {
replace _st = 0 if float(`vname') < float(`atmin') /*
*/ | float(`vname') == float(`atmax')
noi di as txt "(" `lb' " + " `ub' /*
*/ " obs. trimmed due to lower and upper bounds)"
if `lb' & `ub' {
st_note `"`vname'<=`atmin' & `vname'>`atmax' trimmed"'
}
else if `lb' {
st_note `"`vname'<=`atmin' trimmed"'
}
else st_note `"`vname'>`atmax' trimmed"'
}
else {
noi di as txt "(no obs. trimmed because none out of range)"
}
}
`Done'
} /* quietly */
end
* ============================================================================
* Every -- performs every() episode splitting
* ============================================================================
program define Every
syntax newvarname(numeric) [=exp] [if] , Every(str) /*
*/ [ AFter(passthru) COdesplit(passthru) noPREserve Fast ]
local vname `varlist'
local vtype `typlist'
quietly {
tempname e ie it0 it nt xid
marksample touse, novar
replace `touse' = 0 if _st == 0
if `"`exp'`after'"' != "" {
tempvar Base
After `Base' `exp' if `touse', `after'
}
else local Base 0
qui gen `e' = `every' if `touse'
capt assert !missing(`e') if `touse'
if _rc {
noi di as err "every() must be nonmissing"
exit 498
}
capt assert `e' > 0 if `touse'
if _rc {
noi di as err "every() must be a strictly positive"
exit 498
}
* in which intervals are _t0 and _t
* coding: 1 [0,e) 2 [e,2e) 3 [2e,3e) etc
gen `it0' = 1 + int((_t0+`Base')/`e') if `touse'
gen `it' = 1 + int(((_t+`Base')/`e')-1E-8) if `touse'
* number of intervals [_t0,_t)
gen `nt' = `it'-`it0'+1 if `touse'
gen `xid' = _n if `touse'
* expand!
if "`preserve'" == "" & "`fast'" == "" {
preserve
local Done "restore, not"
}
drop `it'
compress `e' `it0' `nt' `xid'
recast double _t0
recast double _t
noi SaveExpand = `nt' if `touse'
* adjust key-variables
sort `xid'
by `xid' : gen long `ie'= `it0' + _n - 1 if `touse'
by `xid' : replace _t0 = -`Base' + `e' * (`ie'-1) if _n > 1 & `touse'
by `xid' : replace _t = -`Base' + `e' * `ie' if _n < _N & `touse'
by `xid' : replace _d = -1 if _n < _N & `touse'
* set episode marker
gen `vtype' `vname' = 0 if `touse'
by `xid' : replace `vname' = `e' * (`ie'-1) if `touse'
* adjust user variables, reset _d
ModifyUserVars, `codesplit'
compress _t0 _t `vname'
`Done'
} /* quietly */
end
* ===========================================================================
* utility routines
* ===========================================================================
program define After
syntax newvarname [=/exp] [if] [, AFter(str) ]
local v `varlist'
marksample touse, novarlist
if `"`exp'"' != "" & `"`after'"' != "" {
di as err "=exp and after() cannot be specified simultaneously"
exit 198
}
else if `"`exp'"' != "" {
* for backward compatibility with version 1.*.*
local Exp `exp'
local ttime time
local how min
}
else {
local 0 `"= `after'"'
capt syntax =/exp
if !_rc {
local Exp `exp'
local ttime time
local how asis
}
else {
gettoken ttime rest : after , parse("= ")
if `"`ttime'"' != "time" & `"`ttime'"' != "t" /*
*/ & `"`ttime'"' != "_t" {
di as err `"invalid after(): `ttime'"'
exit 198
}
gettoken op fexp : rest , parse("=")
if "`op'" != "=" {
di as err "invalid after(): = expected"
exit 198
}
local 0 `"= `fexp'"'
capt syntax =/exp
if !_rc {
local Exp `exp'
local how const
}
else {
gettoken func exp : fexp, parse("(")
if "`func'" == "asis" | "`func'" == "min" {
local how `func'
gettoken Exp rest : exp, parse("(") match(paren)
if `"`rest'"' != "" | "`paren'" != "(" {
di as err `"invalid exp in after(): `fexp'"'
exit 198
}
}
else {
di as err "invalid after(): asis() or min() expected"
di as err `"found: `fexp'"'
exit 198
}
}
}
}
*di in re "exp : `Exp'"
*di in re "ttime : `ttime'"
*di in re "how : `how'"
* Base will contain the "age" at 0 in analysis t units
local id `_dta[st_id]'
sort `touse' `id' _t
gen double `v' = `Exp' if `touse'
if "`how'" == "min" {
* we take earliest value of time/date
* to avoid sorting, the min is stored in last element
by `touse' `id' : /*
*/ replace `v' = `v'[_n-1] if `v'[_n-1]<`v' & `touse'
}
else if "`how'" == "const" {
* verify that base() is constant within id
capt by `touse' `id' : assert `v' == `v'[1] if `touse'
if _rc {
di as err "after() must be constant within `id'"
exit 198
}
}
if "`ttime'" == "time" {
local o `_dta[st_o]'
local s `_dta[st_s]'
}
else {
local o 0
local s 1
}
by `touse' `id' : replace `v' = (`o'-`v'[_N])/`s' if `touse'
compress `v'
end
* SaveExpand =exp [if] [in]
* wrapper around -expand-, producing message on memory requirement
program define SaveExpand
syntax =/exp [if] [in]
marksample touse
capt expand =`exp' if `touse'
if _rc {
tempvar nrec
quiet gen long `nrec' = cond(`touse', int(`exp'), 1)
quiet summ `nrec', meanonly
local nobs = r(sum)
* compute memory requirement (in kb)
quiet des, short
local kb = int((`nobs' * r(width))/1024 + 1)
di _n as err "impossible to split episodes -- probably too little memory"
di
di as txt "expanded dataset would have {res:`nobs'} observations"
di as txt "memory should be set to at least {res:`kb'} Kb " /*
*/ "at the current data width"
di as txt "with extra memory for additional variables"
exit 950
}
end
* Created n0
* Reports number of created obs since n0
program define Created
args n0
if `n0' != _N {
local s = cond(_N-`n0'>1, "s", "")
di as txt "(" =(_N-`n0') " observation`s' (episode`s') created)"
}
else {
di as txt "(no new episodes generated)"
}
end
* IsVar vname
* returns in s(exist) whether vname is an existing variable
program define IsVar, sclass
nobreak {
capture confirm new var `1'
if _rc {
capture confirm var `1'
if _rc == 0 {
sreturn local exists 1
exit
}
}
}
sreturn local exists 0
end
* ModifyUserVars, codesplit(.|#)
* - modifies user variables for _t/_t0 and for event type (failure())
* - recodes -1 to 0 in _d
program define ModifyUserVars
syntax [, COdesplit(str)]
IsVar `_dta[st_bt]'
if `s(exists)' {
qui replace `_dta[st_bt]' = _t * `_dta[st_s]' + `_dta[st_o]' if _st
}
IsVar `_dta[st_bt0]'
if `s(exists)' {
qui replace `_dta[st_bt0]' = _t0 * `_dta[st_s]' + `_dta[st_o]' if _st
}
IsVar `_dta[st_bd]'
if `s(exists)' {
if `"`codesplit'"' == "" | `"`codesplit'"' == "." {
local c .
}
else {
capt confirm number `codesplit'
if _rc {
di as err "codesplit() must be a number"
exit 198
}
local c `codesplit'
}
qui replace `_dta[st_bd]' = `c' if _d == -1
}
qui replace _d = 0 if _d==-1
end
* DropDup newlist : list
* drops all duplicate tokens from list -- copied from hausman.ado
program define DropDup
args newlist /* name of macro to store new list
*/ colon /* ":"
*/ list /* list with possible duplicates */
gettoken token list : list
while "`token'" != "" {
local fixlist `fixlist' `token'
local list : subinstr local list "`token'" "", word all
gettoken token list : list
}
c_local `newlist' `fixlist'
end
* Assert label exp
program define Assert
gettoken lab 0 : 0
capt noi assert `0'
if _rc {
di as err "assert `lab' failed"
exit 9
}
end
exit
History
-------
2.2.0 update using features of Stata 7 (smcl, forvalues);
no functional changes
2.1.2 changed the type of various internally used variables from -int-
to -long- to fix invalid splitting in large datasets
2.1.0 totally new version by jw/ics
- added splitting at failure times
- new implementation of at()
- added every()
- more features in after(), replacing the old =exp syntax
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -