Stata连享会 主页 || 视频 || 推文 || 知乎 || Bilibili 站
温馨提示: 定期 清理浏览器缓存,可以获得最佳浏览体验。
New!
lianxh
命令发布了:
随时搜索推文、Stata 资源。安装:
. ssc install lianxh
详情参见帮助文件 (有惊喜):
. help lianxh
连享会新命令:cnssc
,ihelp
,rdbalance
,gitee
,installpkg
⛳ Stata 系列推文:
作者:李辉旸 (中山大学)
邮箱:lihy299@mail2.sysu.edu.cn
编者按:本文主要摘译自下文,特此致谢!
Source:Cox N J. Speaking Stata: Fun and fluency with functions[J]. The Stata Journal, 2011, 11(3): 460-471. -PDF-
目录
命令是 Stata 舞台上的主角,但命令又依赖于函数来完成它们的工作。今天的推文将对 Stata 中的函数由总到分做一个简要介绍,以加深大家对其了解。
在最开始,首先需要明悉 Stata 函数的定义和基本性质。
“函数”源自于对英语单词 "function" 的翻译,在中文语境之下,“函数”一词依场合不同亦拥有不同代指对象:
Stata 作为一种数据导向的编程语言,对于函数的定义更贴近于计算机科学:接受用户提供的 n 个参数 (此处的 n 可以为 0),并根据定义函数的规则对参数进行计算,返回单个结果的代码块。其基础结构为 fname(input1,input2,...)
,例如:
runiform()
ln(42)
strpos("Stata", "S")
与 C、Python 等编程语言不同的是,在 Stata 中命令不被认为是函数。比如 Python 中最为基础的 print()
函数,在 Stata 中与之对应功能的被称为 display
命令,而非函数。
Stata 中的函数具有如下基本特性。作为一种命令驱动型的语言,Stata 函数仅是对参数值进行运算的子程序,本身不会输出结果,必须与 Stata 命令一起使用。如果仅仅在命令行中输入函数本身,Stata 将会报错:
. ln(42)
command ln is unrecognized
r(199);
正确的使用方法是:
. dis ln(42)
3.7376696
Stata 中的函数数量并不算太多,一个 19 页的 帮助文件 便可穷尽,其主要目的在于提供真正有用的核心功能。
一些复杂的输出不会有专门函数对应,而是需要用户组合使用,但是当需要几个函数解决问题时,用户经常会寻找命令或认为他们需要程序来进行处理。一个真实情境是,对于变量观察值的累加,有人想到的是跑一个循环语句,而忽略了只需要一个 sum()
函数就能达成目的。这也说明了 Stata 函数的又一个特性:函数可以将变量、表达式等作为参数,而不仅仅是数值。
gen sum_var1 = sum(var1)
gen y = exp(runiform(z))
所有函数都在指定的域上定义,并在指定的范围内返回值。当一个参数在函数的域之外时,函数将返回缺失值或报错——以最合适的方式为准。
. dis ln(0)
.
. dis ln("Hello, Stata!")
type mismatch
r(109);
当输入的参数类型相同但在定义域之外时 (ln(x)
是数值函数,对字符串无定义),Stata 选择报告“类型不匹配”。最后,Stata 函数一般不能由用户编写,只能调用。当然,用户能够在 Mata 环境下编写函数,也可以编写 egen
函数,只是此处的“函数”依赖于更宽泛的定义。
函数的本质是一种工具,相比了解定义和性质,更关键的是认识这种工具基本的和更优的使用方法,提高生产力。
Stata 函数的使用需要遵循以下一些基本原则:
display
、 generate
等命令处理函数运算得出的数据才能真正发挥函数的强大作用,同时大幅提升效率。"Hello, Stata!"
),除非输入的是具有字符串存储类型的变量名称。P = comb(r, k) * comb(n-r, m-k) / comb(n, m)
或者,从对数正态分布中获得一个随机样本:
gen X = exp(rnormal())
需要注意的是,在分解目标时要注意语法正确,也即每一个左括号 "(" 都需要有对应的右括号 ")" 相对应。
除了以上“必须遵守”的规则,实证操作中的函数使用还有一些小技巧,不妨一读:
第一,使用各种帮助文档。在不确定某一个函数的输出规则或输入限制时,help fname()
将是最权威的帮助手段 (或者,使用它的 PDF 版本 ihelp
)。如果想知道自己的代码设计中有哪些函数可以使用,也可直接查阅 [FN] Functions by category。或者,Stata 专栏的论文乃至连享会的相关推文 (使用命令 lianxh 关键词, m
) 也能给你一些帮助。
第二,用命令来测试函数输出。display
和 graph
命令在此时可以起大作用。log(x)
代指 display
浅试一下。
. display log(10)
2.3025851
sqr(x)
还是 sqrt(x)
?
. display sqr(10)
unknown function sqr()
r(133);
. display sqrt(10)
3.1622777
或者,试试用 twoway function
画点图。
. twoway function clip(x, 0.2, 0.8)
. twoway function chop(x, 1), ra(0 10)
第三,在代码书写时手动布局。逗号后和运算符周围的空格通常会使代码更具可读性。
gen x=1*2+3/4-sqrt(5)+max(6,7)
gen x = 1 * 2 + 3 / 4 - sqrt(5) + max(6, 7)
第四,在编写代码时适当运用机器逻辑思考,巧用函数。比如,想判定多个数值 (变量) 中是否存在某一个等于某常数,很自然的想法是:
a == 1 | b == 1 | c == 1 | d == 1 | e == 1
但其实可以等价于思考:
1 == a | 1 == b | 1 == c | 1 == d | 1 == e
采用后者的思维,则一句 inlist(1, a, b, c, d, e)
就能够代替冗长的逻辑判断代码。或者,学会以“不在其中”的方式思考。在一个问题中,排除子集的逻辑否定更简单清晰且易于解码,故而在以下情境中,使用上式将优于下式:
if !inlist(arguments)
if inlist(arguments) == 0
Stata 提供数学函数、概率和密度函数、矩阵函数、字符串函数、处理日期和时间序列的函数,以及一组特殊函数供程序员使用。虽然 Stata 函数如上文所述已经尽可能精简,但在一篇文章里大水漫灌式地逐一阐述也是不可能的。因此,本节的讨论将主要着眼于数个分类中的一些常用、实用函数,并结合一些案例来说明这些函数的使用场景。
本小节列示的函数主要着眼于超越数据类型的使用情境,较为基础。
数据类型转换在实证分析中很常见:即使是数字,也经常会有以字符串为数据类型的观察值——显然这样后续的所有数据操作都无法进行。所以,有必要对 2 个类型转换函数先行介绍。
real()
函数接受字符串变量输出对应数值;string()
反之,其同时也能接收 format 参数,用以设置输出的字符串类型的数字的格式。
. dis real("1")
1
. dis string(1)+ "STATA"
1STATA
. dis string(1, "%9.2f")
1.00
有些用户使用 destring
或 tostring
(参见[D] destring) 以及其中的 force
选项来转换数据类型。操作可以达成目标,但这是一种倒退:上述命令只是函数的精心包装器。在知道有函数可以转换的情况下,直接调用函数无疑是更快更不易出错的方式。
cond(x, a, b[, c])
是一个实用的条件输出函数,其输出规则如下:当
. dis cond(0 == 0, "a", "b", "c")
a
. dis cond(0 == 1, "a", "b", "c")
b
. dis cond(., 1, 2, 3)
3
. dis cond(., 1, 2)
1
cond()
函数的实用性体现于如下一个判断某一年是否为闰年的嵌套代码 (复杂度源自“百年不闰,四百年闰”之特殊置闰规则):
cond(mod(year, 400) == 0, 1, cond(mod(year, 100) == 0, 0, cond(mod(year, 4) == 0, 1, 0)))
inlist()
和 inrange()
可以用以判定某一个元素是否在某一个集合之中:前者为穷举情形,后者则给出“连续”范围限定。inlist(x, a, b,...)
将会判断 inrange(x, a, b)
采用相似逻辑,但将集合替换为
. dis inlist(1, 1, 2)
1
. dis inlist(1, 0, 2)
0
. dis inlist(., ., 1)
1
. dis inrange(1, 0, 2)
1
. dis inrange(0, 1, 2)
0
. dis inrange(1.5, 2, 1)
0
有关 inrange()
,尚有以下规则。在缺失值作为区间边界时,一般视其为无穷大值,但对
. dis inrange(1.5,1,.)
1
. dis inrange(.,1,.)
0
允许将字符串类型数据作为输入,也即按字母顺序表 (由 sort
命令决定) 来确定各个字母的“大小”,设定范围。基于此原则,可以设置以下检测字母大小写的代码:
inrange(char, "a", "z")
inrange(char, "A", "Z")
范围判定函数可以作为高效的逻辑判断语句,正如在 3.2 小节中展示的那样。
Stata 中的缺失值在一般操作中被定义为无穷大,于是将其排除的最佳方式是 if x < .
。一般而言,只要不存在诸如 .a
或 ""
之类的扩展缺失值,if x != .
也可以达到目的。当然,最正规的途径还是求助于 missing()
函数。missing(x1, x2,…, xn)
在存在 !missing()
反之。
如果只关心数值变量,则使用 reg
命令后再提取 e(sample)
也是可以的,因为回归过程将自动舍弃所有缺失值。
Stata 是一款数据处理软件,对各种数值的处理向来是重中之重。以下是一些和数值相关的函数专题介绍。
这一专题里的几个函数能够提取输入数值的部分特征,输出用户想要的结果。
abs(x)
提取 sign(x)
则提取
对于缺失输入值,此二函数都会返回缺失值。mod(x, y)
返回
if mod(_n, 2) == 1
Stata 内置了 5 个取整函数,但他们拥有不同的取整规则。具体说来,ceil(x)
向上,floor(x)
向下,int(x)
和 trunc(x)
向零。round(x)
可以认为是 round(x, 1)
的规范写法,其作用是将 round(x, y)
将会把
. dis ceil(1.2)
2
. dis floor(1.8)
1
. dis int(1.8)
1
. dis trunc(-1.8)
-1
. dis round(1.5)
2
. dis round(1.5,1.4)
1.4
取整函数可以用以一些简单的逻辑判断,比如以下就是判断输入是否为整数的代码:
if x == ceil(x)
if x == floor(x)
etc.
round(x, y)
的特性可能会被滥用于浮点数的显示格式,这并不妥当——编程语言用二进制进行运算,而大多数浮点数并没有其完全等价的二进制表达。dis round(1.23,0.1)
确实能显示 1.2,但这不是精确的结果。因此,要想规范浮点数的显示格式,唯有使用 format
命令。
此节中主要出现的函数有 max()
、min()
、sum()
等。
顾名思义,max(x1, x2,...)
返回输入值中的最大值,min(x1, x2,...)
反之,而 sum(var1)
(更应该被写为 cusum()
) 则能将输入的变量 var1 按样本点顺序累加。这三个函数都会自动忽略缺失值 (除非全部输入都是缺失值,特别地,sum()
在此情况下将返回 0)。如果非要把缺失值按照一个无穷大值作为比较,可以使用以下代码:
cond(missing(a, b,...), ., max(a, b,...))
只要存在缺失值,就会返回 .
,而不是 max()
的非缺失最大值。最值函数的存在并不影响一个事实:对于一个变量,应该使用 summarize
获得极端值 (因为真的巨方便)。但是对于统计累计出现的最大值呢?此时巧妙运用 max()
的代码将发挥奇效:
gen record = .
replace record = max(record[_n-1], var1)
对于面板数据,添加代码 by id (time), sort:
便可实现以个体分组的按时序统计。
余下尚未介绍的数值函数主要包括算术类、随机数类、统计类等。
算术类函数主要是对输入的数据进行基本初等函数运算,比如 ln()
、sqrt()
、sin()
等。其中值得一提的是 logit()
和 invlogit()
,定义也很清晰:
随机数类函数主要以特定分布生成随机数样本,基本结构为:rdist(a,...)
。比如,rchi2(df)
会生成以自由度为 df
的
统计类函数则以报告统计量的 (临界) 值或是生成某些概率分布的分布 (密度) 函数为功能,主要用于假设检验等统计场景。
数值函数对数据的各种处理,最终都将着眼于实际应用场景——即使表面上并不在处理数值。以下是它们的一些实操情境。
autocode(x, n, x0, x1)
、irecode(x, x1,..., xn)
、recode(x, x1,..., xn)
都是 Stata 中对某一批量数据进行分组的函数,原理基本一致,均为对首个输入值
floor(x / n)
ceil(x / n)
分别展示了
round(x, n)
此时的分组为
max()
和 min()
并不仅仅用于极端值的发掘,在逻辑判断中他们也很有作用。对于一个命题的真假,我们可以设定规则简单地以其值为 1 或 0 来判定真假——但若是一群命题呢?如何判断是全真全假或是存在例外呢?下面的代码块给出了答案:
min(arg1, arg2,...) == 0 // 部分假
min(arg1, arg2,...) == 1 // 全真
max(arg1, arg2,...) == 1 // 部分真
max(arg1, arg2,...) == 0 // 全假
同样,sum()
也不止步于滚动求和。比如,如果想统计一个面板数据集中不同个体内某一变量 var1 不同值的按时序累计出现次数,可以这样运用 sum()
:
by id x (time), sort: gen distinct = _n == 1
by id (time), sort: replace distinct = sum(distinct)
基本思想是用 1 来标记 var1 每次新出现的观察值,然后求和,最关键的步骤在于正确排序。
字符串是 Stata 数据中的又一大类型,往往还令人头疼——因为它们没有数值那样的排序和运算逻辑。所以,许多数据管理问题 (包括数据清洗) 都离不开字符串函数。
以下是一些常用字符串函数的介绍。在介绍之前首先要明确,Stata 的字符串操作针对于具体输入值,因此在输入时应当用 ""
将字符串括起,或是先给其“取一个名字” (也可为将其值赋给一个变量,在 Stata 中的大多数字符串处理实际上也是基于变量,甚至标量 (scalar) 本身也是一种变量):
scalar s = "Hello, Stata!"
gen var2 = str(var1) // var1 为一数值变量
Stata 中最常用的字符串函数有 strpos()
、substr()
、subinstr()
、length()
和 reverse()
。 strpos(s1, s2)
会输出 s2 在 s1 中的位置 (未出现返回 0,s2 为空返回 1)。
. dis strpos("this", "is")
0
. dis strpos("this", "it")
0
显然,if strpos(s1, s2) > 0
是一个判断 s1 是否包含 s2 的逻辑判断。这一语句可以用于对某一长段内容是否含有某些元素的检验,从而作为后续分组等操作的基础。substr(s, p, l)
将给出字符串 s 的起始位置为
. dis substr("abcdef", 2, 3)
bcd
. dis substr("abcdef", -3, 2)
de
. dis substr("abcdef", 2, .)
bcdef
subinstr(s1, s2, s3, n)
将 s1 中的前
. dis subinstr("this is this", "is", "X", 1)
thX is this
.dis subinstr("this is this", "is", "X", 2)
thX X this
.dis subinstr("this is this", "is", "X", .)
thX X thX
length(s)
返回 s 的长度。比如,length("ab")
为 2。reverse(s)
将把字符串 s 的所有字母顺序倒置。
. dis reverse("abcdefg")
gfedcba
此外,还有一些值得一提的字符串函数。char(n)
返回十进制编码为 ssc install asciiplot
安装 asciiplot
命令。
Stat a有一组正则表达式函数:regexm()
、regexr()
、regexs()
和 strmatch()
,以帮助程序员更好地进行文本管理。关于 Stata 中的正则表达式使用,参见 Stata | FAQ: Regular expressions。
以下是 Stata 字符串函数的一些应用场景,可能也会穿插一些函数介绍。首先是一段统计子字符串 s2 出现次数的代码:
(length(s1) - length(subinstr(s1, s2, "", .))) / length(s2)
大致思路是获取 s1 总长度和去除 s2 长度,相减得到其中含有 s2 的长度,除以 s2 长度自然就得到了它的出现次数。这一操作由基本函数嵌套完成。需要注意的是,有时先对字符串进行分词将会便利后续操作 (参见 [D] split)。
然后是一个删除首个单词的编程目标。先是对两个函数的介绍:strtrim(s)
能够删除字符串 s 前后的空格,而 word(s, n)
则将自动识别字符串 s 的第
strtrim(substr(s, strpos(s, " "), .))
strtrim(subinstr(s, word(s, 1), "", 1))
两个代码完全等效,利用单词间以空格隔开的惯例,则这两个语句都能准确定位到第一个单词的位置,将其删除并同时删除其后的空格。此装置也强调了英语中规范书写空格的重要性:一旦单词间没有空格,整个装置都会失灵。
. dis strtrim(substr("I love Stata.", strpos("I love Stata.", " "), .))
love Stata.
. dis strtrim(substr("IloveStata.", strpos("IloveStata.", " "), .))
// 输出的是空字符串
第三个问题是对物种名称的清理 (本质是单词格式化输出)。物种 (双名) 中属 (前) 是大写的,而不是种 (后),例如:智人 (Homo sapiens),经济人 (Homo economicus)。对于不符合要求的命名,试一下这行代码:
gen sedit = strupper(substr(s, 1, 1)) + strlower(substr(s, 2, .))
在这里,strupper(s)
会将输入的字符串每一个字符大写,strlower(s)
则会小写。原本还有一个 strproper(s)
会将所有单词换成首字母大写的形式,但这并不是我们想要的。
最后一个问题是文字自动填充。Stata 缺少类似 rep("X", 50)
这样的函数来自动生成一长串重复的文字,但这种文字填充并不是束手无策:
. local text : di _dup(50) "X"
. dis substr("`text'", 1, 50)
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第一个数字参数可以尽可能大,后面的按需填就好 (并不一定都是例中的 50)。
作为数据处理软件,Stata 免不了处理真实世界中各种形式的日期和时间,因此系统也自带了一套日期函数用于面板或时间序列数据集。以下是一些要点简述。
Stata 对时间的处理实际上是数字化的:将所输入的字符串时点与基准日 (1960-01-01) 或基准时 (1960-01-01 00:00:00.000) 做比较,得出一个实数存储之。下面是一个日期的输出实例:
. dis date("2022-12-26","YMD")
23005
一般来说,都是对某一个变量执行日期化操作,在操作后可以再以一个命令 format varlist %fmt
来将日期调整为指定格式,详见 [D] format。Stata 的日期函数可以自动识别诸如闰年等特殊情况。前述的闰年判断器可以简化为:
missing(mdy(2,29, year))
上面的内容介绍了 Stata 中最严格定义的函数。事实上,还有一些“函数”也在 Stata 中发挥了一定作用,下面是对其的一些简要叙述。
Mata 是 Stata 的组成部分,是可以被交互使用、或作为 do-file 和 ado-file 延伸的矩阵编程语言。Mata 看起来很像 Java 和 C,具有用户所期望的结构、指针和类,但增加了对矩阵编程的直接支持。而实际上,Stata 的大多数新特性都是用 Mata 编写的,如多水平模型、潜在类分析和贝叶斯估计等。有关 Mata 的更多介绍和注意事项,参见 Mata - Stata 和 [M-0] Intro。
Mata 提供了更多的函数,这些函数在 [M-4] Intro 中有记录。
egen
命令是 Stata 中的一个重要命令,其根据给定函数的参数,创建一个与该函数相同的可选指定存储类型的新变量。这些函数专为 egen
所用,既有系统提供也可由用户编写。一些 egen
函数能够极大减少代码的使用量。例如,当用户想要每一个样本点的数个变量的横向最大值时,在没有 egen
的情况下,需要如下代码:
gen max = .
foreach v in var1 var2 ... {
replace max = max(max, `v')
}
而这只需要一个 egen rowmax(var1 var2 ...)
便可完成。又如,egen
的 total(var)
函数能够给出组内的总数 (配合 bysort
使用),而在传统函数下,则需要如下的代码:
by id, sort: gen sum = sum(var)
by id: replace sum = sum[_N]
先对每组内排序滚动求和,然后将每组的最后一个求和值 (组内总数) 赋给全组。不仅步骤繁多而且效率低下——全组的观察值仅存储了总数一个数值。即使采用 sum
命令,一样麻烦。甚至,在 4.3.2 小节中介绍的删除首单词的编程目标,也可以使用 egen ends(s), tail
轻松解决,免去了冗长的函数嵌套。
有关 egen
及其函数的使用事项,参见 [D] egen。
Stata 中的函数虽然不能直接输出结果,但在与命令配合使用后能够实现 Stata 中大量的运算、生成和判断任务,是数据处理和实证研究不可或缺的工具。不可否认,Stata 在帮助文件和手册中的函数文档可能是它最枯燥的部分,本文也无法穷尽所有 (否则与帮助和手册无异),于是乎尽量以实例和应用作为讲解主轴,力争加深大家对函数的了解,在日后的实证中善用函数。
Note:产生如下推文列表的 Stata 命令为:
lianxh 函数, m
安装最新版lianxh
命令:
ssc install lianxh, replace
免费公开课
最新课程-直播课
专题 | 嘉宾 | 直播/回看视频 |
---|---|---|
⭐ 最新专题 | 文本分析、机器学习、效率专题、生存分析等 | |
研究设计 | 连玉君 | 我的特斯拉-实证研究设计,-幻灯片- |
面板模型 | 连玉君 | 动态面板模型,-幻灯片- |
面板模型 | 连玉君 | 直击面板数据模型 [免费公开课,2小时] |
⛳ 课程主页
⛳ 课程主页
关于我们
课程, 直播, 视频, 客服, 模型设定, 研究设计, stata, plus, 绘图, 编程, 面板, 论文重现, 可视化, RDD, DID, PSM, 合成控制法
等
连享会小程序:扫一扫,看推文,看视频……
扫码加入连享会微信群,提问交流更方便
✏ 连享会-常见问题解答:
✨ https://gitee.com/lianxh/Course/wikis
New!
lianxh
和songbl
命令发布了:
随时搜索连享会推文、Stata 资源,安装命令如下:
. ssc install lianxh
使用详情参见帮助文件 (有惊喜):
. help lianxh