Stata: gen 命令中的 group() 函数的潜在风险

论坛 期权论坛 期权     
Stata连享会   2019-3-29 10:35   8721   0
[h2][/url][/h2]
作者:连玉君 (知乎 | 简书 | 码云)      
[url=http://mp.weixin.qq.com/s?__biz=MzAwMzk4ODUzOQ==&mid=2247485104&idx=1&sn=8c47b6a91d36cd81fc57f9b9cdd550b6&chksm=9b338be4ac4402f2e2b7358a827202258691bdd76d656d07eca27ce50905f1d47dd6042e0071&scene=21#wechat_redirect]python 爬虫与文本分析专题-现场班

连享会-Python爬虫与文本分析现场班-山西大学 2019.5.17-19[h2]一起学空间计量……[/h2]
空间计量专题-西安 2019.6.27-30[h1]1. 问题背景[/h1]我们经常使用
  1. generate
复制代码
(后文简称
  1. gen
复制代码
) 命令提供的
  1. group()
复制代码
函数对某个变量进行分组,产生分组变量 gg,继而基于 gg 变量进行后续的分组回归分析。
例如,在公司金融中,常用如下代码产生融资约束的分组指标:
    1. *-用公司规模衡量融资约束,分成三组
    复制代码
    1. . bysort code: egen av_size = mean(size)
    复制代码
    1. . sort av_size
    复制代码
    1. . gen gg = group(3)
    复制代码
    1. . gen  FC = (gg==1)     //小规模公司定义为 FC 组
    复制代码
    1. . replace FC=. if gg==2 //丢弃中间组
    复制代码

    1. . reg y x if FC==0
    复制代码
    1. . est store m_FC
    复制代码
    1. . reg y x if FC==1
    复制代码
    1. . est store m_NFC
    复制代码

    1. . esttab m_FC m_NFC
    复制代码
恐怖的事情就要发生了!
后续做分组回归时,你会发现:执行相同的代码,但两次得到的结果居然不同!
原因何在??
[h1]一个小例子[/h1]我先虚构一份数据,让各位了解
  1. group()
复制代码
函数的工作原理,搞明白这件事情后,上面的问题就迎刃而解了。
这份数据很简单,只有 4 行观察值。我们对变量 x 排序后再执行
  1. gen g = group(2)
复制代码
命令,以便将样本分成两组。为了测试分组结果是否唯一\稳定,我进一步使用
  1. tatstat
复制代码
命令计算了两组的均值。
    1. clear
    复制代码
    1. input x    y  
    复制代码
    1.        3   13  
    复制代码
    1.        2   10
    复制代码
    1.        1    1  
    复制代码
    1.        2    8
    复制代码
    1. end
    复制代码

    1. sort x            \\ 由小到大排序
    复制代码
    1. gen g = group(2)  \\ 等分两组
    复制代码
    1. tabstat x y, by(g) f(%3.1f)
    复制代码
    1. sort g x y
    复制代码
    1. list, sepby(g) noobs
    复制代码
第一轮执行的结果如下:
    1. . tabstat x y, by(g) f(%3.1f)
    复制代码

    1. Summary statistics: mean
    复制代码
    1.   by categories of: g
    复制代码

    1.        g |         x         y
    复制代码
    1. ---------+--------------------
    复制代码
    1.        1 |       1.5       5.5
    复制代码
    1.        2 |       2.5      10.5
    复制代码
    1. ---------+--------------------
    复制代码
    1.    Total |       2.0       8.0
    复制代码
    1. ------------------------------
    复制代码

    1. . sort g x y
    复制代码

    1. . list, sepby(g) noobs
    复制代码

    1.   +------------------+
    复制代码
    1.   |  id   x    y   g |
    复制代码
    1.   |------------------|
    复制代码
    1.   | 301   1    1   1 |
    复制代码
    1.   | 201   2   10   1 |
    复制代码
    1.   |------------------|
    复制代码
    1.   | 401   2    8   2 |
    复制代码
    1.   | 101   3   13   2 |
    复制代码
    1.   +------------------+
    复制代码
可以看到,g 取值为 1 和 2 时两组的均值分别为 5.5 和 10.5,差为 5。
我又连续执行了两遍上述代码,结果都没有任何变化,似乎表明上述分组结果是稳定的。
然而,当我执行第四次时,得到了如下结果:
    1. .  tabstat x y, by(g) f(%3.1f)
    复制代码

    1. Summary statistics: mean
    复制代码
    1.   by categories of: g
    复制代码

    1.        g |         x         y
    复制代码
    1. ---------+--------------------
    复制代码
    1.        1 |       1.5       4.5
    复制代码
    1.        2 |       2.5      11.5
    复制代码
    1. ---------+--------------------
    复制代码
    1.    Total |       2.0       8.0
    复制代码
    1. ------------------------------
    复制代码

    1. .  sort g x y
    复制代码

    1. .  list, sepby(g) noobs
    复制代码

    1.   +------------------+
    复制代码
    1.   |  id   x    y   g |
    复制代码
    1.   |------------------|
    复制代码
    1.   | 301   1    1   1 |
    复制代码
    1.   | 401   2    8   1 |
    复制代码
    1.   |------------------|
    复制代码
    1.   | 201   2   10   2 |
    复制代码
    1.   | 101   3   13   2 |
    复制代码
    1.   +------------------+
    复制代码
此时,两组的均值分比为 4.5 和 11.5,差为 7。
留给诸位 2 分钟,对比一下两组结果,然后想想为什么会出现结果不一致的现象?


[h1]2. 揭秘:group() 函数的工作原理[/h1]细心的读者已经发现了结果发生漂移的原因:id=201 和 id=401 的两个观察值的 x 变量具有相同的取值,x=2。然而,他们的 y 变量取值却不同。当我们执行
  1. sort x
复制代码
命令对 x 变量进行排序时,id=201 会随机地被排在第二位或第三位。虽然这对 x 的排序结果没有任何影响,但 y 变量中各个观察值出现的顺序却存在差异。
让我们更细致地解读一下
  1. group()
复制代码
函数的工作原理。
上面的分析中我们用到了如下两条核心命令:
    1. sort x            \\ 由小到大排序
    复制代码
    1. gen g = group(2)  \\ 等分两组
    复制代码
也就是说,
  1. gen
复制代码
命令下的
  1. group()
复制代码
函数按如下两个步骤工作:
  • Step 1: 使用
    1. sort
    复制代码
    命令对样本进行排序;
  • Step 2: 将观察值平均分配为两组 (group(2))。比如,上例中样本数 N=4,那么排序后的第 1 个和第 2 个观察值就被分到第一组 (gg=1),第 3 和 4 个观察值被分到第二组 (gg=2)。
显然,某个观察值被分到第一组还是第二组,并不是由
  1. group()
复制代码
函数决定的,而是它之前的
  1. sort
复制代码
命令决定的。
如果诸位此时输入
  1. help sort
复制代码
命令查看其帮助文件,就会发现他有一个唯一的选项——
  1. stable
复制代码
(我用了十几年的 Stata,今天才发现原来
  1. sort
复制代码
命令还有选项!)
执行
  1. sort x
复制代码
时,如果不加入
  1. stable
复制代码
选项,那么就会随机出现如下两种情形中的一种:
    1.    CaseA          Case_B
    复制代码
    1. -----------  -----------
    复制代码
    1.   x    y     |     x   y
    复制代码
    1.   1    1     |     1   1
    复制代码
    1.   2   10     |     2   8
    复制代码
    1.   2    8     |     2  10
    复制代码
    1.   3   13     |     3  13
    复制代码
    1. -----------  -----------
    复制代码
这就是我们此前看到的两种情形。
加入
  1. stable
复制代码
选项后,每次执行
  1. sort x
复制代码
后得到的结果都是相同的,即 Case A (它维持了原始数据中 y 变量各个观察值的相对顺序)。
其实,上述表述并不严谨,
  1. sort x, stable
复制代码
其实上只是帮助我们维持在执行
  1. sort
复制代码
命令前一刻内存中数据的相对状态。然而,如果在
  1. sort
复制代码
命令之前执行过会打乱样本中各个观察值相对位置的命令,即使在
  1. sort
复制代码
命令中附加
  1. stable
复制代码
选项,仍然无法保证排序结果的稳定性,也就无法保证
  1. group()
复制代码
函数分组的唯一性。
有兴趣的读者可以执行如下命令测试一下。
    1. clear
    复制代码
    1. input x    y  
    复制代码
    1.        3   13  
    复制代码
    1.        2   10
    复制代码
    1.        1    1  
    复制代码
    1.        2    8
    复制代码
    1. end
    复制代码
    1. rsort // 外部命令,随机排序,使用 ssc install rsort 下载
    复制代码
    1. sort x, stable  
    复制代码
    1. gen g = group(2)
    复制代码
    1. tabstat x y, by(g) f(%3.1f)
    复制代码
    1. sort g x y
    复制代码
    1. list, sepby(g) noobs
    复制代码
当然,实证分析中很少有人会使用上例中的
  1. rsort
复制代码
命令主动打乱观察值顺序 (一个典型的特例是在 PSM 分析中使用最近邻匹配时,需要预先打乱观察值顺序)。更一般的情形是如下命令 (你在毫不自知的情况下打乱了样本顺序):
    1. bysort industry year: egen av_x = mean(x)  
    复制代码
    1. sort av_x, stable
    复制代码
    1. gen g_x = group(10)
    复制代码
    1. ……
    复制代码
[h2]2.1 同类问题[/h2]其实,当分组变量本身存在较多重复值时,
  1. egen
复制代码
命令提供的
  1. pctile()
复制代码
函数,以及
  1. quantiles
复制代码
命令都存在上述问题,因为背后的道理都是相同的。
下面是针对
  1. quantiles
复制代码
命令的测试代码,也会存在结果不稳定的问题。
    1. clear
    复制代码
    1. input x    y  
    复制代码
    1.        3   13  
    复制代码
    1.        2   10
    复制代码
    1.        1    1  
    复制代码
    1.        2    8
    复制代码
    1. end
    复制代码

    1. quantiles x, gen(gg) n(2)
    复制代码
    1. tabstat x y, by(g) f(%3.1f)
    复制代码
    1. sort g x y
    复制代码
    1. list, sepby(g) noobs
    复制代码
[h2]2.2 小结[/h2]
  • 如果用于分组变量存在诸多重复值,就非常容易导致上述问题;
  • 上例中,x 变量只有一个重复值 (数值 2 重复出现了两次),极端状况是 x 的所有观察值都集中于某一个取值,那么此时使用
    1. group()
    复制代码
    函数进行分组,就相当于随机分组,可能每次的结果都会不同。有兴趣的读者可以反复运行一下如下代码:
    1. clear
    复制代码
    1. input x    y  
    复制代码
    1.        2   13  
    复制代码
    1.        2   10
    复制代码
    1.        2    1  
    复制代码
    1.        1    8
    复制代码
    1.        2   12
    复制代码
    1. end
    复制代码

    1. sort x
    复制代码
    1. gen g = group(2)
    复制代码
    1. tabstat x y, by(g) f(%3.1f)
    复制代码
    1. sort g x y
    复制代码
    1. list, sepby(g) noobs
    复制代码
[h1]3. 解决方法[/h1]狭义而言,这个问题无解。
实证分析中的建议就是:
  • 如果分组变量本身有很多重复值,那么使用
    1. group()
    复制代码
    函数进行分组是非常糟糕的选择。Note:类别变量 (如教育水平)、计数变量 (如教育年限, 专利个数等) 都会存在很多重复值。此时最好是人为地事先设定分组界点,比如教育年限低于 12 年定义为「Low」,高于 12 年定义为「High」。
  • 如果分组变量是连续变量,如公司规模 (ln(总资产))、负债率等,出现重复值的概率很小,此时可以使用
    1. generate
    复制代码
    命令下的
    1. group()
    复制代码
    函数,但更好的办法是使用
    1. quantiles
    复制代码
    命令,基于分位数进行分组。但需要注意的是,
    1. quantiles
    复制代码
    命令在样本数太小或分组个数太小时都不适用。
  • 更好的办法是基于分位数分组 (但分组结果不再保证每组的样本数相当近似相等),此时可以开始用
    1. xtile
    复制代码
    ,或
    1. egen
    复制代码
    命令下的
    1. pctile()
    复制代码
    函数。
[h2]使用 xtile 命令[/h2]由于
  1. xtile
复制代码
基于分位数分组,使用
  1. sum x, detail
复制代码
命令可知,x 变量的中位数是 2,因此,
  1. xitle gg=x, n(2)
复制代码
会以 x=2 为分界点 (cutpoint) 将样本分成两组,用新生成的变量 gg 加以标记。此时,分组结果并不均等,这在小样本中是非常普遍的事情,对于大样本而言,组间样本数的差异基本上可以忽略。
对于前文提到的类别变量,或续别变量,我们可以使用
  1. xtile
复制代码
命令的
  1. cutpoints()
复制代码
选项人为设定分界点,此处不再赘述。也可以使用
  1. egen
复制代码
命令中的
  1. cut()
复制代码
函数实现相似的目的,缺陷是此时无法配合使用
  1. bysort
复制代码
前缀。
    1. clear
    复制代码
    1. input id    x    y  
    复制代码
    1.        101   3   13  
    复制代码
    1.        201   2   10
    复制代码
    1.        301   1    1  
    复制代码
    1.        401   2    8
    复制代码
    1. end
    复制代码

    1. xtile gg = x, n(2) // New
    复制代码
    1. tabstat x y, by(g) f(%3.1f)
    复制代码
    1. sort g x y
    复制代码
    1. list, sepby(g) noobs
    复制代码
结果如下:
    1. .  tabstat x y, by(g) f(%3.1f)
    复制代码

    1. Summary statistics: mean
    复制代码
    1.   by categories of: gg (2 quantiles of x)
    复制代码

    1.       gg |         x         y
    复制代码
    1. ---------+--------------------
    复制代码
    1.        1 |       1.7       6.3
    复制代码
    1.        2 |       3.0      13.0
    复制代码
    1. ---------+--------------------
    复制代码
    1.    Total |       2.0       8.0
    复制代码
    1. ------------------------------
    复制代码

    1. .  sort g x y
    复制代码

    1. .  list, sepby(g) noobs
    复制代码

    1.   +-------------------+
    复制代码
    1.   |  id   x    y   gg |
    复制代码
    1.   |-------------------|
    复制代码
    1.   | 301   1    1    1 |
    复制代码
    1.   | 401   2    8    1 |
    复制代码
    1.   | 201   2   10    1 |
    复制代码
    1.   |-------------------|
    复制代码
    1.   | 101   3   13    2 |
    复制代码
    1.   +-------------------+
    复制代码
[h3]关于我们[/h3]
  • 【Stata 连享会(公众号:StataChina)】由中山大学连玉君老师团队创办,旨在定期与大家分享 Stata 应用的各种经验和技巧。
  • 公众号推文同步发布于 CSDN-Stata连享会 、简书-Stata连享会 和 知乎-连玉君Stata专栏。可以在上述网站中搜索关键词
    1. Stata
    复制代码
    1. Stata连享会
    复制代码
    后关注我们。
  • 点击推文底部【阅读原文】可以查看推文中的链接并下载相关资料。
  • Stata连享会 精彩推文1   || 精彩推文2
[h3]联系我们[/h3]
  • 欢迎赐稿:  欢迎将您的文章或笔记投稿至
    1. Stata连享会(公众号: StataChina)
    复制代码
    ,我们会保留您的署名;录用稿件达
    1. 五篇
    复制代码
    以上,即可免费获得 Stata 现场培训 (初级或高级选其一) 资格。
  • 意见和资料:  欢迎您的宝贵意见,您也可以来信索取推文中提及的程序和数据。
  • 招募英才:  欢迎加入我们的团队,一起学习 Stata。合作编辑或撰写稿件五篇以上,即可免费获得 Stata 现场培训 (初级或高级选其一) 资格。
  • 联系邮件:  StataChina@163.com
[h3]往期精彩推文[/h3]


欢迎加入Stata连享会(公众号: StataChina)[h2][/url][/h2][url=http://mp.weixin.qq.com/s?__biz=MzAwMzk4ODUzOQ==&mid=2247485104&idx=1&sn=8c47b6a91d36cd81fc57f9b9cdd550b6&chksm=9b338be4ac4402f2e2b7358a827202258691bdd76d656d07eca27ce50905f1d47dd6042e0071&scene=21#wechat_redirect]

[url=http://mp.weixin.qq.com/s?__biz=MzAwMzk4ODUzOQ==&mid=2247485064&idx=1&sn=58e05c5ef029356ddfe71cb6defefd4b&chksm=9b338bdcac4402caebd7ff2e33af05ad0133351e277040f5d365a3da2fdbf2d6036fe75f9789&scene=21#wechat_redirect][/url]
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:190
帖子:39
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP