普林斯顿Stata教程-Stata编程

译者:谢作翰 | 连玉君 | (知乎 | 简书 | 码云)

专题链接目录

3.1 暂元

3.1.1 在暂元中存储文本

3.1.2 在暂元中存储结果

3.1.3 全局暂元与键盘映射

3.1.4 关于暂元的更多信息

3.2 循环

3.2.1 遍历数字序列循环

3.2.2遍历列表中的元素

3.2.3 循环专用列表

3.2.4 while循环

3.2.5 条件执行

3.3 编写命令

3.3.1 无参数程序

3.3.2 有参数的程序

3.3.3 复合引号

3.3.4 位置参数

3.3.5 使用Stata语法

3.3.6创建新变量

3.3.7 Coale-McNeil 拟合

3.4其他主题

这部分是对Stata编程的简单介绍。主要讨论暂元循环,并展示如何编写简单程序。编程是一个很大的主题,我在这里仅进行一些提示,希望能激发你进一步学习的兴趣。本文所涵盖的材料将帮助你更有效地使用Stata。

Stata 9 引入了一种新的非常强大的矩阵编程语言Mata。这极大扩展了程序员的工具,远远超出了这里所讨论的暂元,但是Mata是一个值得单独讨论的主题,所以本文不会对此进行介绍。然而,您在这里的努力不会白费,因为Mata仅是Stata编程的补充,而不是完全替代。 要了解关于Stata编程的更多信息,我建议您使用Kit Baum的Stata编程简介,现在已发布到第二版。您也可以查阅用户指南第18章,根据需要参考。Nick Cox 在 Stata Journal 的专栏也是学习 Stata 的绝佳资源。

3.1 暂元

暂元是一个与一些文本相关联的名称,依据范围可分为全局暂元与暂元。

3.1.1 在暂元中存储文本

暂元名称最多为31个字符,并且仅在当前环境(语境)中被识别(console,do file或programe)。

暂元定义语句为 ,输入`name'(请注意使用反引号和左引号。)进行调用。

第一种变体不带等号,用于存储任意文本,最多可达64k个字符(在Stata SE中最多可达100万个字符)。文本通常用引号引起来,但不是必须。

示例 1 :回归方程中的控制变量

有时你需要运行一堆回归方程,其中包括一组标准的控制变量,比如 。当然,你可以在每个等式中键入这些名称,或者可以剪切和粘贴名称,但这些方法很繁琐且容易出错。聪明的方法是定义一个暂元

然后,输入命令

它与一下命令完全等价

如果只运行一次回归,不需要保存信息,但是如果你必须运行多个具有不同结果或处理方式的模型,使用暂元可以提高效率并确保一致性。

这种方法还有一个好处是,如果以后你意识到你应该对某个控制变量取对数,你只需改变暂元的定义即可,比如将 改为 ,对暂元所做的改变会在所有后续模型中应用。

警告:输入不存在的暂元不是错误;,只是会返回一个空字符串。所以要小心拼写正确的暂元名称。如果输入regress outcome treatment `contrls',Stata将读取regress outcome treatment,因为暂元contrls不存在。如果你输入control的话,也会发生同样的情况,因为暂元名称不能按变量名称的方式缩写。

示例 2 :管理虚拟变量

假设你正在参与人口调查工作,受访人按年龄以5年分为一组,从 到 共分为七组。其中有六组会作为虚拟变量在回归中使用。定义一个暂元

然后在你的回归模型中使用

这不仅更短,更具可读性,而且更接近你的意图。这也使得改变对年龄的表示更为容易,如果您以后决定使用线性和二次项表示年龄,则只需定义暂元 并重新运行模型。请注意,第一个 是暂元的名称,第二个是变量的名称。在此我用引号使代码更清晰。

请注意嵌套的暂元。如果一个暂元中包含了另一个暂元(在此不妨称为子暂元),新暂元的内容是在创建那一刻就确定的(解析),而不是在被评估时被解析。例如,如果你定义暂元local controls `age' income education。在定义新暂元control时Stata认为其中包含子暂元age并对子暂元内容展开解读,就算在未来你改变了子暂元age的内容,新暂元中保留的依然是创建时点子暂元的内容与形式。

但是,有一种方法可以实现新暂元随子暂元同步更新的效果。诀窍是在定义暂元时输入斜杠号避开暂元评估字符local controls `age' income education。现在Stata不评估暂元,所以controls内容变成了`age' income education。当暂元controls被评估时,Stata才会发现它包含暂元age并对其展开解读。

