温馨提示: 定期 清理浏览器缓存,可以获得最佳浏览体验。
王宇桐(清华大学)
E-mail: Yutong_Alicia@163.com
Update:
2020/11/24 10:13
笔者注
Mata
是 Stata 的强大工具,是 Stata 8.0 以前 matrix
的重要扩展。名义上,Mata
似乎是进行矩阵运算的,但把它理解成一个超级容器 会更好。因为,Mata 的单元格里是可以放入字符串以及更复杂的元素。后文第一小节的介绍或许更为准确一些。
Note:
Mata
是一个新创词,Stata 公司的员工们将其读作「Meita」。连老师与 Stata 公司的软件工程总监彭华博士 (Peng Hua) 确认过此事,^=^。
关于 Mata 的 (公开电子) 中文学习资料相对较少。此次推出 Mata 系列,供学有余力的老师和同学参考,也希望与大家多多交流。
本文为 Mata 系列第一篇,初步介绍 Mata 及其基本语法。后续系列中将通过介绍 Mata 编写 OLS 回归程序来引入 Mata 编程等内容。
目录
先总结一下有关 Mata 的关键词:高级编程语言 (Stata 中),编译型语言,矩阵语言,交互和拓展。
以下是三个 (近) 官方的定义:
Stata 官网:
Mata 是 Stata 的高级编程语言,运算速度快,它看起来很像 Java 和 C ,但是增加了对矩阵编程的直接支持。当用户希望快速地执行矩阵计算或需要编写复杂的程序时,可以使用 Mata。
作为一门编程语言,Mata 具有用户所期望的结构 (Structure)、指针 (Pointer) 和类 (Classer) 。而实际上,Mata 是 Stata 的开发语言。Stata 的大多数新特性都是用 Mata 编写的,如多水平模型 (Multilevel Modeling),潜在类分析 (Latent Class Analysis) 和贝叶斯估计 (Bayesian Estimation)。
Stata Manual:
Mata 是 Stata 的组成部分,是可以被交互使用、或作为 do-file 和 ado-file 延伸的矩阵编程语言。因此,Mata 适用于以下场景:(1) 以矩阵方式解决问题,并交互式地执行矩阵计算;(2) 向 Stata 中添加新功能。但 Mata 不只是针对 Stata 的开发者,Mata 对所有人都会是有用的工具。
Gould(2008):
确切来说,Mata 是跨平台的代码可移植的编译型编程语言,只是恰好具有矩阵特性。与其矩阵性质同等重要的是,Mata 具有结构、类和指针。
(以下仅是部分优势,还有许多其他强大之处期待大家探索~)
Mata 中的代码会被自动编译成字节码 (Bytecode-Compiled Language),而 Stata 中的另一个编程语言 ado 是解释型语言(Interpreted Language),因此 Mata 会比 ado 的运算速度快 10-40 倍。
举个例子。在 Stata 9 之前尚未引入 Mata 模块,David Roodman 的 xtabond2
命令在 Stata 7 或 Stata 8 中的运行速度,相比在 Stata 9 中运行会慢一个数量级。
对于计算机而言,高级语言最终都需要"翻译"为机器码才可以被"读懂",而这种"翻译"的区别就可以将高级语言大致分为编译型和解释性两类。
编译型语言首先是将源代码编译生成机器码,后再运行。解释型语言的源代码不可以直接翻译成机器码,而是先翻译成中间代码,再由解释器对中间代码进行解释运行。
举个例子。ado 中许多表达清晰的循环,每循环一次,循环中的语句就要被解释(和立即执行)一次,直到循环结束才完成。而在 Mata 中,等价的代码只需要被编译一次,之后直接等待被执行即可,不需要再次对代码进行编译。
因此,编译型语言在运行时,因为预先编译好无需再次执行,就具有更快的速度。当然只是一般而言,编译型语言的开发/调试时间会更长。
具体可参考:
所有版本的 Stata 都会限制矩阵大小 (matrix size),而 Mata 在 Stata/MP 中却可容纳超过 20 亿行和列的矩阵 (Stata/SE 和 Stata/IC 中至多 20 亿)。
如在 Stata 15 中输入 help matsize
,可看到 Stata/MP 和 Stata/SE 可设置的最大矩阵大小为 11000,Stata/IC 为 800。
因为矩阵大小会影响 Stata 估计命令中可以包含的变量的个数,所以有时矩阵大小也会成为一个关键的限制。
将 Stata 中的变量变为矩阵,或将矩阵变为 Stata 变量,会至少占用该数据本身所需内存的两倍。相比复制数据,相反的,Mata 可以通过创建直接引用 Stata 变量内容的矩阵 (类似于虚拟矩阵 views
),来回避这些内存问题,,无论矩阵的大小都仅需要小量的储存空间。
举个例子。假设用户加载了一个包含 200,000 个观察值和 150 个变量的数据集,并且需要在生成一个包含 180,000 个观察值的 80 个变量的矩阵。Stata 中直接生成矩阵可能需要 110MB,Mata 只需要 640 Bytes。
最后,Mata 中配套了更多的矩阵函数,而且明确支持复杂算法和数据类型,如优化、方程求解、分解、特征方程和概率密度函数等等。
首先,Mata 和 Stata 的元素 (变量、暂元和矩阵) 间的交互是非常完整的。
在 Mata 中可以调用 Stata 中的对象并进行修改。在使用 Mata 的 view matrix (前文提到的虚拟矩阵) 时,对 Mata 矩阵的改变也会自动相应改变 Stata 中组成 view matrix 的观测值和变量。
其次,Stata 与 Mata 的程序之间的交互很灵活,促进了时间精力的有效分配。
对于纯粹的矩阵语言,需要处理数据组织、转换和选择等等繁杂细节。但我们可以写一个包含一个或多个 Mata 函数的 ado-file,利用常规的 ado-file 语言来进行数据清洗等等细节,而运用 Mata 来进行数据处理与运算。
综上,Mata 对 Stata 中的传统编程起到了极大的弥补作用,不仅仅是开发者,所有的用户都可以发现 Mata 的价值所在。(可参见本文 4.使用单行命令调用Mata
中的例子)
在正式介绍 Mata 程序编写之前,我们必须了解 Mata 的基础语法,如注释方式、基础运算符、循环、条件语句等。
相较 Stata 的 *
,Mata 中的注释方式发生了较大改变。如果不事先了解 Mata 的注释方式,那么在命令语句写作和运行中可能会多次报错。输入 help mata comments
,可查看相关 help 文档。
Mata 的语句注释分为以下两类:
/* enclosed comment */
// rest-of-line comment
注意点: (1) 注释语句可以出现在 do-files 和 ado-files 中;但不能够在命令窗口的交互状态下使用 (在命令窗口输入 mata
即可进入 Mata 交互状态)。(2) 在 Stata dofile 中常用的 * 注释语句
方式不适用于 Mata 环境。
因此,在运行命令时有以下注意点:
为更好掌握语法,建议读者在 Stata 命令窗口中输入 mata
进入 Mata 环境 (可通过 end
退出),再将下文中的命令语句删去由 // 注释的文字后,逐行输入命令窗口以感受运算过程。(当然,也可以整体粘贴入 do-file 中运行。)
另一方面,在结果显示中,Mata 的命令提示符是冒号 (:
),而在 Stata 中是圆点 (.)。
在本部分,主要介绍 Mata 的运算符中的基础运算 (含元素运算)、逻辑运算、下标运算三大内容。有兴趣的读者可以查看 3.2.4 运算符优先级总结
。Mata 的运算符充分体现了作为矩阵编程语言的特征。
具体运算符和解释已写入以下代码块中,其中较有特色的是元素运算(element-by-element)。
mata //进入 Mata
// --基本运算--
a1=(1,2) //","逗号为列合并运算符
a2=(1..4) //利用".."生成[1,4]范围内的(整数)行向量
a3=(1 \2 \3 \4) //"\"反斜杠为行合并运算符
a4=(1::4) //利用"::"生成[1,4]范围内的(整数)列向量
a5=(1,2)' //"'"撇号为转置运算符
// --代数运算--
b1 = a1 + a1 //"+" 加法
b2 = a1 - a1 //"-" 减法
b3 = a5 * a1 //"*" 乘法
// --例子:混合运算--
c1 = (1, 2, 3 \ 4, 5, 6 \ 7, 8, 9) //生成3×3矩阵
c2 = ((1..3)\(4..6)\(7..9)) //效果同上
c3 = (1 \ 2 \ 3 )' //生成行向量
// --元素对元素运算(element-by-element)--
// -注:Mata中的代数运算,包括用于除法的斜杠(/),都可在前面加一个冒号(:),实现元素运算
r1 = ( 1, 2, 3 )
r2 = ( 1, 2, 3 \ 4, 5, 6 \ 7, 8, 9 )
m1 = r1 :+ r2 //将行向量r1中的元素逐行加入到r2的每一行,最终得到3*3矩阵
m2 = r2 :+ r1 //生成的m2与m1相同
m3 = r1 :/ r2 //将行向量r1中的元素逐行除以r2的每一行,得3*3矩阵
m4 = r2 :/ r1 //将行向量r2中的元素逐行除以r1的每一行,得3*3矩阵
m5 = sqrt(m1) //针对每个元素的开根运算
end //退出 Mata
若想在结果窗口中呈现某个矩阵的内容,只需在 Mata 环境下输入该矩阵的名称,敲回车即可:
mata
a2
b3
m2
m3
end
结果如下:
: a2
1 2 3 4
+-----------------+
1 | 1 2 3 4 |
+-----------------+
: b3
[symmetric]
1 2
+---------+
1 | 1 |
2 | 2 4 |
+---------+
: m2
1 2 3
+----------------+
1 | 2 4 6 |
2 | 5 7 9 |
3 | 8 10 12 |
+----------------+
: m3
1 2 3
+-------------------------------------------+
1 | 1 1 1 |
2 | .25 .4 .5 |
3 | .1428571429 .25 .3333333333 |
+-------------------------------------------+
a==b
和 a!=b
。该逻辑运算不需要考虑 a
和 b
是否一致,或是否是相同类型。其返回值为 0 或 1。>
,>=
,<
,<=
只可以在两个对象是相同的类型 (数字或字符) 且维数 (conformable) 相同时才可以比较。&
) 或 (|
) 只能够用于实数类型的标量 (real scalar)。Mata 使用 中括号 ([]
) 进行下标运算,其可以出现在代数表达式的左端或右端。共有两类:列表下标 (list subscripts) 和 范围下标 (range subscripts)。我们可以利用下标运算来引用、修改、提取数组中元素。
列表下标
下标运算 x[i,j]
中的 i
或 j
可以是一个向量,如:x[i,jvec]
,jvec=(4,6,8)
。i
或 j
也可以缺失 (或用 .
) ,用于指代该行或该列中的所有元素,如:x[i,.]
、 x[i,]
提取第 i
行的所有元素;x[.,.]
、 x[,]
用于提取整个矩阵中的所有元素。
为了避免输入每个连续数字,可以用范围运算符 (前文提到的 ..
和 ::
) 来进行下标运算,如 x[(1..4),.]
和 x[(1::4),.]
,都指代了矩阵 x 的前四行。同时,范围运算也可以是降序的,如 x[(4::1),.]
。
范围下标
范围下标运算方式为 [| |]
。该表达可以用于指代矩阵中的单个元素,但更加重要的是使用 x[| i, j \ m, n |]
,该命令可以生成一个子矩阵,从 x[i, j]
开始至 x[m, n]
结束。形如 x[| 1, 1 \ 4, .|]
和 x[| 4, 4 \., .|]
也可以正常运行,前者代表了子矩阵至最后一列,后者跳过了 x 矩阵的前三行和前三列。
Tip:当可以同时使用范围下标运算
[| |]
和列表下标运算[i,j]
时,使用范围下标运算效率更高 ( 无论是矩阵提取还是用时方面 )。
参考 Gould (2018, p.86),表总结了 Mata 中的运算符及其优先级。利用优先级可以简化代码,例如,a>2+3
与 a>(2+3)
的实现效果相同 (算术运算优先于逻辑运算)。
运算符优先级汇总 (从高到低) | |
---|---|
下标运算 | [] |
递增 & 递减运算 | --, ++ |
结构 & 类元素引用运算 | ->, . |
指针运算 | *, & |
算术运算 | ', ^, - (负号), /, #, *, -, + |
逻辑运算 | !, !=, >, <, >=, <=, ==, &, | |
行填充 | .. |
列合并 | , |
列填充 | :: |
行合并 | \ |
三元条件运算 | ? : |
转为空白 | (void) |
赋值 | = |
注:由冒号 :
代表的元素运算优先级仅低于相应的非冒号运算,如 :==
在逻辑运算 ==
之后,:*
在代数运算 *
之后。
Stata 中的循环有:foreach
、forvalues
和 while
,Mata 中的循环也有三类:for
、while
和 do...while
,但是语法多有不同。
for
和 while
在循环开始时检查循环的运行条件,这意味着循环可能根本不会执行,可以用于优雅地处理极端情况。而 do...while
在循环结束时检查运行条件,用于需要至少循环一次的情况。
Tip:Mata 作为矩阵语言,在可以使用矩阵运算的时候就应当避免使用明确的循环语句。
Mata 中的 for
与 C 语言中的循环语句语法类似。
语法
//单行命令
for (exp1; exp2; exp3) statement
//多行命令需要放入大括号中
for (exp1; exp2; exp3) {
statements
}
/*注意点:
- exp1、exp3 可以为空,但 exp2 不可为空
- exp3 具体表达中可使用递增 (++) 或递减 (--) 运算符,如 i++,++i
*/
例子:求 1~100 间的整数和及平方和
mata //进入mata
v=(1..100)
sum1 = sum2 = 0 //初始化
for (i=1; i<=length(v); i++) {
sum1 = sum1 + v[i]
sum2 = sum2 + v[i]^2
}
(sum1,sum2) //展示结果
end //退出mata
/*输出结果:
: (sum1,sum2)
1 2
+-------------------+
1 | 5050 338350 |
+-------------------+
*/
语法
//单行命令
while (exp) statement
//多行命令需要放入大括号中
while (exp) {
statements
}
//注意 exp 必须是与实数标量相比较
例子:求
Tip:while
通常被用于迭代求解近似值。
mata //进入mata
x=2 //初始化为2
while (abs(x^2-7) > 1e-8){
x = x - (x^2-7)/(2*x) //2*x为f(x)=x^2的求导
}
x //展示结果
end //退出mata
/*输出结果:
: x
2.645751311
*/
do...while
的语法如下:
//单行命令
do stmt while(exp)
//多行命令需要放入大括号中
do {
stmts
} while(exp)
//注意exp必须是与实数标量相比较
与 Stata 类似,在 Mata 中可以使用 if...else
来进行条件判断;不同的是,Mata 中有特殊的三元条件运算符 a ? b : c
if...else
语法结构如下:
if (exp) statement
//或者
if (exp) statement1
else statement2
//或者
if (exp1) {
statement1
}
else {
statement2
}
//或者
if (exp1) {
statements1
}
else if (exp2) {
statements2
}
else {
statements3
}
//注意:exp, exp1, exp2...必须是与实数标量相比较
语法
a ? b : c
//其中,a 必须与实数标量相比较,而 b,c 的形式不受限制
结果输出
如果 a 为真 (a 不为 0),则条件运算符返回 b,否则返回 c。
举个例子
mata //进入mata
k = 2
n = 10
dof = ( k==0 ? n-1 : n-k ) //k=0则返回n-1,否则返回n-k
dof
end //退出mata
/*输出结果:
: dof
8
*/
Stata 命令窗口、do-file 或 ado-file 中输入单行命令 mata:一个或多个 Mata 命令,使用分号分割
,即可实现 Mata 环境的调用。且我们在 Mata 环境中生成的变量会一直保留,直至输入 mata: mata clear
。
在上文中,我们提到 Mata 的一大优势是灵活的交互以实现 Stata 与 Mata 的互补, mata:语句
的方式就是极佳的实现方式。
但是该交互的前提是两者工作空间内的变量与结果的相互调用或修改,因此,我们不可避免需要用到 st_interface
这类函数。
基于 st_interface
类型的函数,Mata 可以获取 Stata 中的所有对象 (数据、宏、标量和矩阵),并实现 Stata 和 Mata 计算结果的无缝交互。
注:以下例子来源于 Baum (2016) Chapter 13.4
在使用 reg
命令后,回归结果的会自动保存在 e()
矩阵中,但
所幸,e()
中保留了估计系数 e(b)
和估计量的方差-协方差矩阵 e(V)
,从而可以通过
思路:
e(b)
和 e(B)
矩阵,以便计算 st_matrix
函数调用 e(b)
和 e(B)
矩阵,并在 Mata 环境中完成 代码:
(命令窗口运行以下语句需删去 //及其注释语句)
*----1.回归----
sysuse auto, clear //调动auto.dta数据集
reg price displacement weight foreign
*----2.单行调用Mata并计算t统计量---
mata: st_matrix("bst", st_matrix("e(b)") :/ sqrt(diagonal(st_matrix("e(V)"))'))
/*该行代码具体解释如下:
st_matrix()的使用可利用 help mata st_matrix 进行查看
bst为逗号后矩阵命名。st_matrix(name, X)将Stata中的名为name的矩阵新建/替换为X中的内容
st_matrix("e(b)")调用系数矩阵
diagonal(st_matrix("e(V)"))'代表提取协方差矩阵后取对角线元素,并转置
利用元素运算符:/完成对应估计量的t统计量运算
*/
*----3.处理生成的bst矩阵的行列名细节----
matrix rownames bst = tstat //行命名
matrix colnames bst = `: colnames e(b)' //列命名
matrix list bst, format(%9.3f) //输出结果
/*矩阵输出结果如下:
. matrix list bst, format(%9.3f)
bst[1,4]
displacement weight foreign _cons
tstat 1.671 3.275 5.745 -2.826
*/
由上述例子可知,通过在 Mata 环境中调用 Stata 中元素,借助 Mata 矩阵运算的优势,轻松实现
虽然 Mata 可以一键输出,Stata 中处理就一定复杂吗?参考 st: Re: p>t value extraction 给出的答案,代码如下:
sysuse auto, clear //调用auto.dta数据集
reg price displacement weight foreign
matrix a = vecdiag(e(V)) //保存e(V)对角线元素,组成行向量
matrix b = (e(b)\a)' //合并矩阵e(b),a至矩阵b,并转置
svmat b //将矩阵b转化为变量
rename b1 beta //将b1变量(对应b矩阵的第一列)重命名为beta
gen se = sqrt(b2) //对b2变量开根,相当于求出se
gen t = beta/se //得到t统计量
list t in 1/4 //列出所有t统计量的值(因为是变量,所以余下的全部缺失)
/*
. mat list b
b[4,2]
y1 r1
displacement 10.253866 37.671085
weight 2.3286255 .50552151
foreign 3899.6304 460717.13
_cons -4048.3431 2052742.2
. list t in 1/4
+------------+
| t |
|------------|
1. | 1.6706426 |
2. | 3.2751395 |
3. | 5.7452147 |
4. | -2.8255962 |
+------------+
*/
通过对比可知,在回归后生成
以上只是一个小例子,Mata 作为专业矩阵编程语言,可以想象它会为我们的科研带来极大的便利。
Tip:以上的方法都是基于回归后生成的
e()
后计算统计量 ,实际上使用回归保留的 r(table) 矩阵会更加方便 ==(目前为止没有找到更简便的方法)==。语句如下,有兴趣的读者可尝试。
matrix a=r(table)
matrix b=a[3,1..4]
mat list b
以上便是 Mata 系列第一讲,敬请期待该系列的后续推文。
关注「Stata 连享会」公众号,回复关键词「Mata」,获取更多 Mata 相关资料。
连享会-直播课 上线了!
http://lianxh.duanshu.com
免费公开课:
直击面板数据模型 - 连玉君,时长:1小时40分钟,课程主页 Stata 33 讲 - 连玉君, 每讲 15 分钟. Stata 小白的取经之路 - 龙志能,时长:2 小时,课程主页 部分直播课 课程资料下载 (PPT,dofiles等)
支持回看
专题 | 嘉宾 | 直播/回看视频 |
---|---|---|
⭐ 最新专题 | 因果推断, 空间计量,寒暑假班等 | |
⭕ 数据清洗系列 | 游万海 | 直播, 88 元,已上线 |
研究设计 | 连玉君 | 我的特斯拉-实证研究设计,-幻灯片- |
面板模型 | 连玉君 | 动态面板模型,-幻灯片- |
面板模型 | 连玉君 | 直击面板数据模型 [免费公开课,2小时] |
Note: 部分课程的资料,PPT 等可以前往 连享会-直播课 主页查看,下载。
关于我们
课程, 直播, 视频, 客服, 模型设定, 研究设计, stata, plus, 绘图, 编程, 面板, 论文重现, 可视化, RDD, DID, PSM, 合成控制法
等
连享会小程序:扫一扫,看推文,看视频……
扫码加入连享会微信群,提问交流更方便
✏ 连享会学习群-常见问题解答汇总:
✨ https://gitee.com/arlionn/WD