温馨提示: 定期 清理浏览器缓存,可以获得最佳浏览体验。
New!
lianxh
命令发布了:
随时搜索连享会推文、Stata 资源,安装命令如下:
. ssc install lianxh
使用详情参见帮助文件 (有惊喜):
. help lianxh
⛳ Stata 系列推文:
作者:邱枞 (中山大学)
邮箱:qiuz@mail2.sysu.edu.cn
目录
阶乘是基斯顿·卡曼 (Christian Kramp, 1760~1826) 于 1808 年发明的一种数学运算符号。其相关表述如下:
那么如何在 Stata 中实现阶乘算法那?首先,让我们从一段代码开始,具体如下所示:
local k = 5
local a = 1
forvalues i = 1/`k'{
local a = `a'*`i'
}
dis "`k'! = " `a'
在这段代码中,我们定义了两个暂元 k 和 a。其中,k 表示需要计算阶乘的自然数,而 a 则是被用于计算累乘。计算思路也很简单,就是遍历所有小于等于 k 的正整数,然后将它们累乘。
但是,当计算大量阶乘时,每次都要运行同样的代码,这就会导致重复工作,并且犯错的概率也会大增。为此,我们可以通过程序编写来简化过程。
capture program drop myfact1
program define myfact1
version 15
args k // 定义暂元 k,用于接收用户输入的数字
local a = 1
forvalues i = 1/`k'{
local a = `a'*`i'
}
dis "`k'! = " `a'
end
可以发现,我们只是在开头和结尾增加了几行代码。接下来,将对这些增加代码进行逐一解读。
program define myfact1
。值得注意的是,按照 StataCorp 惯例,用户写的命令名必须严格小写;version 15
。通过标记版本,用户命令可以始终保持在该版本上运行;args k
。arg
命令比 syntax
和 gettoken
命令更加简单粗暴,即它将命令名后一整行内容都读取到暂元 k
中;end
。不过,一旦程序被定义,就会保存在 Stata 内存里,直到 Stata 关闭。若后面定义同名程序,Stata 就会报错。因此,需要在程序的最开始加上 capture program drop myfact1
命令,以删除内存中已经保存的同名程序。在这里,capture
实现了不显示报错,也不终止程序的功能。
至此,我们便可以直接调用程序来实现阶乘计算。
. myfact1 7
7! = 5040
如果需要在其它 do 文档或命令窗口中调用阶乘程序,我们又该如何做那?其实,Stata 的命令大都是以 ado 文档形式保存,例如 reg
命令。当我们需要使用命令时,直接输入即可调用。为此,我们将计算阶乘的程序保存为 ado 文档。具体操作步骤如下:
adopath
可以查看路径清单,或者你可以使用 adopath + pathname
命令添加新的路径至清单中。. adopath
[1] (BASE) "D:\Stata15\ado\base/"
[2] (SITE) "D:\Stata15\ado\site/"
[3] "."
[4] (PERSONAL) "D:\Stata15/ado\personal/"
[5] (PLUS) "D:\Stata15/ado\plus/"
[6] (OLDPLACE) "c:\ado/"
[7] "D:\Stata15/\ado\personal\_myado"
最好是保存在 PERSONAL 文件夹下 (如果没有,就在 Stata 的 ado 文件夹下新建名为 "personal" 的文件夹。
执行完上述步骤后,我们就可以在任何需要计算阶乘的时候,直接调用阶乘命令。
尽管上述程序已经可以实现阶乘的计算,但仍有很多细节需要注意。接下来,我们将为大家逐一介绍。
第一步自然就是输入。除了sum
、des2
这类命令外,执行命令往往需要外部输入,否则这个命令毫无意义;而如何读取输入也是有讲究。今天我们主要介绍两个输入命令:args
、syntax
。
2.1.1 利用 args
命令输入
args
命令基本语法如下:
args macroname1 [macroname2 [macroname3 ...]]
args
命令默认将程序名后的整行输入存储在暂元 macroname1 中。但是遇到多项输入时,args
也会根据空格对多个输入进行简单拆分,并存储在 macroname1、macroname2、macroname3,...,等暂元中。例如:
capture program drop myprog
program myprog
version 15
args k1 k2 k3
dis "`k1',`k2',`k3'"
end
. myprog 1 2 3
1,2,3
2.1.2 利用 syntax
命令输入
syntax
是 Stata 中功能比较齐全的输入命令,其基本语法如下:
syntax [varlist | namelist | anything]
[if]
[in]
[using filename]
[= exp]
[weight]
[, options]
当输入类型是变量 varlist
时,语法如下:
syntax [varlist/varname/newvarlist/newvarname](varlist_specifiers)
(varlist_specifiers)
主要包括以下可选内容:
default=
:当可变参数列表是可选的且用户没有指定时,程序如何填充可变参数列表。默认情况下是用所有变量填充;min=# max=#
:指定可以输入的最小和最大变量数;numeric
、string
、str#
和 strL
:varlist
由完全数值型、完全字符串型 (即 str#
或 strL
)、完全 str#
或完全 strL
型变量组成;fv
:允许变量列表包含因子变量;ts
:允许可变列表包含时间序列运算符;generate
:对于 newvarlist
或 newvarname
, generate
指定创建新变量并填充缺少的值。syntax varlist ...
syntax [varlist] ...
syntax varlist(min=2) ...
syntax varlist(max=4) ...
syntax varlist(min=2 max=4 numeric) ...
syntax varlist(default=none) ...
syntax newvarlist(max=1) ...
syntax varname ...
syntax [varname] ...
当输入类型是名称 namelist
时,语法如下:
syntax [namelist](namelist_specifiers)
(namelist_specifiers)
包括以下可选内容:
name=
:为存储输入内容的暂元命名,默认 name= namelist
;id=
:定义暂元在报错信息中的名称,当输入 id = height
时,系统会在用户无输入时显示错误信息 height required
,以提示用户输入 height
相关的内容;min=# max=#
:指定可以输入的最小和最大 namelist
数。syntax namelist ...
syntax [namelist] ...
syntax name(id="equation name") ...
syntax [namelist(id="equation name")] ...
syntax namelist(name=eqlist id="equation list")...
syntax [name(name=eqname id="equation name")] ...
syntax namelist(min=2 max=2) ...
当输入类型没有限制 anything
时,语法如下:
syntax [anything](anything_specifiers)
(anything_specifiers)
包括以下可选内容:
name=
:用法同上;id=
:用法同上;equalok
:规定 =exp
不作为标准语法的一部分,而是作为 anything
的一部分;everything
:规定 if
、in
和 use
不作为标准语法的一部分,而是作为 anything
的一部分。syntax anything ...
syntax [anything] ...
syntax anything(id="equation name") ...
syntax [anything(id="equation name")] ...
syntax anything(name=eqlist id="equation list") ...
syntax [anything(name=eqlist id="equation list")] ...
syntax anything(equalok) ...
syntax anything(everything) ...
syntax [anything(name=0 id=clist equalok)] ...
鉴于 syntax
命令的强大功能,我们使用 syntax
替换 args
命令来处理输入内容。
syntax anything(name=k id="an integer")
在上述命令语句中,无论用户输入什么,我们都储存在名为 k 的暂元中。又由于指定 id="an integer"
,当检测不到输入时,系统自动报错 an integer required
。如果想要更加灵活的无输入报错提醒,可以使用以下代码:
capture syntax anything(name=k)
if _rc{ // 用户忘了输入数字
dis as error _rc // 测试错误代码
dis as error "You must enter an positive integer"
exit
}
由于使用了 capture
命令,当我们没有指定 id
时,系统并不会终止或显示报错信息,而是返回一个错误代码 _rc
(此种错误类型对应的代码为 100)。利用这个特性,我们可以加入 if
条件判断语句,检测到无输入时,执行特定的报错提醒。Stata 的报错提醒是 dis as error
命令语句。
2.2.1 使用 assert
进行验证
如果用户输入的不是正整数,则无法计算阶乘,因此我们需要判断用户的输入是否为正整数。
assert
语句是一种简单的验证命令。当判断条件为真时,命令没有返回值,当判断条件为假时,则返回错误代码 9。利用这个特性,我们依然可以结合 capture
命令进行错误提醒。
判断条件很简单,当输入内容整数部分等于它本身且大于 0 时,assert
函数不会报错,否则,assert
函数报错。
capture assert int(`k') == `k'&`k'>0
if _rc{
dis as error "输入的不是一个正整数"
exit
}
2.2.2 使用 confirm
进行验证
confirm
是一个十分强大的验证命令,包括但不限于可以验证变量是否存在、数据类型、是否为正整数等。在这里,我们利用 confirm integer number
即可验证是否为正整数。
capture confirm integer number `k' // 用户输入了非整数或文字
if _rc{
dis as error "You must enter a positive integer"
exit
}
2.2.3 验证是否存在多个输入
验证用户是否输入超过 1 个数字/标量/变量,有以下几种方法:
wordcount
命令:根据空格将输入内容拆分,并返回拆分得到的词数。
if wordcount(`"`k'"')>1{ // 用户输入了多个数字
dis as error "You can only enter one integer"
exit
}
index
命令:用于寻找特定字符串,返回找到的第一个特定字符串的位置。
if index("`k'"," ")!=0{
di in error "You can only enter one integer"
exit
}
tokenize
命令:由于该命令相对重要,因此我们需要详细介绍一下。tokenize
命令的基本语法如下:
tokenize [[`]"][string]["[']] [, parse("pchars") ]
tokenize
命令后跟需要切割的字符串,parse
选项用以设定切割字符,默认为空格,并将得到的子字符串自动保存在名为 1、2、3,……,的暂元中。
字符串左右两端可以加上简单双引号 "string",或者复合双引号 `"string"',不过这并不影响命令的使用。之所以有时区分简单双引号和复合双引号,是为了避免字符串中出现引号。
. tokenize some more words
. display "1=|`1'|, 2=|`2'|, 3=|`3'|, 4=|`4'|"
1=|some|, 2=|more|, 3=|words|, 4=||
. tokenize "some more words"
. display "1=|`1'|, 2=|`2'|, 3=|`3'|, 4=|`4'|"
1=|some|, 2=|more|, 3=|words|, 4=||
. tokenize `"some more words"'
. display "1=|`1'|, 2=|`2'|, 3=|`3'|, 4=|`4'|"
1=|some|, 2=|more|, 3=|words|, 4=||
当需要将空格以外的字符作为切割字符时,可以通过 parse()
选项设定。
. local str "A strange++string"
. tokenize `str', parse("+")
. display "1=|`1'|, 2=|`2'|, 3=|`3'|, 4=|`4'|, 5=|`5'|, 6=|`6'|"
1=|A strange|, 2=|+|, 3=|+|, 4=|string|, 5=||, 6=||
. tokenize `str', parse(" +")
. display "1=|`1'|, 2=|`2'|, 3=|`3'|, 4=|`4'|, 5=|`5'|, 6=|`6'|"
1=|A|, 2=|strange|, 3=|+|, 4=|+|, 5=|string|, 6=||
. tokenize `str', parse( +)
. display "1=|`1'|, 2=|`2'|, 3=|`3'|, 4=|`4'|, 5=|`5'|, 6=|`6'|"
1=|A strange|, 2=|+|, 3=|+|, 4=|string|, 5=||, 6=||
在第一种情况下,当空格没有被作为切割识别字符时,第一个子字符串是 "A strange";而在第二种情况下,当空格和加号同时被作为切割识别字符时,前两个子字符串是 "A" 和 "strange"。括号里的引号也是可选的,即不加引号也可以得到同样的识别效果。值得注意的是,除了空格外,所有的切割识别字符都会在切割结果中作为子字符串保存。
在本程序中,利用 tokenize
命令的代码如下:
tokenize `k'
if `"`2'"'!=""{
di in error "You can only enter one integer"
exit
}
完成基本操作后,若我们还想为程序加上一些额外的功能,又该如何?此时,我们就要利用到 syntax
命令的 [,option]
。
syntax
的可选项用法格式如下:
syntax ... [, MYopt(string) Thisopt(name)]
syntax ... , MYopt(string) Thisopt(name)
基本用法为,在 syntax
语句中添加存储选项的暂元,来作为程序的附加选项。
MYopt
中大写部分为选项的简称。也就是说,使用 MYopt
时,可以用 my
,myo
,myop
和 myopt
中任意一形式,但是都必须包含 my
;integer
、实数 real
等。如果输入的参数类型与要求的不同,系统会自动报错。format
命令的一般形式如下:
format varlist %fmt
format %fmt varlist
常见的数值变量的输出格式为 %#.#f
、%#.#e
和 %#.#g
,分别表示固定格式、科学计数和通用设定方法等。例如,输出格式为 %w.df
,w
表示整体输出宽度,包括符号和小数点,而 d
表示小数点右边的位数。关于 format
的具体使用方式,可以参考 help format
。
在本程序中,我们将定义一个简易版的 format
选项,即限定输入格式为字符串,并储存在暂元 format
中。接着,利用 capture
和 format
命令检测输入的自定义格式是否标准格式,正确则不提示,否则提示错误信息 (错误代码为 7)。
capture program drop myfact8
program define myfact8
version 15
syntax anything(name=k) [, Format(string)]
if "`format'" != ""{
capture confirm format `format'
if _rc{
dis as error "invalid format(`format'). It should be format(%10.0g)."
dis as text "for help, see {help format}"
exit 198
}
}
local a = 1
forvalues i = 1/`k'{
local a = `a'*`i'
}
dis "`k'! = " `format' `a'
end
. myfact8 6, f(%8.3f)
6! = 720.000
. myfact8 6, format(%8.3f)
6! = 720.000
为了保存输出的结果,便于后续研究调用,我们可以生成新变量,将阶乘的结果保存进去。我们通过定义generate
选项实现这个功能。下述代码定义了名为generate
的命令,缩写为gen
,它的功能将用户输入的内容作为变量名,计算的阶乘结果作为值进行保存。类似的gen
选项我们在很多命令的选项中也能见到。
capture program drop myfact9
program define myfact9
version 15
syntax anything(name=k) [, GENerate(string) Format(string)]
local a = 1
forvalues i = 1/`k'{
local a = `a'*`i'
}
if "`generate'" != ""{
gen `generate' = `a'
}
else{
dis "`k'! = " `format' `a'
}
end
. clear
. set obs 1
number of observations (_N) was 0, now 1
. myfact9 6, gen(x1)
. list x1, noobs
+-----+
| x1 |
|-----|
| 720 |
+-----+
. myfact9 6, f(%8.3f)
6! = 720.000
在前文中,我们已经介绍过利用 tokenize
命令将字符串进行切割,并将返回子字符串保存在名为1、2、3,……,的暂元中。对于多个数值输入,我们只需要对其进行切割,然后使用循环程序遍历所有子字符串便可输出所有数的阶乘。
我们可以利用 macro shift
函数计算 tokenize
生成的暂元数量。macro shift
函数的作用是舍弃第一个暂元储存的内容,将后面暂元的内容依次储存在上一个暂元中。因此,只要计算并输出第一个暂元的阶乘后,我们便可以使用该命令,将下一个暂元内容前移。这使得在不知道暂元数量的前提下,也可以完成循环。
*利用循环
capture program drop myfact10
program define myfact10, rclass //利用可选项 rclass,生成并调用返回值
version 15
syntax anything(name=numlist) [, Format(string)]
tokenize "`numlist'"
local j = 1
while "``j''" !=""{ // `j' 表示房间号;``j'' 表示房间中的内容
local a = 1
local k = ``j'' // 注意暂元的引用方式
forvalues i = 1/`k'{
local a = `a'*`i'
}
dis "`k'! = " `format' `a'
return scalar k`k' = `a'
local j = `j' + 1
}
end
. myfact10 6 4
6! = 720
4! = 24
. return list
scalars:
r(k4) = 24
r(k6) = 720
*利用 macro shift
capture program drop myfact11
program define myfact11, rclass //利用可选项 rclass,生成并调用返回值
version 15
syntax anything(name=numlist) [, Format(string)]
tokenize "`numlist'"
while "`1'" !=""{ // `j' 表示房间号;``j'' 表示房间中的内容
local a = 1
local k = `1' // 注意暂元的引用方式
forvalues i = 1/`k'{
local a = `a'*`i'
}
dis "`k'! = " `format' `a'
return scalar k`k' = `a'
macro shift
}
end
. myfact11 6 4
6! = 720
4! = 24
. return list
scalars:
r(k4) = 24
r(k6) = 720
与 tokenize
命令不同,gettoken
只进行一次切割,其语法如下:
gettoken emname1 [emname2] : emname3 [, parse("pchars")]
将需要切割的字符串放在 emname3 的位置,gettoken
对 emname3 进行一次切割,得到的首个子字符串保存储存在暂元 emname1 中,剩下的部分保存在 emname2 中。emname2 是可选的,即可以不保存切割剩下的部分。切割符由 parse()
中指定,如不定义,则默认为空格。例如:
. local k = "3 5 7"
. gettoken 1 k:k
. dis `1'
3
. dis "`k'" // 暂元k保存了切割剩余部分
5 7
. gettoken 2 k:k
. dis `2'
5
. gettoken 3 k:k
. dis `3'
7
在阶乘的例子中,具体思路如下:
macro shift
命令,此时暂元 1 中的数值被删除,第一次切割剩余部分被保存到了暂元 2 中。此时再进行一次切割,同样将得到的数储存在暂元 1 中,把剩余部分储存在暂元 3 中。capture program drop myfact12
program define myfact12
version 15
syntax anything(name=k) [, Format(string)]
gettoken 1 3: k
while "`1'"!=""{
local a = 1
local k = `1'
forvalues i = 1/`k'{
local a = `a'*`i'
}
dis "`k'! = " `format' `a'
macro shift // 将暂元 3 的内容赋予了暂元 2
gettoken 1 3: 2
}
end
myfact12 6 4
. myfact12 6 4
6! = 720
4! = 24
命令的编写者一般都会提供帮助文档以帮助用户理解程序的使用。帮助文档一般包括程序语句、描述、附加选项介绍和例子等。以下是帮助文档的编写格式,更多详细内容可以通过 help smcl
命令查看。
Code Description
--------------------------------------------------------------------------------------------
{smcl} 回车
{hline} 产生一行黑色直线
{p 4 9 2} 首行前4个空格; 之后每行前9个空格; 行右侧留2个空格的空间
{pstd} 和{p 4 4 2}相同
{phang} 和{p 4 8 2}相同
{phang2} 和{p 8 12 2}相同
{p_end} 代替空白行表示段落的结束
{hi:xxx} highlight/突出显示,一般用于引用
{title:xxx} 以级标题格式显示
{bf:xxx} 粗体
{marker xxx} 加下划线
{it:xxx} 斜体
{cmdab:text1:text2} 显示 Stata 命令; text1 表示最小缩写, text2 表示最小缩写正文的其余部分
{cmd:xxx} 显示 Stata 命令(以加粗形式)
{help format:fmt} 对 fmt 添加超链接 help format
{inp:.} 显示用户输入命令
以下为 help myfact
的结果展示:
help myfact
-------------------------------------------------------------------------------------
Title
myfact -- Cualculate factorial of an integer.
Syntax
myfact number [, generate(newvarname) format(format)]
where number is an positive integer number.
Description
myfact calculates the factorial of a positive integer below 171 and outputs the result in the format specified by the user and generate a new variable to restore the value.
Options for myfact
format[(%fmt)] determind the output form of the answer.
generate[(newvarname)] generate a new variable to restore the value.
Example
myfact 10
Calculate the factorial of number 10.
myfact 10, format(%12.3f)
Specify the output form.
myfact 10, gen(x1) format(%12.3f)
Specify the output form and generate a new variable named x1 to restore the value.
Author
Cong Qiu
SUN YAT-SEN UNIVERSITY(中山大学)
Guangzhou, China
qiuz@mail2.sysu.edu.cn
以下为帮助文档源代码:
{smcl}
{* 6October2017}{...}
{hi:help myfact}
{hline}
{title:Title}
{phang}
{bf:myfact} {hline 2} Cualculate factorial of an integer.
{marker syntax}{...}
{title:Syntax}
{p 8 17 2}
{cmdab:myfact} {it:number} [{cmd:,} {cmdab:gen:erate(}{it:newvarname}{cmd:)} {cmdab:for:mat(}{it:format}{cmd:)}]
where number is an positive integer number.
{marker description}{...}
{title:Description}
{pstd}
{cmd:myfact} calculates the factorial of a positive integer below 171 and
outputs the result in the format specified by the user and generate a new
variable to restore the value.
{p_end}
{marker options}{...}
{title:Options for myfact}
{phang}
{opt format}{opt [}{opt (%fmt)}{opt ]} determind the output form of the answer. {p_end}
{phang}
{opt generate}{opt [}{opt (newvarname)}{opt ]} generate a new variable to restore the value. {p_end}
{marker example}{...}
{title:Example}
{pstd}
{phang}
{cmd: myfact 10}
{p_end}
{pstd}
Calculate the factorial of number 10.
{phang}
{cmd: myfact 10, format(%12.3f)}
{p_end}
{pstd}
Specify the output form.
{phang}
{cmd: myfact 10, gen(x1) format(%12.3f)}
{p_end}
{pstd}
Specify the output form and generate a new
variable named x1 to restore the value.
{title:Author}
{pstd}Cong Qiu{p_end}
{pstd}SUN YAT-SEN UNIVERSITY(中山大学){p_end}
{pstd}Guangzhou, China{p_end}
{pstd}qiuz@mail2.sysu.edu.cn{p_end}
Note:产生如下推文列表的 Stata 命令为:
lianxh 程序, m
安装最新版lianxh
命令:
ssc install lianxh, replace
免费公开课
最新课程-直播课
专题 | 嘉宾 | 直播/回看视频 |
---|---|---|
⭐ 最新专题 | 文本分析、机器学习、效率专题、生存分析等 | |
研究设计 | 连玉君 | 我的特斯拉-实证研究设计,-幻灯片- |
面板模型 | 连玉君 | 动态面板模型,-幻灯片- |
面板模型 | 连玉君 | 直击面板数据模型 [免费公开课,2小时] |
⛳ 课程主页
⛳ 课程主页
关于我们
课程, 直播, 视频, 客服, 模型设定, 研究设计, stata, plus, 绘图, 编程, 面板, 论文重现, 可视化, RDD, DID, PSM, 合成控制法
等
连享会小程序:扫一扫,看推文,看视频……
扫码加入连享会微信群,提问交流更方便
✏ 连享会学习群-常见问题解答汇总:
✨ https://gitee.com/arlionn/WD
New!
lianxh
命令发布了:
随时搜索连享会推文、Stata 资源,安装命令如下:
. ssc install lianxh
使用详情参见帮助文件 (有惊喜):
. help lianxh