3.1.2 在暂元中存储结果

第二种类型的暂元定义 (含等于号)用于存储结果。它指示Stata将右侧的文本视为表达式,对其进行评估,并在指定名称中存储结果的文本表达。

假设你只是运行一个回归,并想存储R平方结果,以便与后面的回归进行比较。你知道 命令将残差平方和存储在 中,所以你认为如下命令将会奏效:

但事实并非如此。您的暂元所存储是公式 ,如过你输入

你看到的是一条公式,而不是我们所要的数值。解决方法是使用等号连接 ,这会t提示Stata计算表达式并存储结果。

要看到差异,请尝试一下代码

当你仅仅是想存储文本信息是也可以使用等号,但我并不推荐,尤其当你使用的是旧版Stata时,会带来不必要的麻烦。以 为例,使用等号不影响正常工作,但会使右引号左边内容被Stata计算,并被标记为字符串格式——最多输入244个字符,而正常暂元文本可输入更多内容。

3.1.3 全局暂元与键盘映射

全局暂元名称最多可包含32个字符,正如名称所示,它具有全局范围。

你可以定义一个全局暂元, 并使用 进行计算。(你可能需要用 限定名称的结尾)

我建议你避免使用全局暂元,因为存在名称冲突的可能性。一个有用的应用是使用键盘上的功能键映射。如果你在一个名字很长的共享网络文件夹上工作,请尝试

当你打F5时,Stata会替换全名。而你的do files可以使用命令 (我们需要大括号来表明暂元名称是F5,而不是 )

显然你不想在每次使用Stata时输入这个暂元,你可以将其输入到你的 文件——每次运行Stata时执行的一组命令。通常情况下,你的个人配置文件最好存储在Stata的启动目录 中。 了解更多信息。

3.1.4 关于暂元的更多信息

暂元也可用于使用扩展暂元函数获取和存储关于系统或数据集中变量的信息。例如,你可以检索变量和值标签。还有一些命令可以管理你的暂元集合,包括 和 , 了解更多信息。

3.2 循环

循环用于完成重复性任务。Stata的命令允许循环遍历数字序列和包括变量列表在内的各种类型列表。在我们开始之前,不要忘记Stata自己做了很多循环。如果你想计算收入的对数,你可以在Stata中用一行代码来实现:

这行代码隐晦地在所有观测值上循环,计算每个收入的对数,又称为矢量化操作。你可以自己编写循环,但是没有必要。第一,你不需要;第二,你的代码会比Stata内置循环慢很多

3.2.1 遍历数字序列循环

基本的循环命令采用这种形式

这 是一个关键字, 是一个暂元的名称,它将被设置为序列中的每个数字, 是一个如下形式的数值范围

:从 到 以1为步长的数字序列,例如 产生1,2和 3

:从 到 以 为步长依次排列的数字序列例如15(5)50为15,20,25,30,35,40,45和50。

(指定第二种序列的方法有两种,这里列出的是最清晰的,替代方案请参阅 )

开头的左大括号必须是第一行的最后一项(注释除外),并且循环必须以独立成行的右大括号结束。循环会对序列中的每个值执行一次并将结果保存在暂元 (本例中暂元名为 )中。

创建虚拟变量

这是我最喜欢的创建年龄组虚拟变量的方式。虽然Stata 11引入了因子变量,Stata 13改进了估算表格的标签,大大减少了“滚动自己的”虚拟变量的需求,但代码仍然具有启发性。

这将创建一组虚拟变量 到 。循环时,暂元 将以5为步长在20和45范围内取值表示年龄组的下限(即20,25,30,35,40和45)。

在循环内部,我们创建一个暂元 来表示年龄段的上限,它等于下限加4。第一次循环中 是20,所以 是24.我们使用等号来存储 加4的结果

下一行是一个简单的生成语句。第一次循环时语句会显示 (你可以建立自己的暂元来查看),这将创建第一个虚拟变量,然后Stata将返回顶部创建下一个。

3.2.2遍历列表中的元素

第二个循环命令是 ,它有六种风格,以处理不同类型的列表。我将从通用列表开始:

是一个关键字, 是自己选择的暂元名称, 是另一个关键字,紧跟着是空格分隔的单词。试试这个例子

