P3. 循环语句
整理人:冷萱 (西南财经大学)
邮箱: riemlx@13.com
实证研究中经常需要做一些重复性工作,比如分年度回归、分行业回归,等等。重复性工作最为无聊,但是也恰恰是计算机最为擅长的。本讲介绍如何用 Stata 中的循环语句来高效完成重复性工作。
本节介绍三类循环语句,包括:条件循环、针对数字的循环和针对变量、暂元、文件的循环,涉及的 Stata 语句包括:while
、forvalues
和 foreach
等。
1. while 循环语句
几乎所有的计算机语言中都包含 while
语句,其基本语法是:
while (条件){
动作 }
在每一轮循环中,Stata 首先判断「条件」是否满足,若满足,则执行 {
和 }
之间的语句,即执行「动作」,否则便会停止循环。
日常生活中,我们几乎每天都在执行这条语句,比如,你是个自律的人,要求自己晚上 11 点必须上床睡觉:
clock = 22:00while clock<=23:00{
玩手机
clock = clock + 10min }
上述语句对应的场景是:晚上 10 点钟,你开始「玩手机」(动作),每隔 10 分钟你都会看一下墙上的挂钟,看看是否到了睡觉时间 (条件)。如果没到 (条件满足),那就继续玩 (循环继续),直到 11 点上床睡觉 (循环结束)。
再看一个可以用 Stata 实操的小例子,:
local j = 0
while `j'<5{
`j'
dis local j = `j'+1
}
*-结果:
0
1
2
3 4
具体解释如下: - 初始状态:local j=0
,我们通过定义暂元 j
来设定初始值; - 判断条件:j'<5** - 动作:**dis \
j’,显示暂元 j
中的内容 - 计数器:最后一条语句,让 j
的数值自动增加 1。
下面是对以上循环的解释。首先定义暂元等于 0,也就是计数器。接下来用 while 语句定义一个条件 j<5 ,从语法上讲有两个是非常重要的,左侧花括号与右侧花括号一定要配对出现,而且从写法上讲左侧花括号以后就不再写任何内容,右侧花括号另起一行,也是不再写任何内容,中间的部分就是 Stata 重复执行的命令。现在具体解读一下这几行命令。在第一轮时 j 取 0,0<5,该条件为真,所以 Stata 继续执行里面的内容,执行内容里有两行,第一行是在屏幕上显示 j 的内容,显然会显示 0,接下来 j 被重新赋值为 0+1,变成 1,等到第二轮 j<5,它会继续执行……等到程序完成的时候,它会显示 0,1,2,3 和 4。
这种条件语句被广泛地用在求极大值和极小值的问题上,因为在定义极大值极小值的时候,是在两次计算出来的目标值之间比较。而在运用 Stata 写循环时候,这类循环使用相对较少。更多的是使用 forvalues
和 foreach
循环语句。
2. forvalues 循环语句
比较常用的是本节介绍的第二部分 forvalues 语句,即针对数字进行循环。以下是具体的写法。
forvalues i = 1(2)14{
`i'
dis }
所用的命令是 forvalues
,i 是一个自动的计数器,实际上是一个等差数列,等差数列的起始值是 1,结束值是 14,公差是 2,运行结束屏幕上将显示 1,3,5,7,9,11,13,与 while 语句相比,没有给暂元赋值,原因是 forvalues 语句中()会自动定义增加的数值。
举一个比较实用的例子:分行业回归分析。
sysuse nlsw88, clear
global yx "wage hours collgrad ttl_exp"
forvalues i = 1/4{ //公差为 1 的等差数列
_n(2) in yellow "Occupation == " in green `i'
dis reg $yx if occupation==`i'
est store m`i'
}s(r2_a N) esttab m1 m2 m3 m4, nogap
在上述命令中,对应的含义分别为:导入 Stata 系统自带数据集 nlsw88;第二行表示定义全局宏 yx 为 wage hours collgrad ttl_exp,在下面命令使用中$yx 等价于 wage hours collgrad ttl_exp;第三行设定循环语句;第四行用黄色的语句来显示“occupation 等于什么,然后再用绿色语句显示 i 的内容;第五行根据职业类别进行回归;第六行将回归结果保存;第七行是将回归结果全部呈现在屏幕。以下是屏幕上呈现的内容:
Occupation == 1
of obs = 317
Source | SS df MS Number F(3, 313) = 11.90
-------------+---------------------------------- F = 0.0000
Model | 1305.36065 3 435.120218 Prob >
Residual | 11440.8581 313 36.5522624 R-squared = 0.1024
-------------+---------------------------------- Adj R-squared = 0.0938
Total | 12746.2188 316 40.3361354 Root MSE = 6.0458
------------------------------------------------------------------------------
wage | Coef. Std. Err. t P>|t| [95% Conf. Interval]
-------------+----------------------------------------------------------------
hours | .0095841 .0331267 0.29 0.773 -.0555951 .0747634
collgrad | 2.913874 .68075 4.28 0.000 1.57445 4.253299
ttl_exp | .3785845 .0905836 4.18 0.000 .2003547 .5568143_cons | 3.648188 1.72804 2.11 0.036 .2481452 7.04823
------------------------------------------------------------------------------
Occupation == 2
of obs = 263
Source | SS df MS Number F(3, 259) = 10.79
-------------+---------------------------------- F = 0.0000
Model | 1646.23998 3 548.746659 Prob >
Residual | 13168.1622 259 50.842325 R-squared = 0.1111
-------------+---------------------------------- Adj R-squared = 0.1008
Total | 14814.4022 262 56.5435197 Root MSE = 7.1304
------------------------------------------------------------------------------
wage | Coef. Std. Err. t P>|t| [95% Conf. Interval]
-------------+----------------------------------------------------------------
hours | .0492543 .0559042 0.88 0.379 -.0608304 .1593389
collgrad | 4.453559 .9807532 4.54 0.000 2.522293 6.384824
ttl_exp | .3417837 .1083125 3.16 0.002 .1284984 .5550689_cons | 2.785092 2.83515 0.98 0.327 -2.797788 8.367971
------------------------------------------------------------------------------
Occupation == 3
of obs = 726
Source | SS df MS Number F(3, 722) = 14.36
-------------+---------------------------------- F = 0.0000
Model | 1038.09946 3 346.033155 Prob >
Residual | 17398.2126 722 24.0972473 R-squared = 0.0563
-------------+---------------------------------- Adj R-squared = 0.0524
Total | 18436.312 725 25.4293959 Root MSE = 4.9089
------------------------------------------------------------------------------
wage | Coef. Std. Err. t P>|t| [95% Conf. Interval]
-------------+----------------------------------------------------------------
hours | .0290758 .0206129 1.41 0.159 -.0113926 .0695441
collgrad | 1.916592 .5706926 3.36 0.001 .796177 3.037007
ttl_exp | .2059178 .0408893 5.04 0.000 .1256416 .2861939_cons | 3.285375 .8204117 4.00 0.000 1.674697 4.896052
------------------------------------------------------------------------------
Occupation == 4
of obs = 102
Source | SS df MS Number F(3, 98) = 4.67
-------------+---------------------------------- F = 0.0043
Model | 926.231653 3 308.743884 Prob >
Residual | 6483.74127 98 66.1606252 R-squared = 0.1250
-------------+---------------------------------- Adj R-squared = 0.0982
Total | 7409.97292 101 73.3660685 Root MSE = 8.1339
------------------------------------------------------------------------------
wage | Coef. Std. Err. t P>|t| [95% Conf. Interval]
-------------+----------------------------------------------------------------
hours | .1689017 .0642062 2.63 0.010 .0414866 .2963168
collgrad | -.4487836 2.110148 -0.21 0.832 -4.636304 3.738737
ttl_exp | .3357523 .1782149 1.88 0.063 -.0179093 .6894139_cons | -1.035403 2.683117 -0.39 0.700 -6.359962 4.289157
------------------------------------------------------------------------------
s(r2_a N)
. esttab m1 m2 m3 m4, nogap
----------------------------------------------------------------------------
(1) (2) (3) (4)
wage wage wage wage
----------------------------------------------------------------------------
hours 0.00958 0.0493 0.0291 0.169**
(0.29) (0.88) (1.41) (2.63)
collgrad 2.914*** 4.454*** 1.917*** -0.449
(4.28) (4.54) (3.36) (-0.21)
ttl_exp 0.379*** 0.342** 0.206*** 0.336
(4.18) (3.16) (5.04) (1.88)_cons 3.648* 2.785 3.285*** -1.035
(2.11) (0.98) (4.00) (-0.39)
----------------------------------------------------------------------------
r2_a 0.0938 0.101 0.0524 0.0982N 317 263 726 102
----------------------------------------------------------------------------statistics in parentheses
t p<0.05, ** p<0.01, *** p<0.001 *
3. foreach 循环语句
接下来第三种循环语句是 foreach 语句。forvalues 语句只能针对有规律的等差数列进行运算,有时候需要针对某一些变量或者文件名称进行循环,这时就需要 foreach 这种更一般化的循环语句,其中分为几种情形:
3.1 任意格式的 foreach 语句
任意格式的 foreach
语句,写法是 foreach v in…,v 是 Stata 自动定义的暂元。假如你想购买一辆汽车,目前你只考虑购买奥迪、奔驰、宝马 3 种品牌。你朋友发给你了 300 份这相关的文本文件「其实你只是需要 3 份关于奥迪、奔驰、宝马的信息」。如果使用 excel 打开分析,你觉得可能不像一个经济学者做的事情,于是你需要要把 3 份文本文件转化成 Stata 格式,然后进一步希望把这 3 份合并成完整的数据文件。此时 Stata 的循环语句就派上用场了。第一步,将文本文件转换为 Stata 格式的文件;第二不,将前面转换好的文件合并在同一个文档中「如果你还在选择复制粘贴,那你就 out 了」 。
*-将 txt 文件转化为 dta 格式foreach file in Audi Benz BMW{
insheet using `file'.txt,clear
save `file'.dta, replace
}
上述语句场景是:拿到资料后,你开始「将文本文件导入 Stata 」(动作),检查一下是不是你想要的关于奥迪、奔驰、宝马的资料 (条件)。如果是 (条件满足),那就将资料导入(循环继续),直到这 3 个文本都成功导入 (循环结束)。
*-合并样本
use Audi.dta, clear
foreach file in Benz BMW{
append using `file'.dta
}
为了方便起见,你希望把刚刚生成的 3 个 Stata 文件合并到一起。此时需要用到的命令是append
,当然,依然需要用到循环,不然全都复制粘贴,哪有空闲时间玩手机「不,是学习其他的」。上述循环也就不难理解,先导入Audi 的资料,接着判断其他的是不是你想要的 Benz 和 BMW (条件满足),如果是 (条件满足),那就将数据集纵向合并(循环继续),直到全部合并介绍。
3.2 针对变量名的 foreach 语句
下面是第二类的 foreach
语句,foreach v of varlist…,是针对变量名进行循环。先看第一个例子,针对多个变量进行缩尾处理 (Winsorized)。在这个例子里先调入美国 1988 年美国妇女工资水平的资料,接下来把一系列变量名称都放入 vars 这个暂元里,循环的第一轮会把 wage 放在 v 这个暂元里,针对 wage 进行缩尾处理,产生的新变量是 wage_w,依次对剩下几个变量进行缩尾处理。
sysuse nlsw88, clear
local vars "wage hours ttl_exp grade"
foreach v of varlist `vars'{
`v' , gen(`v'_w) p(0.01)
winsor }
当然,连老师以前也编写过可以一次性处理多个变量的命令 winsor2,具体可以:
help winsor2 //便捷的命令
3.3 暂元循环
第三类的 foreach 语句是暂元循环,foreach cc of local…,这里的 vars 并没有用暂元的方式,与第二类主要的区别是,这一类 local 后只需要暂元的名称,可以发现,关于第二类和第三类完全根据自己的喜好来决定。具体可以对比 3.2 节和 3.3 节内容。
sysuse auto,clear
local vars "price weight length"
foreach v of local vars{
gen `v'_2 = `v'^2
}