capture#

video

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