由于暂元 被设置为列表中的每个单词,因此该循环将输出“猫”“和”和*“狗” *。Stata不知道“和”不是一种动物,但即使这样做,它也不会在意,因为这个列表是通用的。

如果你想循环一个不规则的数字序列 - 例如你需要用Coale-Demeny区域模型生命表对第2,6和12级做一些事情 - 你可以编写

foreach level in 2 6 12 { ... do something with `level' ... } 这可能是你需要了解循环的全部内容。

3.2.3 循环专用列表

还有其他五种变体,它们在特定类型的列表上循环,我现在简要介绍它们。

变量列表

也许是最有用的变体

这里 , 和 是关键字,它们必须准确输入。 是一个存储变量名字的列表。你可以进行适当缩写,比如使用类型变量的名称 来指代以 开头的所有变量,或 对变量 到 进行引用。

这种循环的有点是 命令中Stata会检查列表中的每个名称是否确实是现有的变量,并且可以缩写或使用扩展名称。

如果你需要对新生成变量进行循环,使用命令 。关键字 替换原有的 ,告诉Stata检查列表中此前不存在的变量名称。

遍历暂元中词汇

其他两个变体循环遍历本地或全局暂元中的单词,格式是分别使用关键字 或 ,后跟暂元名称(代替列表名)。例如,这里有一种方法可以从暂元中列出控制变量名称

在数据集中我们可以使用 和 来实现相同目的。

数字列表

Stata中有一个 变体专门用于处理 无法处理的数字列表。

假设一项调查在1980年开始进行,并在1985年和1995跟进。(在1990年虽然他们同样希望跟进,但并未得到资助),要对间隔不规则数字列表进行循环,你可以使用

你可以做一些比打印年份名更有趣的事情,比如将 指定为1 2 3或1/5((意思是1 2 3 4 5)),或是1(2)7(从1到7以2为步长计数以得到1 3 5 7); 更多信息 。

这个命令优于优点通用命令的地方是Stata会检查数字列表中的每个元素是否为一个数字。

3.2.4 while循环

与许多编程语言一样,Stata也有一个while循环,它具有以下结构

条件是表达式。只要条件为真(非零),循环就会执行。通常情况下,内部循环时会有条件不满足,否则代码将永远运行。

循环一种典型用途是迭代估计,当连续估计的差值大于预设的容差循环继续进行。迭代计数通常用于检测对象是否缺乏收敛性。

命令将会中断包括 , , 在内的所有循环。该命令将停止当前迭代并继续下一个迭代,除非已经指定了 ,否则将退出循环。

3.2.5 条件执行

Stata的也有一个 编程命令,为了不与限定子集的限定符 混淆(如 ),该 命令具有以下结构

这里 和可选的 是关键字, 了解表达式信息。 必须是一行中最后一项(注释除外), 必须单独成行。

如果 或 部分由单个命令组成,便不需要大括号并且可以单独成行,如 。但不可以使用括号情况下同行成列如 。你可以将代码分成三行来使用大括号,这通常会提高代码的可读性。

在这里我们设置一个简单的循环,在十次迭代的五次之后停止,

3.3 编写命令

我们接下来的任务是编写自己的Stata命令,跟随我们开发几个简单的程序,一个用于标注你的输出( sign your output),另一个来评估Coale-McNeil模型的婚礼安排,我们可以创建如下所示图表:

3.3.1 无参数程序

让我们开发一个命令,以你的名字标注你的输出。开发一个命令最简单的方法是从一个do文件开始。启动Stata的do-file编辑器(Ctrl+9)并输入:

如果你现在输入 ,Stata将使用文本样式显示签名

的 在需要改变 内容或重新运行该文件时使用,类似于数据集中的 命令。因为你不能定义一个已经存在的程序。在一次使用时需要 ,此时没有什么程序需要删除。

表明该命令是为Stata版本9.1开发的,可以帮助未来版本的Stata正确运行它,即使语法在此期间发生了变化。

最后一行使用了一些 (Stata Markup Control Language),发音为“smickle”,Stata标记控制语言是Stata输出处理器的名称。 使用纯文本结合大括号中。例如, 将显示模式设置为文本, 绘制一个宽度为62个字符的水平线。了解更多关于SMCL类型的信息 。

3.3.2 有参数的程序

为了制作有用的程序,你经常需要在命令后面输入参数以传递信息给它们。让我们写一个能够回应你所说的话的命令:

尝试输入 看看会发生什么。

当你调用 命令时,Stata将参数存储在名为 的暂元中,并且用 命令将暂元结果以文本呈现,由于是文本,所以带上双引号。(假如你输入 ,暂元 由空白变为 ,命令会读取 ,此时stata会报错无法找到 命令,我们希望命令读取`display “Hi”,因此为暂元添加引号)

