capture#
Let’s talk about flexible programs. We’ll discuss this with demos using transplants.dta. We started off with quite inflexible programs:
use transplants, clear
capture program drop table1
program define table1
qui {
disp "Variable, mean(SD), range"
foreach v of varlist age wait_yrs rec_wgt_kg {
quietly sum `v'
#delimit ;
noi di "`v'"
_col(15) %3.2f r(mean) "(" %3.2f r(sd) ")"
_col(30) %3.2f r(min) "-" %3.2f r(max)
;
#delimit cr
}
}
end
table1
This program is of value only to someone using transplants.dta, who happens to be interested in the mean (sd) of age wait_yrs rec_wgt_kg.
So lets try to add some flexibility to make this program useful beyond this dataset and selected variables.
We’ll achieve this task by introducing syntax varlist, something we already did two weeks ago:
use transplants, clear
capture program drop table1_v1
program define table1_v1
syntax varlist
qui {
disp "Variable, mean(SD), range"
foreach v of varlist `varlist' {
quietly sum `v'
#delimit ;
noi di "`v'"
_col(15) %3.2f r(mean) "(" %3.2f r(sd) ")"
_col(30) %3.2f r(min) "-" %3.2f r(max)
;
#delimit cr
}
}
end
table1_v1 age wait_yrs rec_wgt_kg
In this program the user specifies the variables to be included in the table1_v output. If we may recall chapter 2: r(mean):
local macro
name -> `varlist'
content -> age wait_yrs rec_wgt_kg
This snippet shouldn’t confuse you:
foreach v of varlist `varlist' {
foreach v of varlist is the syntax for a loop. It generates a local macro v
What? Yup, every loop syntax generates a local macro and its the programmer who assigns the name to the macro. In this example, the content of the macro is…
`varlist'
it is holds user-defined content as contrasted with system/programmer-defined names (as the programmer you are now a part of the system).
Now lets add some more flexibility in the next iteration of table1
use transplants, clear
capture program drop table1_v2
program define table1_v2
syntax [varlist]
qui {
disp "Variable, mean(SD), range"
foreach v of varlist `varlist' {
quietly sum `v'
#delimit ;
noi di "`v'"
_col(15) %3.2f r(mean) "(" %3.2f r(sd) ")"
_col(30) %3.2f r(min) "-" %3.2f r(max)
;
#delimit cr
}
}
end
table1_v2
What is the difference between table1_v2 and table1?
And between table1_v2 and table1_v1?
Let’s add even more flexibility:
use ../downloads/transplants, clear
capture program drop table1_v3
program define table1_v3
syntax [varlist] [if]
qui {
disp "Variable, mean(SD), range"
foreach v of varlist `varlist' {
quietly sum `v' `if'
#delimit ;
noi di "`v'"
_col(15) %3.2f r(mean) "(" %3.2f r(sd) ")"
_col(30) %3.2f r(min) "-" %3.2f r(max)
;
#delimit cr
}
}
end
table1_v3 if age<20
And this?
use transplants, clear
capture program drop table1_v4
program define table1_v4
syntax [varlist] [if], [round]
qui {
local D %3.0f
if "`round'" != "" {
local D %3.0f
}
else {
local D %3.2f
}
disp "Variable, mean(SD), range"
foreach v of varlist `varlist' {
quietly sum `v' `if'
#delimit ;
noi di "`v'"
_col(15) `D' r(mean) "(" `D' r(sd) ")"
_col(30) `D' r(min) "-" `D' r(max)
;
#delimit cr
}
}
end
table1_v4 age peak_pra, round
And the coda…
use transplants, clear
capture program drop table1_v5
program define table1_v5
syntax [varlist] [if], [title(string)] [precision(int 1)]
qui {
if "`title'" != "" {
noi di "`title'"
}
assert inrange(`precision',0,6)
local pplus = `precision'+1
local D %`pplus'.`precision'f
disp "Variable, mean(SD), range"
foreach v of varlist `varlist' {
preserve
capture keep `if'
quietly sum `v'
#delimit ;
noi di "`v'"
_col(15) `D' r(mean) "(" `D' r(sd) ")"
_col(30) `D' r(min) "-" `D' r(max)
;
#delimit cr
restore
}
}
end
table1_v5 age bmi wait_yrs if age>40, precision(2) title("Study Population")
In this coda, subtle variants when compared with iterations 1-4 are introduced:
, title(string)] //title is programmer-defined, string is a programmer-constraint but the content will be user-defined
, [precision(int 1)] //precision is programmer-defined, int is a programmer-constraint but the actual integer will be user-defined
//note: %`pplus'.`precision'f has identical structure to, say, %3.2f; the integer to the left MUST > the integer to the right of '.`
local D %`pplus'.`precision'f
capture keep `if' //keeps subset defined by user; if the user doesn't use the conditional `if', the `capture' insures that Stata doesn't return an error
//this means the programmer doesn't have to include an `if' option with each command that has `syntax varlist if`
preserve
//more on this later!
restore