1、第十六章第十六章 STATA编程基础编程基础主要内容基本概念与工具程序文件的基本格式程序控制语句实验实验16-1:基本概念与工具:基本概念与工具实验基本原理实验基本原理如果我们需要反复执行一些命令,就可以将相关命令存放在一个do文件中,然后保存并运行相应的do文件即可。相比起一遍一遍地输入命令,这种方式既方便又不容易出错。而另一些时候,我们会需要编写程序,从而能用一个命令来实现某种结果。一个程序应以program开头,以end结束。也就是说,应呈现如下的形式:program 程序名相应的命令end我们可以采取交互的方式定义程序(即,在Stata命令窗口中输入程序的各行命令),但实际应用中,程序
2、经常是被保存到一个do文件或ado文件中去,从而方便以后的应用。在程序或do文件中,我们可能需要加入注释,从而方便以后或他人的阅读。通常,我们还会声明版本,从而使得程序能够在以后更高版本的Stata中继续使用。此外,局部宏、全局宏、临时变量、临时矩阵和临时文件等也会经常被使用。实验内容及数据来源实验内容及数据来源本实验中,我们会讲解do文件的创建和执行方法、定界符的修改、程序和do文件的联系、ado文件的创建和保存、注释的添加方法、版本的声明、局部宏和全局宏的定义及引用以及临时变量、临时矩阵和临时文件的定义和使用等内容。本实验主要讲解编写程序的一些基本操作,不需要使用数据文件。实验操作指导实验
3、操作指导1 Do文件文件do文件是一种文本文件,其扩展名为“.do”。要创建一个do文件,可以通过菜单栏中Window的下拉选项Do-file Editor来打开Do文件编辑器,也可以直接点击工具栏的图标 。而要执行一个do文件,可以键入以下命令:do filename这里,filename指相应的do文件的文件名。但需要注意的一点是,这个filename.do文件需要放在当前目录下,只有这样,才可以不写文件的路径;否则,需要在文件名前写出完整路径(而如果路径中有中文字符,一定要将全部路径和文件名置于英文双引号之间)。要查看当前目录,我们可输入命令:cd当然,我们也可以先将当前目录更改到我们偏
4、好的一个文件夹下,然后再将do文件存放其中。例如,如下命令可以将当前目录更改到d盘data文件夹下:cd“D:data”这里,需要注意的一点是,cd命令要求其后的文件夹原来就存在。对于do文件中的命令,值得注意的是,每一行命令都需要结束于一个硬回车(包括最后一行);除非通过“#delimit”命令设置其他符号为换行符。例如,输入命令:#delimit.则我们设置以英文的句号作为换行符,也就是说,Stata只有遇到英文句号才会认为这一句命令结束。设置其他的换行符对于将很长的命令分成几行很有帮助,因为这时我们可以在想分行的地方输入回车符,而Stata又不会认为这是一句命令的结束。而如果我们想重新以
5、硬回车(carriage return)为换行符,可输入以下命令:#delimit cr2 Stata程序和程序和Do文件文件Stata处理程序和处理do文件的方式是一样的,包括参数的传递、结果的表达等。但do文件和程序也存在一些小的差别。例如,要激发一个do文件,我们需要键入“do filename”,而要激发一个程序,我们只需要键入程序名称就可以。此外,键入“do filename”之后,Stata会显示do文件中的命令以及执行结果;而键入程序名之后,Stata只会显示其执行结果。下面,我们重点讲一下,通常情况下,将程序放到do文件中去需要注意的问题。例如,我们编写了一个简单的程序:pro
6、gram exampdisplay“this is an example”end并把它保存到名为“examp.do”的文件中,且把文件置于当前目录下。下面,我们要执行这个do文件,就在Stata命令窗口输入如下的命令:do examp下面,我们再在命令窗口输入命令:examp也就是说,我们要执行程序examp。这时,Stata就会显示:this is an example在随后的时间里,我们如果还想显示“this is an example”,直接输入命令“examp”就可以。这里,我们展示了共同使用do文件和程序的一种方式,即,先在do文件中写出程序的命令并保存,再键入“do filenam
7、e”,然后就可以在随后的时间使用该程序命令了。当然,我们也可采取一种更为简洁的方式,即在do文件的最后一行加上程序名,这样,当键入“do filename”的时候,Stata就会在加载完程序后就执行程序。但需要注意的是,程序一旦被定义,Stata就不允许对其重新定义。这样,如果我们随后又输入一遍“do filename”,Stata就会显示错误提示。要解决这个问题,我们可以在do文件的第一行输入这样的命令:program drop 程序名这样,在定义该程序之前,如果内存中已有这个程序,我们会先将其从内存中删掉。但这种解决方案也存在一个问题:在打开Stata的期间第一次运行这个do文件,Stat
8、a会显示错误提示。因为这时还没有定义程序,所有没有办法将其删除。我们继续修正该命令为:capture program drop 文件名将命令“capture”置于其他命令之前,就表示无论该命令是否作用,Stata也不显示错误提示,且能继续执行下面的命令。事实上,在包含程序的do文件中,我们经常可以看到程序的定义之前有这样的命令。综合了以上几点,我们前面的do文件可以修改为这样的形式:capture program drop exampprogram exampdisplay“this is an example”endexamp这里,第一行先检查是否有已定义的examp程序,如果有就将其从内存
9、中删除。第二到四行是定义程序examp,最后一行是执行程序examp。3 Ado文件文件如果想自动加载并运行程序内容,我们可以将程序保存到ado(automatically do)文件中(同样是利用do文件编辑器,保存时选择扩展名为ado),以后,直接输入程序名就可以使用该程序。但需要注意的是,ado文件的文件名和其中的程序名必须一致。值得注意的是,如果在Stata运行的过程中改变了某个ado文件的命令语句,则在重新运行这个ado文件前,要先将Stata内存中的ado文件清除。即,输入命令:discard否则,Stata还是会运行原来的那个ado文件。个人编写的ado文件通常被存放在两个地方,
10、一个是当前目录,另一个是个人ado目录。个人ado目录通常位于“C:adopersonal”,要查看其具体位置,可输入命令:personal而要查看或改变当前目录,可使用命令“cd”。4 版本版本随着Stata版本的变化,相应的命令也会有些变化。这样,较早版本的命令可能就没法在现在的版本中使用,而现在的某些命令可能也没法在以后的版本中使用。为了让现在的程序能够在其他的版本中继续使用,我们可以在程序中声明所使用的版本;这样,在更新版本的Stata中运行这个程序,Stata就会做相应的调整,按当前版本的方式来翻译程序的命令。要声明版本,可输入命令:version 10.1这里,我们使用的是10.1
11、版的Stata。对于其他版本的Stata,在version后面输入相应的版本编号即可。5 注释注释有时,我们想在命令中加入注释,从而方便以后或他人的阅读。要在do文件或ado文件中加入注释,可以采取如下几种方式:1.以“*”来开始一行。这样,该行就会被当做注释。2.将注释放在“/*”和“*/”之间。该种格式可以置于句中的任何位置。此外,在行末使用“/*”,并在下一行行首使用“*/”,可以将很长的命令分成两行。3.将注释置于双斜线“/”之后。如果双斜线之前有命令,则双斜线与命令之间至少要有一个空格。4.将注释置于三斜线“/”之后。如果三斜线之前有命令,斜线与命令之间也是至少要有一个空格。此外,对
12、于“/”,其下一行的命令会被认为是前面命令的继续。三斜线也可单独置于行尾,从而将很长的命令分成几行。例如,我们可以写这样一段命令:*this is an exampleuse/*get data*/C:Stata10datasample.dtasummarize age education/occupation tab region/obtain summary statistics如果去掉注释,上面的命令即为:use C:Stata10datasample.dtasummarize age education occupation tab region此外,对于交互方式的命令,注释只可采取第
13、一种方式,即在句首加上“*”。6 宏宏宏是Stata程序的变量,它用一个字符串(宏的名称)来代表另一个字符串(宏的内容)。宏分为局部宏(local macro)和全局宏(global macro)。局部宏只属于其所定义的程序,不能从其他程序中调用。而全局宏一旦被定义,就会留在内存,且可以被其他程序使用。局部宏的名称最多有31个字符,它的定义方式为:local 宏的名称 宏的内容或:local 宏的名称=表达式例如,我们输入命令:local nv“this is a newvar”就定义了一个叫做nv的局部宏,其内容为this is a newvar。如果我们要引用这个局部宏的内容,其格式为:n
14、v。注意,左边的引号为标准键盘左上角的重音符(数字1左边的键),右边的引号为通常的单引号(回车键左边的键)。定义完毕之后,如果我们输入:nv我们就相当于输入了:this is a newvar例如,我们可以输入如下的命令来显示这个宏的内容:display“nv”注意,这里,宏nv外面的双引号必不可少,因为如果不加引号,我们相当于输入了如下的命令:display this is a newvarStata会显示错误提示:this not found。只有加上外面的双引号,才表示我们要显示一个字符串。否则,Stata会将其当做变量来处理。当然,如果宏的内容确实为存在的变量名,而我们要显示这个变量,
15、就不必加上双引号。对于定义局部宏的命令,宏内容上的引号可以省略。也就是说,我们在定义时,可以采用如下的命令:local nv this is a newvar这与前面是等价的。当然,加上引号能使命令的可读性更强。下面,我们来看一下定义宏的两种形式的区别。例如,对于如下两种形式:local one 3+2 或等价地,local one“3+2”local two=3+2第一种形式中,局部宏one的内容为字符串3+2;而第二种形式中,Stata会先计算表达式的结果(3+2=5),然后将5保存到宏two中。在第二种形式中,表达式可以是数值表达式或字符串表达式。这里,3+2是数值表达式(如果想令其为字
16、符串型,在外面加双引号就可以)。此外,局部宏(以及全局宏)可以组合使用。例如,局部宏i为数值6,宏x6 为字符newvar,则宏xi就指代字符newvar。另外,在组合时,我们可以通过大括号来设定运算的优先级。如果要清除一个局部宏,可将其内容设置为空。这可以通过如下三种方式实现:local macnamelocal macname local macname=这里,macname指宏的名称。而如果我们在程序中直接使用了一个没有被定义的宏,则Stata会将其当做一个内容为空的宏来处理。对于全局宏而言,其名字最多可以有32个字符长。全局宏的定义方法与局部宏类似,只需要将local改为global即
17、可。此外,引用全局宏时,在其名字前加上美元符号$。此外,即便程序中的全局宏和局部宏有相同的名称也没有关系,因为他们的引用方法不同,Stata可以将其区分。值得注意的一点是,如果我们要显示的内容第一个字符为美元符号,为了和全局宏进行区分,我们可以在该字符前加上反斜线。例如:display“$that”就表示要显示字符串$that,而不是全局宏that的内容。7 临时变量、临时矩阵和临时文件临时变量、临时矩阵和临时文件有些时候,程序在运行的过程中需要产生一些临时的变量、矩阵等,而一旦程序运行结束,这些变量、矩阵等就不再需要。这种情况下,我们可以生成临时变量等。生成临时变量的命令格式为:tempva
18、r var1 var2 这里,tempvar 是生成临时变量的基本命令,var1、var2代表临时变量的名称。确切地说,命令tempvar生成的是临时变量的名称,我们后面可以使用这些名称来生成临时变量;这些临时变量在程序结束时会被自动删掉。此外,tempvar生成的变量名被保存在局部宏内,在后面引用时需要加引号。临时变量的一个优点在于,即便内存中已存在相同名称的变量,我们也不必先将其删掉;这不会影响我们定义临时变量。例如,我们要定义并生成两个临时变量x和y(可能内存中已有变量是这个名称,但没有关系),可输入命令:tempvar x ygen x=gen y=这里,第一步是定义临时变量,第二步和
19、第三步是生成相应的临时变量。我们这里没有给出具体的表达式,因为这完全视具体情况而定。需要注意的是,在命令tempvar之后,我们引用临时变量时,都需要加上局部宏的引号。类似地,定义临时矩阵或临时标量的命令为:tempname ms1 ms2 这里,tempname 是定义临时矩阵或临时标量的基本命令,ms1、ms2等代表临时矩阵或临时变量的名称。同样地,定义临时文件的命令为:tempfile file1 file2 这里,tempfile是定义临时文件的基本命令,file1、file2代表临时文件的名称。例如,我们在程序中可能出现这样的命令:preserve /保存初始数据tempfile m
20、ales females /定义临时文件males和femaleskeep if sex=1 /*保留变量sex取值为1的观测值*/save males /*将这些观测值保存到临时文件male中*/restore,preserve /恢复原始数据keep if sex=0 /保留变量sex取值为0的观测值save females /将这些观测值保存到临时文件female中与临时变量相同,在程序结束时,Stata会自动删除临时矩阵、临时标量或临时文件。实验实验16-2:程序文件的基本格式:程序文件的基本格式实验基本原理实验基本原理在编写程序时,我们需要遵从一定的格式。基本的一种格式为:progr
21、am define 程序名,version#syntax end其中,第一行是定义程序,第二行是表明所用的Stata版本,第三行表明语法格式,后面是其他的一些命令,最后一行表明该程序结束。当然,version和syntax都不是必选项。但syntax等命令的应用在很多时候能使得程序的编写变得比较方便。而当我们不使用语法的时候,有时也会需要对输入的参数进行解析,这可以通过tokenize或gettoken命令来实现。此外,命令中使用的观测值有时需要被标记。例如,程序设定了条件语句、范围语句,需要确认其所使用的观测值等。由于很多程序错误产生于在程序的不同部分使用不同的样本,因此,我们强烈建议在程序
22、的开始处对样本进行标记。最后,很多程序需要保存一些结果,并且可以在运行程序时返回相应的结果。对于这些,我们也会进行讲解。实验内容及数据来源实验内容及数据来源本实验中,我们主要讲解程序的定义、程序的删除、程序内容的显示、变元的定义和引用、语法的定义、标记变量的生成和调整、输入参数的解析、结果的保存和返回等内容。本实验的部分命令会用到本书附带光盘data文件夹下的“usaauto.dta”工作文件。该文件给出了美国汽车产业的横截面数据,主要变量包括:price=汽车的价格,mpg=每加仑油所行驶的英里数,weight=汽车的重量,length=汽车长度,turn=转弯圆周,displacement
23、内燃机气缸的工作容积,foreign=是否进口车(0代表是国产车)。实验操作指导实验操作指导1 程序的定义程序的定义定义程序的基本命令为:program define program_name,nclass|rclass|eclass|sclass byable(recall,noheader|onecall)properties(namelist)sortpreserve plugin这里,program define是定义程序的基本命令,其中,define可以省略(所有中括号内的内容都是可选项)。program_name是程序名。选项nclass是默认选项,用于表明程序的类型,该选项表示
24、程序不保存任何结果;rclass、eclass和 sclass分别表示程序结果保存在r()、e()和s()中。选项byable()表示程序在运用时允许“by:varlist”前缀,而默认情况下这是不允许的。byable()主要有两种运用形式:by(recall)和by(onecall)。其中,by(recall)最为常用。需要注意的是,如果程序中有生成新变量(指永久变量而非临时变量),或者没使用marksample或mark(该命令的讲解见后面)来限制相关的子样本,则仅仅使用选项by(recall)是不恰当的。了解by(recall)的作用机理会有助于我们理解这一点:如果程序在运用时使用了“b
25、y:varlist”前缀,程序会被执行k次(k是分组变量varlist的类别数);而每次运行时,marksample会标记出不属于当前组的观测值。因此,如果程序中有生成永久变量,则第一次运行时就会生成,而第二次再运行时,因为相应的变量已经存在,故会出错。而如果没有用marksample来标记相关样本,会导致过多的观测值被使用,也会出错。by(onecall)的主要作用是用于像“generate”和“egen”这一类的程序,它运行前缀by,但作用于所有数据,且生成一个变量来保存不同组的结果。如其名字所暗示的,这类程序只执行一次。properties()表示程序的特征,比如运行stepwise或s
26、vy前缀。sortpreserve表示程序会改变数据的排序,但程序结束时,就恢复初始顺序。该选项会使Stata在程序开始时通过临时变量_sortindex记录当前数据排序情况,并在程序结束时通过该变量恢复数据的排序。这样,即便程序中使用命令“sort”对数据进行了重新排序,程序结束时数据也会恢复初始的顺序。需要注意的一点是,必须保证临时变量_sortindex在程序结束时还存在,这样,程序才能恢复原来的排序。因而,如果程序结尾处有这样的命令:keep id varlist 我们应修改为:keep id varlist _sortindex 此外,如果程序中还有preserve命令,则程序运行结
27、束后,会首先恢复到preserve所保存的状态,然后再恢复到sortpreserve所保存的状态。最后,选项plugin表示动态载入插件程序。程序的显示和删除程序的显示和删除显示内存中程序的命令为:program dir删除内存中程序的命令为:program drop program_name program_name|_all|_allado这里,program drop是删除内存中的程序的基本语句,program_name指要从内存中删除的程序名,_all表示删除所有内存中的程序,_allado表示删除内存中所有从ado文件加载的程序。显示程序内容的命令为:program list pro
28、gram_name program_name|_all这里,program list是显示程序内容的基本语句,program_name指要显示内容的程序名,_all表示显示所有内存中的程序的内容。3 变元变元有些时候,我们需要在程序中使用某些变量。这些变量从外界输入,并且在每次运行程序时可以进行修改。定义并命名变元最简单的方式是使用args命令,定义之后,变元被保存到局部宏之中。下面,我们通过一个简单的程序来说明该命令的应用。程序为:program argexampargs arg1 arg2 arg3 arg4display“The 1st argument is:arg1”display“
29、The 2nd argument is:arg2”display“The 3rd argument is:arg3”display“The 4th argument is:arg4”end这里,我们定义了四个变元,arg1、arg2、arg3和arg4;定义之后,它们被传递到局部宏arg1、arg2、arg3和arg4之中,并在后面的命令中被引用。在执行程序时,在程序名之后依次输入各变元的内容就可以。下面,我们运行程序。输入命令:argexamp this is an argument如果我们输入以下命令:argexamp“this is an argument”则字符串“this is an
30、 argument”就被看做是一个变元的内容。此外,即便我们不通过命令args来定义变元名,也可以直接在运行程序时,于程序名后面写上各变元的内容。这时,Stata将依次把各变元的内容传递到局部宏1、2等之中,而且,宏0会按照用户输入的形式来保存所输入的内容(包括引号等),宏*则相当于将宏1、2等依次排列,且两两之间有一个空格。但这样的一个缺点是,变元的含义往往不够明确,程序的可读性比较低。事实上,在用args命令之后,Stata不过把局部宏1、2的内容又传递到args新定义的变元中;而这时,我们仍可在程序中通过局部宏1、2等来引用相应的变量。最后,如果在执行程序时,命令后面的变元个数多于arg
31、s定义的变元的个数,我们可以通过数字形式的宏来引用多出来变元。4 语法语法通过在程序中设置命令的语法(syntax)格式,我们可以不必设定变元,直接按照语法来使用该程序即可。标准的语法格式为:by varlist:command varlist=exp using filename if in weight,options这里,by varlist表示按照变量varlist的类别分别运行命令,command表示命令的名称(即程序名),varlist代表命令中的变量,=exp表示运算表达式,using filename使用数据文件filename,if代表条件语句,in代表范围语句,weight
32、代表权重语句,options代表其他选项。在写语法时,我们选取需要的部分进行组合。也可以将某些项用中括号括起来,表明该内容为可选项;或者不使用中括号,表明该项为必选项。但需要注意的一点是,在程序中写语法之前,必须写上命令snytax,表明该命令是语法格式。而by varlist和command则不必出现在syntax之后,它们是在运行程序时使用的。另外,执行程序时,语法中各项内容都会被保存在相应的宏中。例如,条件语句会被保存在宏if中,范围语句会被保存在宏in中,等等。下面,我们对语法格式中的几项做进一步的说明。对于varlist,我们可以进行多种形式的设置。例如:syntax varlist
33、 表示执行程序时,变量名varlist必须设置syntax varlist 表示变量名varlist为可选项syntax varlist(default=none)设置默认的变量个数为0syntax varlist(min=3)表示变量varlist必须设置,且最少要有3个syntax varlist(min=2 max=5 numeric)表示变量varlist最少要有2个,最多有5个,且必须为数值型。括号中还可设定:string=只允许字符变量,ts=允许时间序列算子等。syntax varname 只允许设置一个变量,且该变量为可选项对于varlist的位置,我们也可以设置为newvar
34、list或newvarname,表明生成新变量。此外,如果设置为namelist或name,表明输入的对象可以不是变量名。要想进一步放宽要求,还可采用anything,这时,在命令后输入表达式或一串数字等都没有关系。最后,如果不设定varlist,则该程序命令会不允许输入变量。此外,对于语法格式的选项weight,它必须用中括号括起来,因为weight永远是可选项。此外,我们可以设定多种权重形式,包括fweight、aweight、pweight和iweight。例如,我们可以设定:syntax fweight pweight 表明fweight和pweight是可选的。下面,我们定义一个包含
35、语法的程序,来对前面的讲解做进一步的说明。program mysynsyntax varlist(min=1)if in,title(string)*设定变量名为必选项,且至少要有一个变量,if、in、title()为可选项display“varlist contains|varlist|”*显示字符串“varlist contains|变量名|”,变量名被保存在宏varlist中display if contains|if|*显示字符串“if contains|条件语句|”,条件语句保存在宏if中display in contains|in|display“title contains|ti
36、tle|”end下面,在打开数据文件“usaauto.dta”的情况下,我们执行这个程序。输入命令:mysyn mpg weight length if foreign,title(“my syntax”)这里,mysyn为命令名(即程序名),mpg、weight和length为变量,if foreign为条件语句,title()指标题。mysyn后的各项即按照程序中指定的语法结构所写。5 标记使用的观测值标记使用的观测值有时,我们需要对命令中使用的观测值进行标记。例如,程序要对不包含缺失值的观测值计算统计量(例如线性回归);或程序设定了条件语句、范围语句等,需要确认其所使用的观测值。由于很多
37、程序错误产生于在程序的不同部分使用不同的样本,因此,我们强烈建议在程序的开始处对样本进行标记。Stata有专门的命令来生成标记变量,对使用的观测值进行标记。这种变量为临时变量,取值为0或1;如果观测值在随后的编码中被使用,则临时变量取值为1,若未被使用,则取值为0。下面,我们对相关命令进行介绍。(1)生成标记变量在语法(syntax)命令之后生成标记变量的命令格式为:marksample lmacname,novarlist strok zeroweight noby这里,marksample是生成标记变量的基本格式;lmacname为局部宏的名字,该宏用于保存临时变量的信息,如果某观测值的标
38、记变量取值为1,就表示该观测值在命令执行中被使用;选项novarlist表示不排除包含缺失值的观测值;strok表示varlist中的变量可以是字符串(string is OK);zeroweight表示不排除权重为0的观测值;选项noby只有在程序设定了byable(recall)选项时才可以使用,它表示,在标记样本时,分组限制被忽略也就是说,marksample会像未设定by前缀那样来生成标记变量。marksample的通常使用形式是这样的:program syntax marksample touse rest of code if touseend这里,我们使用局部宏touse作为标记
39、变量;这是一种惯例,而非要求。但为了保持程序间的一致性,我们推荐使用该名字。此外,倒数第二行命令含义为,如果宏touse的值为1(即样本被使用),则对其进行相关的操作;“rest of code”指相应的操作的命令。此外,我们在应用该命令时可以生成多个标记变量,分别为其设定不同的选项,从而标记不同的观测值。(2)设定临时变量为标记变量如果我们已生成临时变量,且要将观测值是否被使用的信息保存到该临时变量中,可使用如下的命令格式:mark newmarkvar if in weight,zeroweight noby其中,mark是生成标记变量的基本命令,newmarkvar是已生成的临时变量的名
40、称,if代表条件语句,in代表范围语句,weight代表权重语句。选项zeroweight和noby的含义与marksample处相同。需要说明的一点是,相比起marksample,该命令较少被使用。因为如果之前的语法中设定了条件语句等,mark命令也需要设定相应的语句,且往往还要通过markout来设定varlist(见下一部分);而marksample可自动完成这些,不容易有遗漏项。(3)调整标记变量在命令mark或marksample之后,如果于varlist的变量之外又有了新的变量,或者,如果varlist中变量包含缺失值的观测值都要被排除掉(即令标记变量为0),我们可使用如下命令:m
41、arkout markvar varlist,strok sysmissokmarkout是调整标记变量的基本命令,markvar是前面设定的标记变量名称,varlist代表相关的变量名,选项strok表示允许字符串变量。最后,选项sysmissok表示系统缺失值(.)是允许的,但扩展缺失值(形如.a、.b等)会被排除;默认是包含任何类型缺失值的样本都会被排除。下面,我们给出一个同时利用mark命令和markout命令的例子:program myprogversion 10syntax varlist if intempvar tousemark touse if inmarkout tous
42、e varlistend这里,mark命令依据条件语句和范围语句来生成临时变量touse;而如果没有条件语句或范围语句,则每个观测值的touse值都是1。之后,markout命令会对标记变量进行更新,对于touse值为1的变量,Stata会检查varlist的值是否缺失,如果有缺失值,则将相应观测值的touse值修正为0。在后面的数据管理或统计量计算的命令中,加上条件语句“if touse”会将命令的执行限制在合适的样本之中。(4)标记变量的适用规则不管使用的是marksample还是mark,以及它们后面有没有markout,如下的一些规则都适用:1.观测值的权重为0时,标记变量被设定为0(
43、设定选项zeroweight除外)。2.如果权重weight无效(像某些观测值的权重小于0,或者频率权重不是整数等),程序会被停止,且Stata会返回错误信息。3.如果观测值不满足条件语句(if),则标记变量被设为0。4.如果观测值不满足范围语句(in),标记变量被设为0。5.如果varlist中的任何一个数值变量包含数值缺失值,标记变量的值为0。6.如果varlist中有变量为字符串型,则所有观测值的标记变量为0(设定选项strok除外)。7.其他情况,标记变量的值为1。(5)寻找选定观测值的范围寻找选定观测值的范围的基本命令为:markin if in,name(lclname)noby这
44、里,markin是寻找选定观测值范围的基本命令,if为条件语句,in为范围语句,选项name()用于指定新生成的局部宏的名字,如果不设定该选项,名字in会被使用。命令markin用在marksample、mark或markout之后,且在使用时要很小心,因为该命令永远都不是必须的。但如果我们知道,前面命令的条件语句将选取很多观察值的一个很小的样本(例如,从58000个中选10个),且这些观测值的序号有比较接近,则使用markin命令能使程序执行得更快。使用该命令的一种方法是,先写出一个不带该命令的程序,然后在定义标记变量之后,加入这样的语句:markin if touse 并在后面所有使用if
45、 touse的命令后面(这里,我们假定标记变量被命名为touse)同时加上in(假定没有指定新的局部宏的名字)。6 输入参数的解析输入参数的解析如果想对宏进行分解,获取所需的部分,我们可以通过tokenize或gettoken命令来实现。相比起语法(syntax)命令,这种解析方式也被称为低水平语法分析。下面,我们对这两个命令分别进行介绍。(1)tokenize通过tokenize将宏分解成几部分的基本命令为:tokenize”string“,parse(“pchars”)其中,tokenize是解析宏的基本命令,string代表要分解的宏的名称,选项parse(“pchars”)表示用pch
46、ars作为各个部分的分隔符,默认为空格。tokenize命令会将指定的宏分解为几个记号(token),并依次将各部分保存在局部宏1、2中。下面,我们通过一个例子来说明该命令的应用。输入命令:local str A strange+string (定义局部宏str)tokenize str,parse(+)(将str进行分解,设定分隔符为空格和加号)di 1=|1|,2=|2|,3=|3|,4=|4|,5=|5|,6=|6|(显示分解的各个部分)我们得到如下的结果:1=|A|,2=|strange|,3=|+|,4=|+|,5=|string|,6=|通过这个例子,我们可以看出,多个符号可以同时
47、被用作分隔符(这里是空格和加号);空格被用作分隔符时不会被保存在数值宏中,但其他字符被用作分隔符时会被保存在数值宏中(这里是宏3和4)。tokenize命令对于分解语法命令(syntax)输入的参数很方便。例如,可能有如下的形式:program myprogversion 10syntax varlist if inmarksample tousetokenize varlist /对输入的变量进行分解local first 1 /将分解后的第一个变量的局部宏命名为firstmacro shift /宏转移,即原来第i个宏现在变为第i-1个。local rest*/将剩余的宏命名为resten
48、d(2)gettokengettoken提供了解析输入参数的另一途径,其基本命令格式为:gettoken emname1 emname2:emname3,parse(pchars)quotes qed(lmacname)match(lmacname)bind其中,gettoken是解析参数的基本命令。选项parse(“pchars”)表示用pchars作为各个部分(记号)的分隔符,pchars默认为空格。该命令会获取宏emname3分隔符之前的记号(token),并将其存储在宏emname1中。如果同时设定了宏emname2,则Stata会将剩下的记号存储在宏emname2中。宏emname1
49、和emname3,或者宏emname2和emname3,可以有相同的名字。宏emname可以有如下三种形式:macroname 局部宏(local)macroname 局部宏(global)macroname 全局宏对于其他的选项,quotes表示宏emname1内容的外引号被保留;该选项不影响宏emname2,因为它总是保留其内容的外引号。qed(lmacname)表示生成一个局部宏lmacname;如果返回的记号(token)在初始字符串中为引号所包围,则其值为1,否则为0。match(lmacname)表示,在决定记号时,括号会被匹配;此外,在记号被存储在宏emname1之前,括号会被移
50、除;而如果有括号,局部宏lmacname 的值为“(”,否则,就是空串。bind表示小括号()或中括号中的内容会被当做一个记号,即便分隔符不是括号。通常情况下,gettoken命令的运用方式是这样的:gettoken emname1:0,options或 gettoken emname1 0:0,options这里,宏0包含了用户所键入的内容。第一种方式会将取出的第一个token保存到宏emname1中;而第二种方式还会将剩下的部分再保存到宏0中。下面,我们通过一个交互式例子来进一步加深对gettoken命令的理解。首先,我们定义一个局部宏str。输入命令:local str panel+da