如果我们没有指定任何内容,则暂元 将是一个空字符串,该命令将读取 并且Stata将打印一个空行。

3.3.3 复合引号

在庆祝之前,我们需要解决新命令的一个小问题。输入 ,Stata会报错。为什么呢?因为 中含有引号在读取命令显示为:

Stata理解的则是 ,两个引号包围的词夹着没有引号的 ,这在Stata看来是一句无效表达。显然我们需要一些方法来区分内部和外部引用。

顺便提一句,你可以通过 命令看到Stata执行的整个过程(如下所示)。完成后别忘了输入 。 以了解更多信息。

那么问题该如何解决呢?答案是使用Stata的复合引号: 开始, 结束,例如 。由于打开和关闭符号不同,这些引号可以嵌套。复合引号可以在任何使用双引号的地方使用。如果引用的文本包含双引号,则必须使用复合引号。 所以我们的最终版本是 。

你会注意到我删去了 这是因为我们现在准备将程序保存为 文件。输入 以找出您的个人 目录位置,然后将文件保存为 。现在你可以在任意时刻调用该命令。

(ps,如果要确认新建命令没有和官方命令重名,输入 ,得到回复 即可)

3.3.4 位置参数

Stata除了将所有参数一起存储在暂元 ,还会把参数的文本分别存储在暂元'1','2','3'等等之中(使用参数作为分隔符),并在这些暂元中对解析参数。

通常你会先解析暂元 然后转向下一个。这时 命令会派上用场,因为它将所有的暂元都向下移动一个,所以现在2的内容在1,3的内容在2等等。这样,你只需一直对暂元 处理即可,直到列表耗尽时 内容为空,你的工作就完成了。

下是显示参数内容的规范程序:

不要忘记 ,否则你的程序可能会永远运行。(或者直到你按下 )

尝试 和 。体会如何通过使用引号将多个单词分组为单个参数。

(ps:我们不仅可以将参数传递给命令,还可以将其传递给do files, 以便了解更多信息。)

3.3.5 使用Stata语法

如果你使用标准的stata语句,你可以充分利用stata自己的解析器,它可以方便地将所有元素存储在暂元中供你使用,不过一个标准的stata语句意味你的参数必须是一个变量列表,或是权重, / 子句,或是一堆选项中的一种或多种。

一个命令原型让我们编写一个命令,用一个给定的均值,标准差和结婚比例的Coale-McNeil模型计算一定年龄段的结婚概率。我们设计的使用语法是:

对照语法,我们需要包含确切年龄数据的年龄变量,以及一个必须选项——在给定结婚比例时生成一个新变量。还有一些可选选项指定平均值,标准差和结婚比例,当然这些都预设有默认值。 因此,我们需要一个具有确切年份的年龄的现有变量,以及一个强制选项,指定一个以已婚比例生成的新变量。还有一些选项可以指定时间表中的平均值,标准偏差和结婚比例,所有这些都有默认值。这是命令的第一步

首先要注意的是,该 命令看起来非常像我们的原型。

变量列表

在我们的语法中的第一个元素是一个变量列表 。你可以指定最小值和最大值,例如一个需要两个变量的程序 。当我们只有一个变量时,可以输入 ,这是 的简称。

然后,Stata将确保您的程序调用时使用的是一个现有变量的名称,该变量将被存储在名为 的暂元中。(t即使你只有一个变量并在你的语法语句中使用 ,暂元名依然是 )尝试 ,Stata会抱错,说“variable nonesuch not found”。

(如果你之前有过编程经验,并且花费大部分时间用于检查输入错误,仅少部分真正用于手头任务,你将会非常喜欢 ,因为它为你执行了很多错误检查)

选项和默认值

可选语法元素包含在方括号[]中。在我们的命令中, 选项是必需的,其他三个是可选的。尝试使用这些命令生成一个范围在15到50的年龄变量小测试数据集

