Stata:蒙特卡洛模拟分析 (Monte Carlo Simulation)

作者:陈勇吏(上海交通大学)

连享会 - 与君分享 lianxh.cn

文章目录

    • 1. 蒙特卡洛模拟(MC)简介
        • 连享会计量方法专题……
    • 2. 两种常用的 MC 方法
      • 2.1 postfile 命令
        • 2.1.1 `postfile` 命令语法
        • 2.1.2 postfile 的使用方式
        • 连享会计量方法专题……
      • 2.2 simulate 命令
    • 3. Stata 实现
      • 3.1 Stata 范例 1:对数正态分布的均值和方差
      • 3.2 Stata 范例 2:内生性偏误的影响
    • 参考文献
        • 关于我们

本文介绍 Stata 中做蒙特卡洛模拟的两种常用方法。第一种方法是使用 postfile 命令,第二种方法是 simulate 命令,并举了两个具体的例子,说明如何在 Stata 中做蒙特卡洛模拟。

1. 蒙特卡洛模拟(MC)简介

蒙特卡洛模拟方法(MC),即从总体中抽取大量随机样本的计算方法。当根据总体的分布函数 F ( x ) F(\bf x) F(x) 很难求出想要的数字特征时,可以使用蒙特卡洛模拟的方法,从总体中抽取大量样本,使用样本的数字特征估计总体的数字特征。

比如,我们想知道 E [ g ( X ) ] E[g(\bf X)] E[g(X)],其中 X {\bf X} X 是随机向量,其概率密度函数为 f ( x 1 , x 2 , . . . x n ) f(x_1, x_2, ... x_n) f(x1,x2,...xn)。根据期望公式可以得到:

E [ g ( X ) ] = ∫ ∫ ⋅ ⋅ ⋅ ∫ g ( x 1 , x 2 , . . . , x n ) f ( x 1 , x 2 , . . . , x n ) d x 1 d x 2 ⋅ ⋅ ⋅ d x n E[g({\bf X})] = \int\int\cdot\cdot\cdot\int g(x_1,x_2,...,x_n)f(x_1,x_2,...,x_n)dx_1dx_2\cdot\cdot\cdot dx_n E[g(X)]=g(x1,x2,...,xn)f(x1,x2,...,xn)dx1dx2dxn

这是一个多重积分,大多时候很难求解。此时,我们可以使用蒙特卡洛模拟的方法,从总体中抽取大量的样本,通过样本来近似 E [ g ( X ) ] E[g(\bf X)] E[g(X)]。具体操作过程如下:

  • 从总体的概率分布 f ( x 1 , x 2 , . . . x n ) f(x_1, x_2, ... x_n) f(x1,x2,...xn) 中抽取一个随机样本 X ( 1 ) = ( X 1 ( 1 ) , X 2 ( 1 ) , . . . , X n ( 1 ) ) {\bf X}^{(1)} = (X_1^{(1)}, X_2^{(1)}, ..., X_n^{(1)}) X(1)=(X1(1),X2(1),...,Xn(1)),并计算 Y ( 1 ) = g ( X ( 1 ) ) Y^{(1)} = g({\bf X}^{(1)}) Y(1)=g(X(1))
  • 重复 r r r 次 上述过程,得到 r r r 个独立同分布的样本 Y ( i ) = g ( X ( i ) ) ,   i = 1 , . . . , r Y^{(i)} = g({\bf X}^{(i)}),\ i = 1,...,r Y(i)=g(X(i)), i=1,...,r
  • 使用 Y ( i ) Y^{(i)} Y(i)的平均值(样本均值)来近似 E [ g ( X ) ] E[g({\bf X})] E[g(X)](总体均值)。

 

连享会计量方法专题……

2. 两种常用的 MC 方法

MC 方法的原理是从总体中生成大量的样本,Stata 有两种常用的方法。第一种是使用 postfile 命令,另一种是使用 simulate 命令。

2.1 postfile 命令

