1 标准的程序文件格式
我们已经多次强烈建议大家尽量用程序来完成自己的工作。在写程序时,有一些经验写法,遵循这些写法将会提高工作效率。
clear //相当于让 STATA 处于初始状态,清除所有使用过的痕迹
version9 //由于不同版本命令等略有不同,因此最好事先指明版本号
cd d:/stata9 //设定路径,将数据、程序和输出结果文件均存入该文件夹
capture log close /*如某结果输出文件已被打开,则关闭之,
若没有,则忽略该命令*/
log using myfile, replace //将运行结果存到一个输出文件 myfile 中
set more off //在程序执行过程中,不要因为结果窗口屏幕已满而停下来
log off //暂时关闭结果记录功能,以下的执行和结果均不记录
*下面开始写完成特定任务的命令,如
sysuse auto, clear
sum
log on //打开结果记录功能,以下命令和结果记录
tab forei
log close //关闭结果输出文件,在前面设定的文件目录中可以找到。
Log 命令记录所有已执行命令或执行结果, 结果文件的存贮类型有两种,一种后缀名为.smcl,一种为.txt,如果不指明为 txt,默认为*.smcl。后面的 replace选项用于覆盖原来的同名结果文件。
2 创造自己的命令
Capture的作用:这是因为 stata 按顺序来执行命令,比如执行 program drop hello,然而 hello 却不在内存中。因此,标准的程序中通常要加上一个灵活选择性命令 capture,意思是,如果有hello ,则删除,如果没有,则跳过这条命令,执行下一条命令
3 暂元 Macros: local/global
暂元是程序中的临时变量,分为暂元名和暂元内容两部分,类似于变量名和变量值。
sysuse auto, clear
list price length weight in 1/5
local v3 “price length weight” //将 price length weight 这组字符赋给暂元名 v3
list `v3’ in 1/5 /*等价于 list price length weight in 1/5。注意`v3’中,左边不是单引号,而是标准键盘上第二排最左边的哪个撇号。*/
local cmd “list” //将 list 这组字符赋给暂元名 cmd
`cmd’ `v3’ in 1/5 //等价于 list price length weight in 1/5
local pre “pri” //将 pri 这组字符赋给暂元名 pre
local suf “ce” //将 ce 这组字符赋给暂元名 suf
`cmd’ `pre’`suf’ //等价于 list price
tab rep`=26*3’ //等价于 tab rep78, 注意到 26*3=78
*类似地可以使用 global 来定义暂元
sysuse auto, clear
list price length weight in 1/5
global v3 “price length weight” //将 price length weight 这组字符赋给暂元名 v3
list $v3 in 1/5 //等价于 list price length weight in 1/5。
global cmd “list” //将 list 这组字符赋给暂元名 cmd
$cmd $v3 in 1/5 //等价于 list price length weight in 1/5
Global 与 local 的区别
capt prog drop myprog
program myprog //定义程序 myprog
local i="主程序局域" //定义局域暂元 i
global j="主程序全局" //定义全局暂元 j
di as txt "`i' " //调用局域暂元 i,这里是显示暂元名 i 中的内容“主程序局域”,绿色
di as txt "$j" //调用全局暂元 j,这里是显示暂元名 j 中的内容“主程序全局”,绿色
mysub //执行子程序体 mysub
di as error "`i' " //再次调用局域暂元 i,显示暂元名 i 中的内容“主程序局域”
di as error "$j" //再次调用全局暂元 j,显示 “子程序全局暂元” ,红色
end
capt prog drop mysub
prog mysub //定义子程序 myprog
local i="子程序局域" //定义子程序中的局域暂元 i
global j="子程序全局" //定义子程序中的全局暂元 j
di as result "`i'" //调用局域暂元 i,这里是显示暂元名 i 中的内容“子程序局域暂元”
di as result "$j" //调用全局暂元 j,这里是显示暂元名 j 中的内容“子程序全局暂元”
end
myprog //执行主程序体 myprog
在上述程序的执行过程中,首先显示主程序所定义的暂元内容。然后调用子程序,显示子程序中的命令规定的暂元内容。再回到主程序,此时在主程序中用 local 所定义的暂元并没有因为子程序的重新定义或调用而改变,仍然保持着在主程序中所定义的内容,即“主程序局域”,但是由 global 所定义的全局暂元却发生了改变,变为由子程序所更新定义后的“子程序全局暂元”。
4 自带命令参数
实际上,命令参数可以是字符,数值,也可以是变量,矩阵,文件等任何内容。命令参数也不仅限于一个,可以是多个。
保存以下代码为 listargs.do
captu prog drop listargs
prog listargs
di "第一个参数为: `1' "
di "第二个参数为: `2' "
di "第三个参数为: `3' "
di "第四个参数为: `4' "
end
5 scalar 标量
scalar a=2 //赋予标量 a 的值为 2
dis a+2 //a+2=2+2=4
scalar b=a+3 //b=a+3=2+3=5
di b //结果窗口显示出:5
scalar s=”hello” //标量也可以为字符型
di s //结果窗口显示出: hello
clear
set obs 3
input a
1
3
5
end
scalar b=3
gen v1=a*b //回忆线性代数,该命令相当于对 a 的每个值乘 b(即 3)
list
input b
2
3
4
end
gen v2=a*b //注意 v1 与 v2 这两个命令完全相同
list //v1 与 v2 结果不同,表明 STATA 优先进行变量相乘
gen v3=a*scalar(b) //当 b 即是变量名又是标量名时,标量相乘时要指明标题
list
说明:当变量名和标量名相同时,STATA 将优先使用变量名,如果要强制使用标题,需要用 scalar(varlist)
6 临时变量和临时数据文件:tempvar 和 tempfile
在我们的数据处理过程中,会产生一些中间的临时性变量,如果随着退出 STATA,这些临时的中间性变量能自动消失,既不会破坏原始数据,也不会因为这些干扰变量妨碍我们查看结果性的变量。
clear
set obs 5
tempvar x y //指明临时变量名为 x 和 y
gen `x'=_n //生成临时变量`x’,取值为 1,2,3,4,5
gen `y'=_N //生成临时变量`y’,取值为 5,5,5,5,5
edit //查看数据编辑器,却并没有变量
gen z=`x'+`y' //将两个临时变量相加,得到新变量 z
edit //查看数据库,只有新变量 z
在程序的运行过程中,我们需要破坏内存中的数据文件来获得预期结果,但我们并不希望改变内存的数据,和临时变量一样,我们可以依赖临时数据文件。一般使用preserve….restore,这两个命令之间的命令不会对数据造成任何破坏,但是一个可能的风险是:他人在运行该文件时强行中止,来不及执行 restore。这会使得数据仍然被破坏。而临时数据文件则不存在这样的风险。
use auto, clear
preserve //表明下面的操作将不破坏当前数据中的文件
keep price weight //仅保留 price 和 weight 这两个变量
save master, replace //保存新数据文件 master,该数据包含两个变量
drop weight //再去掉变量 weight,当前数据中只剩一个变量 weight
save part1,replace //保存新数据文件 part1,该数据包含一个变量
use master, clear
drop price
rename weight price
append using part1
erase master.dta
erase part1.dta
restore //回到 preserve 命令之前的数据集,即 auto
/*这是一个坏程序,因为中间性数据文件 master 和 part 并不是我们想要的,即使最后有 erase命令将其删除,一旦在此前强行退出,仍会导致这两个不必要的文件占用空间并造成混乱。
preserve
keep price weight
tempfile master part1
save “`master’”
drop weight
save “`part1’”
use “`master’”, clear
drop price
rename weight price
append using “`part1’”
restore
/*在这种情形下,无论是出现运行错误还是中途中止,都不会破坏原数据文件,也不会产生
垃圾文件。*/
7 基尼系数命令的创建案例
1 背景知识:基尼系数
2 粗糙且只能使用一次的程序
*如果为上面的这段程序加上程序头和程序尾,并取名为 gini,则该程序将能重复执行。
capture program drop gini //如果 gini 命令已存在,则删除,否则跳过该步
program gini //定义命令名为 gini
gen m=inc/hhsize //m 为人均纯收入
sort m //按人均纯收入由小到大排序
egen tp=sum(hhsize) //求总人口 tp
egen tinc=sum(inc) //求总收入 tinc
gen p=hhsize/tp //求每个家庭人口占总人口的比例 p
gen w=inc/tinc //求家庭纯收入占总收入的比例 w
gen q=sum(w) //求按人均纯收入排序后的累积收入比例
gen gini=1-sum(p *(2*q-w)) //计算出基尼系数(即 gini 变量的最后一个数)
dis gini[_N] //显示基尼系数
end
use hb97,clear
gini //执行该命令,得到计算结果
*.26685518
3 变量声明: args
上面的命令只能用于计算家庭纯收入的基尼系数,如果我们想计算现金收入或者食品消
费的基尼系数?就必须重新修改命令,至少需要将上面程序中的 inc 替换为 cash 或者 food,
有没有一种办法,使得我们不必修改程序,每次使用时,只要在 gini 命令之后带上要计算
的变量即可?*/
capture program drop gini //如果 gini 命令已存在,则删除,否则跳过该步
program gini //定义命令名为 gini
args inc hhs //规定命令 gini 后要带两个变量 inc 与 hhs
egen tinc=sum(`inc') //计算 gini 命令后第一个变量(收入支出等)的总和
egen tp=sum(`hhs') //计算 gini 命令后第二个变量(人口)的总和
gen m=`inc'/`hhs' //计算人均水平值 m
sort m //按人均水平值排序
gen gini=1-sum(`hhs'/tp*(2*sum(`inc'/tinc)-`inc'/tinc)) //计算基尼系数
dis gini[_N] //显示基尼系数
end //程序结束
use hb97,clear
gini cash hhsize //计算现金收入的基尼系数
*.35139242
4 用1’
2’ 等来声明命令后的参数
STATA 也提供了另一种选择,不需要使用 args 语句,请看下面的做法*/
capture program drop gini //如果 gini 命令已存在,则删除,否则跳过该步
program gini //定义命令名为 gini
egen tinc=sum(`1') //计算 gini 命令后第一个变量(收入支出等)的总和
egen tp=sum(`2') //计算 gini 命令后第二个变量(人口)的总和
gen m=`1'/`2' //计算人均水平值 m
sort m //按人均水平值排序
gen gini=1-sum(`2'/tp*(2*sum(`1'/tinc)-`1'/tinc)) //计算基尼系数
dis gini[_N] //显示基尼系数
end //程序结束
/在上面的程序中,我们用1‘和
2’代替了前两段程序中的 inc 和 hhs。这一变换使得
gini 程序可以用于计算其他变量的不均等程度,如 /
5 使用暂元
注意到在上面的命令执行后,内存数据中冒出了许多中间变量,如 tp tinc m,gini 等等。
正是这些变量阻碍了对该命令的扩展运用。如果每执行一个命令都会生成这么多变量,将会
对原始数据带来多沉重的负担!想想 STATA 所提供的命令一般都不破坏原数据,我们能否
也不破坏原数据呢?答案是:使用暂元(临时变量) */
capture program drop gini
program gini
tempvar tinc tp m gini //设定 tinc tp m gini 四个变量为临时变量
egen `tinc’=sum(`1') //生成总收入,将总收入数据暂存在临时变量’tinc’中
egen `tp’=sum(`2')
gen `m’=`1'/`2'
sort `m’
gen `gini’=1-sum(`2'/`tp’*(2*sum(`1'/`tinc’)-`1'/`tinc’))
dis `gini’[_N] //显示基尼系数
end
6 真正的 STATA 命令语句格式
执行上述命令将不会破坏任何原来的数据文件,但美中不足的是我们仍然没法运行更复
杂的命令。想一想,STATA 自己的命令后面不仅仅可以用变量,还可以带上条件 if 或者范
围 in。我们的命令也要这样子! */
capture program drop gini
program gini
syntax varlist [if] [in] [,title(string)] //设定我们自己的命令格式
tempvar tinc tp m gini //设定 tinc tp m gini 四个变量为临时变量
marksample touse //生成一个 0/1 暂元,暂元名为 touse
preserve //将当前内存中数据暂封存,直到 restore 命令再复原
quietly { //大括号内的命令将在后台执行,前台无显示
keep if `touse’ //根据 if 后输入的条件得到一个子数据
egen `tinc’=sum(`1') //生成总收入,将总收入数据暂存在临时变量’tinc’中
if "`2'"=="" {
local 2=1
} //如果没有人口变量,则默认为该变量为 1
egen `tp’=sum(`2')
gen `m’=`1'/`2'
sort `m’
gen `gini’=1-sum(`2'/`tp’*(2*sum(`1'/`tinc’)-`1'/`tinc’))
}
display as result "`title'基尼系数为:" as error `gini'[_N] //显示基尼系数
restore
end