program define rc_spline version 7 * * rc_spline creates variables that can be used for regression models * in which the linear predictor f(xvar) is assumed to equal a restricted cubic * spline function of an independent variable xvar. In these regressions, the user * explicitly or implicitly specifies k knots located at xvar = t1, t2, ..., tk. * f(xvar) is defined to be a continuous smooth function that is linear before t1, * is a piecewise cubic polynomial between adjacent knots, and is linear after * tk. See Harrell (2001) for additional details. * * Restricted cubic splines are also called natural splines. * * Input: * * xvar is a continuous covariate that is to be entered into a model using RCPs. * * Options: * * nknots specifies the number of knots. * * knots specifies the exact location of the knots. The values of these knots must * be given in increasing order. * * If both of these options are given they must both specify the same number on knots. * When knots is omitted the default knot values are chosen according to Table 2.3 of * Harrell (2001) with the additional restriction that the smallest knot may not be * less than the 5th smallest value of xvar and the largest knot may not be greater than * the 5th largest value of xvar. The values of the all knots are displayed. When * knots is omitted the number of knots specified by nknots must be between 3 and 7. * The default number of knots when neither nknots nor knots is given is 5. * * Frequency weights are allowed. * * Output: * * rc_spline creates variables called _Sxvar1, _Sxvar2, ..., _Sxvar(k-1), where * "xvar" is the input variable name. There are always one fewer variables created * than there are knots. If the model has k parameters beta0, beta1, ... , beta(k-1) * then * * f(xvar) =beta0 + beta1*_Sxvar1 + beta2*_Sxvar2 + ... + beta(k-1)*_Sxvar(k-1). * * An important aspect of restricted cubic splines is that the variables _Sxvar1, * ... , _Sxvar(k-1) are functions of xvar and the knots only and are not affected by * the response variable. This means that we can use rc_spline to define the * _Sxvar* variables before specifying the response variable or the type of regression * model. * *---------------------------------------------------------------- syntax varlist(max=1) [if] [in] [fw] [,NKnots(numlist max=1) KNots(numlist)] * * Preserve the data set. If this program bombs then we * want to restore the data set. Otherwise we want to keep * the data set as modified. * preserve * * Default number of knots (nknots) is 5. * if "`nknots'"!="" { local nk `nknots' } else { local nk=5 } * * If knots is specified then count the number of * knots in the list. * if "`knots'"!="" { local nc 0 tokenize "`knots'" while "`1'" != "" { local nc = `nc' + 1 local t`nc' "`1'" mac shift } } * * If nknots or knots is specified but not both then * set nc and nk to the same value. * if "`nknots'"!="" & "`knots'"=="" { local nc `nk' } if "`nknots'"=="" & "`knots'"!="" { local nk `nc' } disp as text " number of knots = " as result "`nk'" * * touse=1 if the record is included in this run. touse=0 * otherwise. * tempvar touse mark `touse' `if' `in' * * If nknots and knots are both specified then the number * of knots must be the same as nknots. * if "`nknots'"!="" & "`knots'"!="" { if `nc' != `nk' { display as error "nknots count must be the same as the number of knots specified" err_handler exit } } * * Set a variable so we can return data set to original * sort order when we are done. * gen _Order = _n * * If frequency weights are specified then expand the * data set as appropriate. * if "`weight'" != "" { qui expand `exp' } sort `varlist' if "`knots'"!="" { * * Execute this clause if the user specified their own knots. * if `nc' < 2 { display as error "Restricted cubic splines must have at least 2 knots" err_handler exit } * * Check order of t(i). * local prevt=`t1' local j=2 while `j'<=`nk' { if `t`j'' <= `prevt' { disp as error "knots must be in increasing order" err_handler exit } local prevt=`t`j'' local j=`j'+1 } } else { * * Execute this clause if user is taking the knots as specified * in Harrell (2001). * if `nk' == 3 { qui centile `varlist' if `touse', centile(10 50 90) } else if `nk'== 4 { qui centile `varlist' if `touse', centile(5 35 65 95) } else if `nk'== 5 { qui centile `varlist' if `touse', centile(5 27.5 50 72.5 95) } else if `nk'== 6 { qui centile `varlist' if `touse', centile(5 23 41 59 77 95) } else if `nk'== 7 { qui centile `varlist' if `touse', centile(2.5 18.33 34.17 50 65.83 81.67 97.5) } else { display as error "Restricted cubic splines with `nk' knots not implimented" display as error "Number of knots must be between 3 and 7" err_handler exit } forvalues i=1 / `nk' {local t`i' = r(c_`i')} if `t1' < `varlist'[5] { local t1 = `varlist'[5] } if `t`nk'' > `varlist'[r(N)-4] { local t`nk' = `varlist'[r(N)-4] } } * * Set _Sx1=x. * gen _S`varlist'1=`varlist' * if `t1' < `varlist'[5] local t1 = `varlist'[5] * if `t`nk'' > `varlist'[r(N)-4] local t`nk' = `varlist'[r(N)-4] local j = 1 while `j' <= `nk' { display as text" value of knot `j' = " as result `t`j'' local j = `j'+1 } local km1 = `nk' - 1 if `t1' >= `t2' | `t`nk'' <= `t`km1'' { display as error "Sample size too small for this many knots" err_handler exit } local j = 1 while `j' <= `nk' { gen _Xm`j' = `varlist' - `t`j'' uplus _Xm`j' _Xm`j'p local j = `j'+1 } local j = 1 while `j' <= `nk' -2 { local jp1 = `j' + 1 gen _S`varlist'`jp1' = (_Xm`j'p^3 - (_Xm`km1'p^3)*(`t`nk'' - `t`j'')/(`t`nk'' - `t`km1'') + (_Xm`nk'p^3 )*(`t`km1'' - `t`j'')/(`t`nk'' - `t`km1'')) / (`t`nk'' - `t1')^2 local j = `j' + 1 } * * Return data set to original sort order. * sort _Order * * If frequency weights caused data set to be expanded then * this statement will return data set to original number * of records. * qby _Order: keep if _n==1 * list `varlist' _S`varlist'* drop _Order _Xm* _Xm*p * * This is the normal, successful exit so we don't * want to restore the data set (we've added variables). * restore, not end program define uplus version 7 * * This program has a single input variable u and an output variable * uplus as defined below. * args u uplus gen `uplus' = `u' quietly replace `uplus' = 0 if `u' <= 0 end program define err_handler version 7 * * Clean up after an error before returning. * capture restore error 498 end exit