postfile 命令需要与循环语句结合使用。使用 foreach、while 等循环语句逐次生成独立的样本,并基于样本计算感兴趣的统计值。使用 postfile 命令生成 dta 文件,并将每次循环得到的数据追加进来。蒙特卡洛模拟次数由循环次数决定,最后生成的 dta 文件中,每一行代表一次蒙特卡洛模拟,每一列代表一个基于样本计算出的统计值。

2.1.1 postfile 命令语法

查看帮助文件 help postfile,可以看到三个配套使用的命令 postfile、post、postclose

postfile 命令
postfile 命令的原理是:在内存中划出一个区域,在该区域中存放 MC 中生成的数据。在这个过程中,需要给区域起一个名字,来告诉程序需要把数据存入哪一块区域中。同样地,需要给存入的数据取一个名字,方便索引到这个数据。具体语法规则如下:

postfile postname newvarlist using filename [, every(#) replace]

其中,

  • postname 表示内存中划出的区域的名字,
  • filename 表示保存的数据的名字,
  • newvarlists 表示数据中包含的变量名列表。

简言之,postfile 的工作原理是:在 postname 内存区域中,生成一个 filename 数据文件,该数据包含的变量是 newvarlists

post 命令
post 命令的原理是,在 postfile 生成的数据文件中追加数据。具体语法规则如下:

post postname (exp) (exp) ... (exp)

其中,postname 告诉程序在哪一块内存区域追加数据(即追加到哪个内存区域对应的数据集中)。(exp) (exp) ... (exp) 为追加的数据内容,与 postfile 中的 newvarlist 变量名一一对应。

postclose 命令
postclose 表示结束追加数据,将所有蒙特卡洛模拟过程中 post 的数据写入硬盘中,可以使用 use 命令打开。

2.1.2 postfile 的使用方式

通常情况下,使用 postfile 做蒙特卡洛模拟的 Stata 代码格式如下:

tempname memhold	//定义临时的内存区域的名字
tempfile results	//定义临时的保存文件的名字

postfile `memhold' ... using `results'	//在 `memhold' 内存区域中生成 `results' dta文件,包含 ... 中列出的变量。
forvalues i = 1/# {	    //循环语句,做 # 次蒙特卡洛模拟
	...					//根据总体分布,生成随机样本,进行相关运算
	post `memhold' ...	//追加感兴趣的计算值
}
postclose `memhold'		//结束追加数据,完成 # 次蒙特卡洛模拟

use "`results'", clear	//打开蒙特卡洛模拟生成的数据

连享会计量方法专题……

2.2 simulate 命令

simulate 命令的语法格式如下:

simulate [exp_list] , reps(#) [options] : command

其中,command 为一次蒙特卡洛模拟执行的程序,可以是 Stata 自带的或者外部下载安装的命令,也可以是用户自己封装的程序。[exp_list] 表示将每一次 command 命令的执行结果按 [exp_list] 格式提取出来。

exp_list 格式 表达式 举例
(name: elist) (scale: sd=r(sd) iqr=(r(p75)-r(p25))
elist newvar = (exp)
(exp)
mean=r(mean)
r(sd)
eexp _b、_b[]
_se、_se[]
_b

选项 reps(#) 表示做 # 次蒙特卡洛模拟,即执行 #command 中的命令。其他选项 [options] 和功能如下:

options                  作用
-----------------------------------------------------------
nodots                   MC 过程中不在屏幕上打印点
dots(#)                  每隔 # 次模拟,在屏幕上打印一个点
noisily                  将每次 MC 的结果都显示在屏幕上
trace                    追踪命令运行过程
saving(filename, ...)    将 MC 结果保存在数据中
nolegend                 不显示 MC 信息
verbose                  显示 MC 信息
seed(#)                  设定随机数种子为 #  

 

3. Stata 实现

3.1 Stata 范例 1:对数正态分布的均值和方差

从对数正态分布中,抽取样本量为 100 的样本,计算样本均值与方差。将上述过程重复 1000 次,查看 1000 次蒙特卡洛模拟的结果。

使用 postfile 命令:

clear
set obs 100
set seed 1234
gen b = .

tempname sim
tempfile results
postfile `sim' mean Var using "`results'", replace
quietly {
	forvalues i = 1/1000 {
		replace b = exp(rnormal())
		summarize
		scalar mean = r(mean)
		scalar Var = r(Var)
		post `sim' (mean) (Var)
	}
}
postclose `sim'
use "`results'", clear
sum

使用 simulate 命令:

//封装代码
cap program drop lnsim
program lnsim, rclass
	version 13
	drop _all
	set obs 100
	gen z = exp(rnormal())
	summarize z
	return scalar mean = r(mean)
	return scalar Var = r(Var)
end

set seed 1234
simulate mean=r(mean) var=r(Var), reps(1000) nodots: lnsim
describe *
sum

两个命令的结果是一样的:

. describe *
              storage   display    value
variable name   type    format     label      variable label
-----------------------------------------------------------------------
mean            float   %9.0g                 r(mean)
var             float   %9.0g                 r(Var)

. sum
    Variable |        Obs        Mean    Std. Dev.       Min        Max
-------------+---------------------------------------------------------
        mean |      1,000    1.630648    .2173062   1.106372   2.612052
         var |      1,000     4.60798    4.502166    .966087   70.55969

3.2 Stata 范例 2:内生性偏误的影响

实验拟合模型 y j = a + b x j + u j y_j = a + bx_j + u_j yj=a+bxj+uj。假设真实的模型参数为: a 0 = 1 a_0 = 1 a0=1, b 0 = 2 b_0 = 2 b0=2, u j = c x j + v j u_j = c x_j + v_j uj=cxj+vj, v j ∼ N ( 0 , 1 ) v_j \sim N(0,1) vjN(0,1)

显然,当 c ≠ 0 c \neq 0 c=0 时,该模型存在内生性问题,因为 C o r r ( x j , u j ) ≠ 0 Corr(x_j, u_j) \neq 0 Corr(xj,uj)=0。在随后的模拟分析中,我们可以设定不同的 c c c 值 ( c c c 越大,表示内生性问题越严重),来分析 OLS 估计量的偏误程度。

在实验过程中,我们将维持保存参数估计值和标准误差,并尝试不同的 c c c 的参数取值。 x j x_j xj 在实验中固定,且从 N ( 0 , 1 ) N(0,1) N(0,1) 中生成。

在这之前,生成一个真实数据 truth.dta

drop _all
set obs 100
set seed 54321
gen x = rnormal()
gen true_y = 1 + 2*x
save truth.dta, replace 

根据 truth.dta 数据,做 10000 次蒙特卡洛模拟。在每一次模拟过程中:

  • 生成随机序列 v j ∼ N ( 0 , 1 ) v_j \sim N(0,1) vjN(0,1)
  • 生成干扰项序列: u j = 2 x j + v j u_j = 2 x_j + v_j uj=2xj+vj
  • 计算 y j = 1 + 2 x j + u j = 1 + ( 2 + c ) x j + v j y_j = 1 + 2x_j + u_j = 1 + (2+c)x_j + v_j yj=1+2xj+uj=1+(2+c)xj+vj
  • reg y x 命令拟合模型,提取参数估计值和标准误。

显然,如果设定 c = 0 c=0 c=0,即不存在内生性问题,则模型 y j = α + β x j + u j y_j = \alpha + \beta x_j + u_j yj=α+βxj+uj 的 OLS 估计值 β ^ x O L S ≃ b 0 = 2 \hat{\beta}_x^{OLS} \simeq b_0 = 2 β^xOLSb0=2

使用 postfile 命令:

use truth.dta, clear
set seed 123
local c = 3

tempname sim
tempfile results
postfile `sim' b se using "`results'", replace
quietly {
  forvalues i = 1/10000 {
  	capture drop y
  	gen y = true_y + (rnormal() + `c'*x)
  	reg y x
  	post `sim' (_b[x]) (_se[x])
  }
}
postclose `sim'

use "`results'", clear
sum

使用 simulate 命令:

set seed 123
cap program drop hetero
program hetero
  version 13
  args c
  capture drop y
  gen y = true_y + (rnormal() + `c'*x)
  regress y x
end

simulate _b _se, reps(10000): hetero 3

两个命令的结果是一样的:

===== postfile 命令的结果 ====
. sum
    Variable |        Obs        Mean    Std. Dev.       Min        Max
-------------+---------------------------------------------------------
           b |      1,000    5.000521    .0998493   4.748041   5.342362
          se |      1,000    .0996826    .0069156   .0757347   .1218035

===== simulate 命令的结果 ====
. sum
    Variable |        Obs        Mean    Std. Dev.       Min        Max
-------------+---------------------------------------------------------
        _b_x |      1,000    5.000521    .0998493   4.748041   5.342362
     _b_cons |      1,000    .9970645    .0972862   .6995795   1.323864
       _se_x |      1,000    .0996826    .0069156   .0757347   .1218035
    _se_cons |      1,000    .0997151    .0069178   .0757594   .1218432

在本例中,由于我们设定 c = 3 c=3 c=3,导致存在内生性问题。最终的 β ^ x O L S \hat{\beta}_x^{OLS} β^xOLS 严重偏离了其真实值 b 0 = 2 b_0=2 b0=2。有趣的是,我们估计出的 M e a n ( β ^ x O L S ) ≃ 5 = 2 + c   ( c = 3 ) Mean(\hat{\beta}_x^{OLS}) \simeq 5 = 2+c \ (c=3) Mean(β^xOLS)5=2+c (c=3)

有兴趣的读者,可重新设计一个 DGP (数据生成过程),把工具变量估计考虑进去,进而模拟一下 IV 或 2SLS 估计法是否存在偏误。如下是我建议的一个 DGP,我们将在下一篇推文中公布完整的分析过程。

    *-Ref: Cameron and Trivedi (2009), pp.143  Section 4.6.5
	
	*--------------DGP----------------
	*
	*   y = a + b*x + u;   u~N(0,1)
	*   
	*   x = z + 0.5*u;     z~N(0,1)
	*
	*   a=10;  b=2;  N=150
	*
	*---------------------------------
	
	*-Q1: OLS 估计的性质如何?
	*
	*-Q2: IV  估计的性质如何?

参考文献

  • Cameron A C, Trivedi P K. Microeconometrics Using Stata[M]. 2009. [PDF]
  • Clarke, D., & Matta, Benjamín. Practical considerations for questionable IVs. Stata Journal, 2019, 18 (3), 663-691. [PDF]


关于我们

  • Stata连享会 由中山大学连玉君老师团队创办,定期分享实证分析经验。直播间 有很多视频课程,可以随时观看。
  • 你的颈椎还好吗? 您将 ::连享会-主页:: 和 ::连享会-知乎专栏:: 收藏起来,以便随时在电脑上查看往期推文。
  • 公众号推文分类: 计量专题 | 分类推文 | 资源工具。推文分成 内生性 | 空间计量 | 时序面板 | 结果输出 | 交乘调节 五类,主流方法介绍一目了然:DID, RDD, IV, GMM, FE, Probit 等。
  • 公众号关键词搜索/回复 功能已经上线。大家可以在公众号左下角点击键盘图标,输入简要关键词,以便快速呈现历史推文,获取工具软件和数据下载。常见关键词:
    • 课程, 直播, 视频, 客服, 模型设定, 研究设计,
    • stata, plus,Profile, 手册, SJ, 外部命令, profile, mata, 绘图, 编程, 数据, 可视化
    • DID,RDD, PSM,IV,DID, DDD, 合成控制法,内生性, 事件研究
    • 交乘, 平方项, 缺失值, 离群值, 缩尾, R2, 乱码, 结果
    • Probit, Logit, tobit, MLE, GMM, DEA, Bootstrap, bs, MC, TFP
    • 面板, 直击面板数据, 动态面板, VAR, 生存分析, 分位数
    • 空间, 空间计量, 连老师, 直播, 爬虫, 文本, 正则, python
    • Markdown, Markdown幻灯片, marp, 工具, 软件, Sai2, gInk, Annotator, 手写批注
    • 盈余管理, 特斯拉, 甲壳虫, 论文重现
    • 易懂教程, 码云, 教程, 知乎

Stata:蒙特卡洛模拟分析 (Monte Carlo Simulation)_第1张图片

你可能感兴趣的:(stata编程,Bootstrap-MC)