P3. 循环语句

整理人:冷萱 (西南财经大学)
邮箱:

实证研究中经常需要做一些重复性工作,比如分年度回归、分行业回归,等等。重复性工作最为无聊,但是也恰恰是计算机最为擅长的。本讲介绍如何用 Stata 中的循环语句来高效完成重复性工作。

本节介绍三类循环语句,包括:条件循环针对数字的循环针对变量、暂元、文件的循环,涉及的 Stata 语句包括:whileforvaluesforeach 等。

1. while 循环语句

几乎所有的计算机语言中都包含 while 语句,其基本语法是:

while (条件){
   动作
}

在每一轮循环中,Stata 首先判断「条件」是否满足,若满足,则执行 {} 之间的语句,即执行「动作」,否则便会停止循环。

日常生活中,我们几乎每天都在执行这条语句,比如,你是个自律的人,要求自己晚上 11 点必须上床睡觉:

clock = 22:00
while clock<=23:00{
   玩手机
   clock = clock + 10min
}

上述语句对应的场景是:晚上 10 点钟,你开始「玩手机」(动作),每隔 10 分钟你都会看一下墙上的挂钟,看看是否到了睡觉时间 (条件)。如果没到 (条件满足),那就继续玩 (循环继续),直到 11 点上床睡觉 (循环结束)。

再看一个可以用 Stata 实操的小例子,:

local j = 0
    while `j'<5{
    dis  `j'
    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 写循环时候,这类循环使用相对较少。更多的是使用 forvaluesforeach 循环语句。

2. forvalues 循环语句

比较常用的是本节介绍的第二部分 forvalues 语句,即针对数字进行循环。以下是具体的写法。

forvalues i = 1(2)14{
  dis  `i'
}

所用的命令是 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 的等差数列
         dis _n(2) in yellow "Occupation == " in green `i'
         reg $yx if occupation==`i'
         est store m`i'
}
esttab m1 m2 m3 m4, nogap s(r2_a N)

在上述命令中,对应的含义分别为:导入 Stata 系统自带数据集 nlsw88;第二行表示定义全局宏 yx 为 wage hours collgrad ttl_exp,在下面命令使用中$yx 等价于 wage hours collgrad ttl_exp;第三行设定循环语句;第四行用黄色的语句来显示“occupation 等于什么,然后再用绿色语句显示 i 的内容;第五行根据职业类别进行回归;第六行将回归结果保存;第七行是将回归结果全部呈现在屏幕。以下是屏幕上呈现的内容:

Occupation == 1

      Source |       SS           df       MS      Number of obs   =       317
-------------+----------------------------------   F(3, 313)       =     11.90
       Model |  1305.36065         3  435.120218   Prob > F        =    0.0000
    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

      Source |       SS           df       MS      Number of obs   =       263
-------------+----------------------------------   F(3, 259)       =     10.79
       Model |  1646.23998         3  548.746659   Prob > F        =    0.0000
    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

      Source |       SS           df       MS      Number of obs   =       726
-------------+----------------------------------   F(3, 722)       =     14.36
       Model |  1038.09946         3  346.033155   Prob > F        =    0.0000
    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

      Source |       SS           df       MS      Number of obs   =       102
-------------+----------------------------------   F(3, 98)        =      4.67
       Model |  926.231653         3  308.743884   Prob > F        =    0.0043
    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
------------------------------------------------------------------------------

.         esttab m1 m2 m3 m4, nogap s(r2_a N)

----------------------------------------------------------------------------
                      (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.0982
N                     317             263             726             102
----------------------------------------------------------------------------
t statistics in parentheses
* 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'{
       winsor `v' , gen(`v'_w) p(0.01)
}

当然,连老师以前也编写过可以一次性处理多个变量的命令 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
}