* lspline -- create a linear spline transformation *! version 1.0.0 Constantijn Panis STB-18: sg24 * Program to create a linear spline transformation of a variable. * Usage: * . lspline var stem [nodelist] [if exp] [in range], [replace] [nodetype] * where var = variable name, to be transformed * stem = stem of output variables * nodelist = list of nodes * nodetype = 'quartile', 'median' or 'mean' * * Constantijn Panis, 21 August 1993 * RAND Corporation, Santa Monica, California * email: panis@rand.org program define lspline version 3.0 * Make sure that the nodes are separated from the option-comma and * check that there are at least 3 arguments: parse "`*'", parse(" ,") if ("`3'"=="") { display "Usage: lspline var stem [nodelist] [if exp] [in range], [replace] [nodetype] display "where var = name of variable to be transformed" display " stem = stem of output variables" display " nodelist = list of nodes" display " nodetype = 'quartile', 'median' or 'mean'" display "Note that either nodelist or nodetype must be specified, not both." exit 198 } * Determine input variable and check that it exists local invar "`1'" capture confirm variable `invar' if (_rc!=0) { display "Error: `invar' does not exist." exit 111 } macro shift * Assign stem of output variables. Let's not get confused when the * user forgets the stem, i.e., check that stem is not numerical and * not the option-comma local stem "`1'" capture confirm number `stem' if (_rc==0) { display "Error: illegal stem for output variables: `stem'" exit 198 } if ("`stem'"==",") { display "Error: stem for output variables needs to be specified" exit 198 } macro shift * Determine number of nodes, their values and check that they * are ordered local nnodes=0 local ok "ok" while ("`ok'"=="ok") { capture confirm number `1' if (_rc==0) { local nnodes=`nnodes'+1 if ("`prenode'"!="") { if (`1' <= `prenode') { display "Error: nodes must be strictly increasing" exit 99 } } local node`nnodes' `1' local prenode `1' macro shift } else { * argument may only be "", "if", "in" or ",". * without these statements, syntax errors would be reported * as illegal varlist, which would be confusing. if ("`1'"!="" & "`1'"!="if" & "`1'"!="in" & "`1'"!=",") { display "Usage: lspline var stem [nodelist] [if exp] [in range], [replace] [nodetype] display "where var = name of variable to be transformed" display " stem = stem of output variables" display " nodelist = list of nodes" display " nodetype = 'quartile', 'median' or 'mean'" display "Note that either nodelist or nodetype must be specified, not both." exit 198 } local ok "" } } * Now parse the rest, if any, for "if", "in", or options "replace" * "quartile", "median" "mean" local if "optional" local in "optional" local options "REPLACE Quartile MEDian MEAn" parse "`*'" * No nodes must be specified if quartile, median or mean is specified; * at least one node must be specified if none of these options applies. if (`nnodes'==0) { * Determine the nodes; make sure only one option was specified. if ("`quartile'"=="quartile") { if ("`median'"=="median" | "`mean'"=="mean") { display "Error: only one of 'quartile', 'median' and 'mean' may be specified" exit 198 } qui sum `invar' `if' `in', detail if (_result(1)<3) { display "Error: fewer than 3 observations" exit 499 } local nnodes 3 local node1 = _result(9) local node2 = _result(10) local node3 = _result(11) } else if ("`median'"=="median") { if ("`quartile'"=="quartile" | "`mean'"=="mean") { display "Error: only one of 'quartile', 'median' and 'mean' may be specified" exit 198 } qui sum `invar' `if' `in', detail if (_result(1)<1) { display "Error: no observations" exit 499 } local nnodes 1 local node1 = _result(10) } else if ("`mean'"=="mean") { qui sum `invar' `if' `in' if (_result(1)<1) { display "Error: no observations" exit 499 } local nnodes 1 local node1 = _result(3) } else { display "Error: no nodes are specified. Program will select nodes" display "automatically with options 'quartile', 'median' or 'mean'" exit 198 } } else if ("`quartile'`median'`mean'"!="") { * nodes may not be specified in combination with 'quartile', * 'median' or 'mean' display "Error: nodelist may not be specified with `quartile'`median'`mean'" exit 198 } * Add extra condition "invar!=." to if-statement. This is necessary * since Stata would make max(.,n)=n, rather than missing. if ("`if'"=="") { local if "if `invar'!=." } else { local if "`if' & `invar'!=." } * Make sure that output variables do not exist, or may be replaced local i=1 while (`i'<=`nnodes'+1) { capture confirm variable `stem'`i' if (_rc==0 & "`replace'"=="") { display "Error: `stem'`i' already exists." exit 110 } local i=`i'+1 } ********************************* * All is OK: program will work. * ********************************* * Initialize output variables (unless they already exist) local i=1 while (`i'<=`nnodes'+1) { capture confirm variable `stem'`i' if (_rc==0) { * variable already exists; make sure it is of type float: local oldtype: type `stem'`i' if ("`oldtype'"!="float") { qui gen float _temp = `stem'`i' drop `stem'`i' rename _temp `stem'`i' } } else { * variable does not yet exist qui gen float `stem'`i'=. } local i=`i'+1 } * Generate linear spline transformations of type float: local i=1 while (`i'<=`nnodes'+1) { if (`i'==1) { local formula "min(`invar',`node1')" } else if (`i'<=`nnodes') { local lo=`i'-1 local hi=`i' local formula "max(min(`invar'-`node`lo'',`node`hi''-`node`lo''),0)" } else { local formula "max(`invar'-`node`nnodes'',0)" } display " `stem'`i' = `formula'" qui replace `stem'`i' = `formula' `if' `in' label var `stem'`i' "`formula'" local i=`i'+1 } * Warn against variables that look like spline transformations, * but were (probably) created in an earlier run. local i=`i'+1 capture confirm variable `stem'`i' if (_rc==0) { display _newline "Warning: `stem'`i' was not created by this run, but it does exist." } end