现在尝试 。这次Stata并未对 变量报错,但提示 'option generate() required'。带参数的选项需要指定参数类型(integer,real,string,name)和默认(不强制)值。 需要一个 ,没有默认值。尝试 。Stata会抱怨说2不是一个变量名。

正常情况下,选项的内容将存储在与选项同名的暂元中。

检查参数现在我们需要做一些工作来检查名称是否是一个有效的变量名称,我们使用 :

然后Stata检查你是否可以生成这个变量,如果不行,则发出错误110.尝试 ,Stata会说 'age already defined'。

现在你应该清楚,stata会检查均值,标准差和结婚比例这三个选项是否有指定数值(缩写为 , 和 ),这些值必须是实数,并将被存储在名为 , , 和 的暂元中。

你可以对输入做更多的检查。让我们快速检查所有三个参数是负数,其中比例不能大于1。

你可能希望对每个参数进行单独检查,这正是接下来我们要做的。

临时变量

我们现在准备利用Coale-McNeil模型和伽玛分布之间的关系做一些计算,下面是该程序的工作版本

我们可以将概率公式一行写出,但这会牺牲可读性。相反,我们先对年龄变量进行标准化,只是我们该如何称呼这个新变量呢?你或许会叫它 ,这当然可以。只是如果你命令的用户恰巧有一个变量叫 该怎么办呢?当我们计算伽玛函数后,我们又应该如何命令称呼结果呢?

上述问题的答案是 命令,该命令使stata生成唯一的临时变量名,本例中两个变量名会分别被存储在暂元 和 中。由于这些暂元是本地的,因此不存在名称冲突的问题。临时变量的另一个特点是当程序结束时它们会自动消失,所有工作stata会自动替你做好。

这行代码乍一看会有些奇怪,但请记住我们所有感兴趣的量现在都存储在暂元中,我们调用时必须先对暂元解析: varlist'获取用户指定的年龄变量的名称, stdev'得到标准差的值。在暂元代换之后,这行代码会读取类似的内容 ,这可能会更有意义。

if/in

你可能在考虑允许用户在命令中指定 和 条件,这需要添加到 中,它们将被存储在暂元中,然后你可以在计算中使用它们,在这种情况下传递给生成。

有关此主题类型的更详细的讨论

3.3.6 创建新变量

有时你编写的命令会创建一个新的变量。事实上,这就是我们的小命令所做的。如果我们可以使用 像这样的一种命令输入形式会非常方便:

答案是肯定的。因为 是用户可扩展的。要实现一个名为 的函数,你必须创建一个程序(do files )的_gpnupt,换句话说就是添加前缀 。关于 扩展的文档有点稀少,但一旦你知道了这个基本事实,你只需要查看 命令的源文件并复制即可。

所以这里是我们的Coale-McNeil命令版本的 。

这个版本和上一个之间的差异很小,主要体现在 接受表达式输入而不是单单变量输入.输入参数会被解析并存储在一个名为 的临时变量中。输出变量被指定为 ,在本例中为 。这就是为什么 现在与 和 产生 。 的存在是因为 可以让你指定输出变量的类型(默认情况下为float)并传递给我们的函数,函数再将它传递给 。

3.3.7 Coale-McNeil 拟合

我们准备揭示最初的图形是如何制作的。这些数据可以在我网站的人口统计部分的Stata文件中找到,数据包括已婚和单身女性的年龄。我们将计算观察到的已婚比例,根据Rodríguez和Trussell(1980)的估计值计算拟合值,并绘制结果图。这一切只需几行代码即可完成。

3.4 其他主题

由于缺乏时间和篇幅,我还没有讨论程序返回值, 了解更多信息。对于可以发布估计结果的估计命令的相关主题,请参阅 和 。关于估计的重要参考是Maximum Likelihood Estimation with Stata, Fourth Edition, by Gould, Pitblado and Poi (2010).

其他有趣的主题还有矩阵( ),对于输出更深入的讨论需要了解更多SMCL的信息( )。对于图形工作,你可能想要学习类的编程( )并学习sersets( )。为你的命令提供一个图形用户界面 。

[参考]: RodríguezG.和Trussell TJ(1980);测量数据对Coale模型婚姻调度参数的最大似然估计;世界生育率调查技术公告

关于我们

联系我们

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180622G170OW00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券