Stata数据处理:清洗CFPS数据库

发布时间:2021-04-29 阅读 6717

Stata连享会   主页 || 视频 || 推文 || 知乎

温馨提示: 定期 清理浏览器缓存,可以获得最佳浏览体验。

New! lianxh 命令发布了:
随时搜索推文、Stata 资源。安装命令如下:
. ssc install lianxh
详情参见帮助文件 (有惊喜):
. help lianxh

课程详情 https://gitee.com/lianxh/Course

课程主页 https://gitee.com/lianxh/Course

⛳ Stata 系列推文:

作者:陈波 (深圳大学)
E-Mail: 1900123011@email.szu.edu.cn


目录


1. 引言

对于流行实证分析的经管等学科而言,每逢毕业季,大家都会殚精竭虑的寻找数据,以满足自己的研究需求,所谓“上穷碧落下黄泉,动手动脚找数据”,不外如是。但是,近年来,也有一些现成的微观数据,为大家提供了不少便利。如西南财大的中国家庭金融调查 (CHFS) 、北大国发院的中国健康与养老追踪调查 (CHARLS) 、北大中国社科调查中心的中国家庭追踪调查 (CFPS) 等。很多学界大佬都拿这些数据发过顶刊,数据质量十分可靠;多数数据对外公开,数据获取也较为便利。

但是,有时候拿到数据仅仅意味着开始,数据清洗也是一头来势汹汹的拦路虎。那么,我们该怎么清洗微观数据呢?下面我们就以北京大学的中国家庭追踪调查 (CFPS) 为例,详细讲一下怎么清洗微观数据,并构造一些较为复杂的变量。

2. 准备工作

首先,我们需要从官方渠道申请数据。目前主要有两个方式,一是直接从 CFPS 数据中心下载,该平台提供 Stata 和 SAS 两种格式,并实时更新数据。最近一次更新是 2021 年 4 月 16 日,上载了跨年个人核心变量库和技术报告 CFPS-40 《中国家庭追踪调查跨年个人核心变量库清理报告》。下文的示例也是使用该平台的数据。

北京大学开放数据平台也有提供 CFPS 数据,附有 Stata 和 SAS 两种格式。但是该平台更新较慢,最近一次更新是 2020 年 8 月,且没有最新的 CFPS2018 数据。

同时,两个网站都需要注册并进行认证,但是认证速度很快,一般能在 1-2 个工作日内完成。所以还是建议大家使用正规渠道的数据,不要使用各种非官方的野生数据。

3. 数据清洗

下载好数据之后,我们先新建三个文件夹: CFPS2016 、 CFPS2018 和 Result_data ,存储 CFPS2016 、 CFPS2018 的原始数据和清洗后的数据。我们再在 Result_data 中新建 4 个文件夹: Dofiles 、 Logfiles 、 Temp_data 和 Working_data ,存放数据清洗的 do 文档、 log 文档、产生的过程数据和最终的结果数据。这么复杂的起手式是为了帮助我们在后续清洗过程中理清自己的思路,不至于手忙脚乱,这在处理繁复的数据中尤为重要。

我们使用 global 定义其存储路径,方便后面直接调用,不用输入一串长路径。这也为合作者提供了便利,当我们的合作者需要数据和代码时,我们将文件夹打包发给对方,对方修改一下 root 路径,即可完整的运行代码和输出结果。

* =======================================================
* Program:CFPS数据清洗
* Date:2021-04-25
* =======================================================

clear all
clear matrix
set more off

global root         = "/Applications/Stata/personal/CFPS" // Mac
global root         = "D:/mydata/CFPS" // Windows
global cfps2018     = "$root/CFPS2018"
global cfps2016     = "$root/CFPS2016"
global dofiles      = "$root/Result_data/Dofiles"
global logfiles     = "$root/Result_data/Logfiles"
global temp_data    = "$root/Result_data/Temp_data"
global working_data = "$root/Result_data/Working_data"
log using "$logfiles/cfpsclean.log", replace

3.1 提取变量

我们先以 CFPS2018 为例,讲解截面数据的清洗步骤。

我们先导入 CFPS2018 中的家庭经济问卷,这是一份超过 300 个变量的长问卷,而我们只需要其中小部分变量。因此我们可以使用 keep 提取需要的家庭信息,如家庭id、省份、区县顺序码、村居顺序码、城乡分类等。

*- 提取变量
. use "$cfps2018/cfps2018famecon_202101.dta", clear
. keep fid18 fid16 provcd18 countyid18 cid18 urban18 ///
       resp1pid fk1l ft200 fincome1_per total_asset familysize18

提取完之后,我们先用 sum 看一下这份数据,发现里面有一些异常值,诸如 -9 、 -8 。这些值代表着什么含义呢?

. sum

    Variable |     Obs       Mean    Std. Dev.      Min        Max
-------------+----------------------------------------------------
       fid18 |  14,218   401481.9    316756.2    100051    6759191
       fid16 |  14,218   372239.8    171404.6    100051    2845311
    provcd18 |  14,217   38.13913    14.95284        11         65
  countyid18 |  14,199   567.5955    1681.507         1       9992
       cid18 |  13,122   319687.8    267463.4    100100     999544
-------------+----------------------------------------------------
     urban18 |  14,218   .3703756    1.275113        -9          1
    resp1pid |  14,218   3.85e+08    1.53e+08  1.00e+08   2.49e+09
        fk1l |  14,218   3.241103    1.985484         1          5
       ft200 |  14,218   4.726122    1.186591        -8          5
fincome1_per |  14,218   30592.59    85940.67         0    5660000
-------------+----------------------------------------------------
 total_asset |  13,423   775690.7     1842052  -2470000   5.05e+07
familysize18 |  14,218    3.55901    1.917424         1         21

这时候我们可以使用 label list ,查看变量的标签值。发现原来 -9 是缺失值, -8 是不使用,以及其他一些 missing value :

*- 查看变量标签
. label list urban18
urban18:
         -10 无法判断
          -9 缺失
          -8 不适用
          -2 拒绝回答
          -1 不知道
           0 乡村
           1 城镇

这些缺失值我们用不上,因此可以将数字全部变为 . 。因为涉及多个变量和多个值,我们可以引入 for 循环和 inlist

*- 缺失值替换为.
. for var _all: replace X =. if inlist(X, -10, -9, -8, -2, -1)

另外, label list 一下,发现 fk1lft200 都不是常用的 0-1 变量,而是 1 =是, 5 =否,因此我们可以用 recode 重新赋值:

. *- 重新赋值
. recode fk1l  (1 = 1 "是")(5 = 0 "否"), gen(agri)

. recode ft200 (1 = 1 "是")(5 = 0 "否"), gen(finp)

随后,我们删除掉冗余变量,将其存储到事先建立的 Temp_data 文件夹中:

. *- 存储家庭变量
. drop fk1l ft200

. save "$temp_data/family_2018.dta", replace 

3.2 跨表合并

大部分情况下,单个表的变量并不能满足我们的研究需求,我们需要进行跨表间的变量合并。我们以最常见的家庭库和个人库合并为例。

我们先调入个人库,将所需变量提取出来,并将各种原因的缺失值统一替换为.

*- 提取个人库变量
. use "$cfps2018/cfps2018person_202012.dta", clear
. keep pid fid18 fid16 provcd18 countyid18 cid18 urban18 gender age ///
	qa301 qea0 qp605_s_* cfps2018edu 

. for var _all: replace X =. if inlist(X, -10, -9, -8, -2, -1)

然后对变量重新赋值。户口状况中,有 21人 无户口, 12人 非中国国籍,我们将其做缺失值处理。婚姻状态是一个五分类变量,我们也将其重新赋值,将在婚、同居定义为有配偶,未婚、离婚、丧偶定义为配偶。学历也做相似处理,将大专、大学本科、硕博划分为大学及以上。

. tab qa301

      现在的户口状况 |      Freq.     Percent        Cum.
-------------------+-----------------------------------
           农业户口 |     22,585       73.85       73.85
         非农业户口 |      7,964       26.04       99.89
           没有户口 |         21        0.07       99.96
  不适用(非中国国籍) |         12        0.04      100.00
-------------------+-----------------------------------
             Total |     30,582      100.00


. tab qea0

       当前婚姻 |
          状态 |      Freq.     Percent        Cum.
---------------+-----------------------------------
          未婚 |      4,147       13.56       13.56
  在婚(有配偶) |     23,938       78.25       91.81
          同居 |        133        0.43       92.24
          离婚 |        624        2.04       94.28
          丧偶 |      1,750        5.72      100.00
---------------+-----------------------------------
         Total |     30,592      100.00

. recode qa301 (1 = 1 "农业户口")(3 = 0 "非农户口")(5 79 =.), gen(hukou)

. recode qea0  (2 3 = 1 "有配偶")(1 4 5 = 0 "无配偶"), gen(spouse)

. recode cfps2018edu      ///
     (1 = 0 "文盲/半文盲") ///
     (2 = 1 "小学")        ///
     (3 = 2 "初中")       ///
     (4 = 3 "高中")       ///
     (5 6 7 8 = 4 "大学以以上"), gen(edu)

有时候,我们需要一些较为复杂的变量,例如说受访者是否购买了医疗保险、购买了具体哪种医疗保险,以及购买了几种医疗保险。而医保类型有 5 个变量,我们不能直接对其进行 recode ,这个时候就需要善用 for 循环了。

我们先将 5 个医保变量中的数值 78 替换为 . ( 78 表示以上都没有),然后生成 3 个变量:medsure_dummedsure_xnhmedsure_num,分别表示是否购买医保、是否购买了新农合,以及购买医保数量。

我们编写了 3 个 for 循环,第一个循环计算是否购买医保,其含义是:若 5 个医保变量中任一一个存在非缺失值(回答了具体保险类型),就将 medsure_dum 替换为 1 。第二个循环是计算是否购买新农合,其含义是:若 5 个医保变量中,任一一个回答其购买了新农合(标签值为 5 ),就将 medsure_xnh 替换为 1 。第三个循环是计算医保数量,其含义是:若医保类型 i 非缺失值,就在 medsure_num 的基础上加 1 。

. des qp605_s_*

              storage   display    value
variable name   type    format     label      variable label
------------------------------------------------------------
qp605_s_1       double  %47.0g     qp605_s_1
                                              医疗保险类型1
qp605_s_2       double  %47.0g     qp605_s_2
                                              医疗保险类型2
qp605_s_3       double  %47.0g     qp605_s_3
                                              医疗保险类型3
qp605_s_4       double  %47.0g     qp605_s_4
                                              医疗保险类型4
qp605_s_5       double  %47.0g     qp605_s_5
                                              医疗保险类型5


*- 计算是否有医保及医保数量
. for var qp605_s_*: replace X =. if X == 78
. gen medsure_dum = 0
. gen medsure_xnh = 0
. gen medsure_num = 0

. for var qp605_s_*: replace medsure_dum = 1 if X !=.
. for var qp605_s_*: replace medsure_xnh = 1 if X == 5
. for var qp605_s_*: replace medsure_num = medsure_num + 1 if X !=.

最后我们删除冗余变量,也将其存储到的 Temp_data 文件夹中:

. drop qa301 qea0 qp605_s_* cfps2018edu

. save "$temp_data/person_2018.dta", replace

闲话少叙,现在我们正式进行跨表合并。

跨表合并也分为两种,一种是以个人库作为主表 master ,家庭库作为附表 using ,进行合并。此类研究一般是以个体作为分析对象,例如研究医保对个体健康的影响。另一种是以家庭作为 master ,个人库作为 using ,此类研究一般以家庭作为分析对象,例如研究金融素养对家庭金融投资的影响。

如果以个人库作为 master ,一般是以家庭 id ifd18 作为唯一识别码,进行 mergekeepusing 为指定using中具体的变量, keep(1 3) 为仅保留 master 和匹配成功的样本, nogen 为不生成 _merge 变量。

. *- 跨表合并
		//个人库merge家庭库
. use "$temp_data/person_2018.dta", clear

. merge m:1 fid18 using "$temp_data/family_2018.dta", ///
        keepusing(fincome1_per total_asset familysize18 agri finp) keep(1 3) nogen
   

    Result                           # of obs.
    -----------------------------------------
    not matched                           619
        from master                       619  
        from using                          0  

    matched                            36,735  
    -----------------------------------------

可以看到,有 619 个样本没有匹配到家庭信息,它们的家庭信息可能因为某些原因缺失了。我们给部分变量加上 label ,并进行 rename 之后,一般就可以直接使用了。

. label var medsure_dum "是否购买医保"
. label var medsure_xnh "是否购买新农合"
. label var medsure_num "购买医保数量"

. rename (provcd18 countyid18 cid18 urban18 familysize18) ///
		(provcd countyid cid urban familysize)
		
. save "$temp_data/person2family2018.dta", replace

如果以家庭库作为 master 的话,则情况相对复杂,因为它属于一对多的关系,一个家庭中有多位家庭成员。此时我们一般选择户主作为家庭库与个人库的连接。户主的选取则参考 CFPS 的建议,将家庭的财务管理者认定为户主。关于户主的选取,大家可以详细阅读这篇文章:CFPS数据中有没有“户主”信息?|CFPS小课堂·家庭篇

我们将家庭库中财务回答人的 resp1pid 改为 pid ,然后再进行 merge 。具体代码如下:

.         //家庭库merge个人库
. use "$temp_data/family_2018.dta", clear

. rename resp1pid pid

. merge 1:1 fid18 pid using "$temp_data/person_2018.dta", ///
        keepusing(gender-medsure_num) keep(1 3) nogen


    Result                           # of obs.
    -----------------------------------------
    not matched                           715
        from master                       715  
        from using                          0  

    matched                            13,503  
    -----------------------------------------

此时有 715个 家庭的户主信息没有匹配上。同样的,我们给部分变量加上 label ,修改变量名,存储到 Temp_data 文件夹里。

. label var medsure_dum "是否购买医保"
. label var medsure_xnh "是否购买新农合"
. label var medsure_num "购买医保数量"

. rename (provcd18 countyid18 cid18 urban18 familysize18) ///
		(provcd countyid cid urban familysize)
		
. save "$temp_data/family2person2018.dta", replace

4. 数据核查

将数据整理出来之后,我们还可以进一步对数据进行核查。例如查看这个样本的变量缺失情况,我们可以引入 egen 里面的 rowmiss 函数:

*- 数据核查
. use "$temp_data/family2person2018.dta", clear

	//查看变量缺失情况
. egen miss = rowmiss(urban fincome1_per total_asset familysize agri finp gender age)

. tab miss

       miss |      Freq.     Percent        Cum.
------------+-----------------------------------
          0 |     12,600       88.62       88.62
          1 |        875        6.15       94.77
          2 |        613        4.31       99.09
          3 |        121        0.85       99.94
          4 |          9        0.06      100.00
------------+-----------------------------------
      Total |     14,218      100.00

可以发现, 12600个 样本有上述八个变量的完整数据,占总样本的 88.62% ,四个变量有缺失数据的样本数为9,占总样本的 0.06% 。如果我们需要剔除变量缺失的样本,输入 keep if miss == 0 即可。

另外,我们观察数据发现,户主的年龄范围为 [11, 95] , 11 岁的户主显然说不过去,我们可以保留 16-85 岁的户主样本,这一步可以用 inrange 来实现:

	//保留16-85岁的样本
. keep if inrange(age, 16, 85)

. save "$working_data/result_cfps2018.dta", replace

截面数据整理到这一步,已经基本算是告一段落了,我们将其存储到 Working_data 文件夹,方便后续进行回归分析。

5. 跨年合并

但是,有时候截面数据并不能满足我们的研究需求,还需要进一步构造面板数据,甚至是多期面板数据。下面以 CFPS2016-2018 的家庭库为例,构造一个两期的面板数据。

5.1 构建非平衡面板

我们先导入数据,用 keep 提取需要的变量,将各种原因的缺失值统一赋值为.,在对变量进行重新赋值,操作与 CFPS2018 家庭库的处理方法一致。跨年合并需要注意一点,那就是保持变量名,以及变量值所代表的含义在各年之间保持不变。所以我们需要对部分变量 rename ,并生成一个年份变量。处理代码如下所示:

*- 跨年合并
. use "$cfps2016/cfps2016famecon_201807.dta", clear

. keep fid16 provcd16 countyid16 cid16 urban16 resp1pid fk1l ft200 ///
		fincome1_per total_asset familysize16

	//缺失值替换为.
. for var _all: replace X =. if inlist(X, -10, -9, -8, -2, -1)

	//重新赋值
. recode fk1l  (1 = 1 "是")(5 = 0 "否"), gen(agri)
. recode ft200 (1 = 1 "是")(5 = 0 "否"), gen(finp)

	//存储家庭变量
. drop fk1l ft200
. rename (provcd18 countyid18 cid18 urban18 familysize18) ///
		(provcd countyid cid urban familysize)
. gen year = 2016
. save "$temp_data/family_2016.dta", replace 

我们导入 CFPS2018 的数据,同样进行 rename 和生成年份变量(实际上这一步在清洗 CFPS2018 时即可完成):

	//CFPS2018数据
. use "$temp_data/family_2018.dta", clear

. rename (provcd18 countyid18 cid18 urban18 familysize18) ///
		(provcd countyid cid urban familysize)
. gen year = 2018
. save "$temp_data/family_2018.dta", replace

然后我们对两份数据进行合并,并用 xtset 设定面板数据:

.         //合并数据
. use "$temp_data/family_2016.dta", clear
. append using "$temp_data/family_2018.dta"
. drop fid18
. order fid16 year

. xtset fid16 year
repeated time values within panel

结果发现数据报错了,出现了 repeated time values ,也就是说有时间值是重复的。这主要是因为,我们以 CFPS2016 作为基期进行 append,而在 2016-2018 两年间,有小部分家庭分家了。这部分家庭 fid16 是一样的, fid18 却不一致(实际上,以 CFPS2018 作为基期进行 append ,也会出现相似的问题)。我们需要删除分家的样本,仅保留两家/多家中的一家,或者两家/多家都删除。

我们可以先使用 duplicates tag ,标记出分家的样本。可以看到,两年间,有 989 个样本分成了两家, 89 个样本分成了三家, 7 个样本分成了四家。

	//标记分家样本
. duplicates tag fid16 year, gen(num)

. tab num

        num |      Freq.     Percent        Cum.
------------+-----------------------------------
          0 |     25,964       91.95       91.95
          1 |      1,978        7.00       98.96
          2 |        267        0.95       99.90
          3 |         28        0.10      100.00
------------+-----------------------------------
      Total |     28,237      100.00

我们即可以将分家的样本全部剔除,也可以仅保留分家中的一家,两种方法最终都可以组合成一个非平衡面板(推荐第一种方法,因为分家之后家庭可能发生了系统性变化,已不适合作为分析样本):

	//删除全部分家样本
. keep if num == 0

. xtset fid16 year
       panel variable:  fid16 (unbalanced)
        time variable:  year, 2016 to 2018, but with gaps
                delta:  1 unit
                
	//保留分家中的一家样本
. duplicates drop fid16 year, force

. xtset fid16 year
       panel variable:  fid16 (unbalanced)
        time variable:  year, 2016 to 2018, but with gaps
                delta:  1 unit

5.2 构建平衡面板

出于一些研究需求,有时候我们需要构建一份平衡面板。这时候我们可以借助 egen 中的 count 函数,计算出 fid16 出现的次数,并保留出现了两次的样本(说明该家庭在 CFPS2016 和 2018 中皆有出现)。可以发现,有 84.05 的家庭在两次调查中都存在。此时, xtset 之后就是平衡面板了。

	//构建平衡面板
. bys fid16: egen num = count(fid16)
. tab num

        num |      Freq.     Percent        Cum.
------------+-----------------------------------
          1 |      4,140       15.95       15.95
          2 |     21,824       84.05      100.00
------------+-----------------------------------
      Total |     25,964      100.00
      
. keep if num == 2
(4,140 observations deleted)
. drop num

. xtset fid16 year
       panel variable:  fid16 (strongly balanced)
        time variable:  year, 2016 to 2018, but with gaps
                delta:  1 unit

5.3 计算社区均值

很多文献都喜欢使用某个变量的社区/区县均值作为工具变量,因为人作为社会化的动物,必然会受到周围环境的影响,但是单独的个体却很难影响到环境。我们以是否持有金融产品为例,也计算一个“除自身以外,社区内持有金融产品的比例”。

我们先使用 bys+egen+count 计算出该社区的总人数 cidnum ,基于同样的逻辑,计算出该社区持有金融产品的人数 finpnum ,随后将持有金融产品人数减去自身,社区人数减去 1 ,两者之商即是社区均值(如果使用的是截面数据,去掉 bys 后面的 year 即可)。

	//构造社区均值
. bys cid year: egen cidnum = count(cid)
. bys cid year: egen finpnum = sum(finp)
. gen average = (finpnum - finp) / (cidnum -1)

5.4 构造处理组与对照组

面板数据的一大优势的可以做双重差分 (DID) ,而 DID 则必然会有处理组和对照组,那么我们如何在微观数据中进行分组呢?我们继续以金融产品持有为例,假如我们想研究金融产品持有对 Y 的影响。此时,可以将 2016 年不持有、 2018 持有金融产品的样本作为处理组,两年间皆持有或皆不持有的样本作为对照组。

在具体的实现上,我们可以先对 finp 进行差分,即下一期减去当期,并用差分值对缺失值进行填补。由此,我们就可以知道每个样本持有金融产品的变化情况了。

可以发现, 235 个样本退出了金融市场, 10371 个样本的金融产品持有状况保持不变, 270个 样本则进入了金融市场,进入金融市场的样本就是我们的处理组。

	//构造对照组与处理组
. xtset fid16 year
. bys fid16: gen treat = finp[_n+1] - finp[_n]

. bys fid16: replace treat = treat[_n-1] if mi(treat)
. tab treat

      treat |      Freq.     Percent        Cum.
------------+-----------------------------------
         -1 |        470        2.16        2.16
          0 |     20,742       95.36       97.52
          1 |        540        2.48      100.00
------------+-----------------------------------
      Total |     21,752      100.00

我们可以删除退出金融市场的样本,并生成一个时间虚拟变量 post ,此时, DID 的两个基本变量就已经完成了。

. drop if treat == -1
. gen post = (year == 2018)
. save "$working_data/panel2016_2018.dta", replace

6. 结语

下面我们来进行一个简单总结。在处理 CFPS 等微观数据时,我们可以先从单个表入手,提取出需要的变量,进行简单清洗。随后使用 merge 命令进行跨表合并,整理成截面数据。如果想进一步清洗面板数据的话,则需要将各年数据先整合成截面,然后再使用 append 进行跨年合并。

当然,以上都只是一家之言,笔者也只是给大家提供一种数据清洗的思路而已。

最后附上本文的全部代码,供大家参考:

* ======================================================
* Program:CFPS数据清洗
* Revised:2021-04-25
* ======================================================

clear all
clear matrix
set more off

global root         = "/Applications/Stata/personal/CFPS"
global cfps2018     = "$root/CFPS2018"
global cfps2016     = "$root/CFPS2016"
global dofiles      = "$root/Result_data/Dofiles"
global logfiles     = "$root/Result_data/Logfiles"
global working_data = "$root/Result_data/Working_data"
global temp_data    = "$root/Result_data/Temp_data"
log using "$logfiles/cfpsclean.log", replace

*- 提取家庭库变量
use "$cfps2018/cfps2018famecon_202101.dta", clear
keep fid18 fid16 provcd18 countyid18 cid18 urban18 resp1pid fk1l ft200 ///
	fincome1_per total_asset familysize18

	//查看变量标签
label list urban18

	//缺失值替换为.
for var _all: replace X =. if inlist(X, -10, -9, -8, -2, -1)

	//重新赋值
recode fk1l  (1 = 1 "是")(5 = 0 "否"), gen(agri)
recode ft200 (1 = 1 "是")(5 = 0 "否"), gen(finp)

	//存储家庭变量
drop fk1l ft200
save "$temp_data/family_2018.dta", replace 

*- 提取个人库变量
use "$cfps2018/cfps2018person_202012.dta", clear
keep pid fid18 fid16 provcd18 countyid18 cid18 urban18 gender age ///
	qa301 qea0 qp605_s_* cfps2018edu 

for var _all: replace X =. if inlist(X, -10, -9, -8, -2, -1)
tab qa301
tab qea0

	//重新赋值
recode qa301 (1 = 1 "农业户口")(3 = 0 "非农户口")(5 79 =.), gen(hukou)
recode qea0  (2 3 = 1 "有配偶")(1 4 5 = 0 "无配偶"), gen(spouse)
recode cfps2018edu (1 = 0 "文盲/半文盲")(2 = 1 "小学")(3 = 2 "初中")(4 = 3 "高中") ///
	(5 6 7 8 = 4 "大学以以上"), gen(edu)

	//计算是否有医保及医保数量
for var qp605_s_*: replace X =. if X == 78
gen medsure_dum = 0
gen medsure_xnh = 0
gen medsure_num = 0
for var qp605_s_*: replace medsure_dum = 1 if X !=.
for var qp605_s_*: replace medsure_xnh = 1 if X == 5
for var qp605_s_*: replace medsure_num = medsure_num + 1 if X !=.

drop qa301 qea0 qp605_s_* cfps2018edu
save "$temp_data/person_2018.dta", replace

*- 跨表合并
	//个人库merge家庭库
use "$temp_data/person_2018.dta", clear
merge m:1 fid18 using "$temp_data/family_2018.dta", ///
	keepusing(fincome1_per total_asset familysize18 agri finp) keep(1 3) nogen

label var medsure_dum "是否购买医保"
label var medsure_xnh "是否购买新农合"
label var medsure_num "购买医保数量"
rename (provcd18 countyid18 cid18 urban18 familysize18) ///
	(provcd countyid cid urban familysize)
save "$temp_data/person2family2018.dta", replace

	//家庭库merge个人库
use "$temp_data/family_2018.dta", clear
rename resp1pid pid

merge 1:1 fid18 pid using "$temp_data/person_2018.dta", ///
	keepusing(gender-medsure_num) keep(1 3) nogen

label var medsure_dum "是否购买医保"
label var medsure_xnh "是否购买新农合"
label var medsure_num "购买医保数量"
rename (provcd18 countyid18 cid18 urban18 familysize18) ///
	(provcd countyid cid urban familysize)
save "$temp_data/family2person2018.dta", replace

*- 数据核查
use "$temp_data/family2person2018.dta", clear

	//查看变量缺失情况
egen miss = rowmiss(urban fincome1_per total_asset familysize agri finp gender age)
tab miss
keep if miss == 0

	//保留16-85岁的样本
keep if inrange(age, 16, 85)
save "$working_data/result_cfps2018.dta", replace

*- 跨年合并
use "$cfps2016/cfps2016famecon_201807.dta", clear
keep fid16 provcd16 countyid16 cid16 urban16 resp1pid fk1l ft200 ///
	fincome1_per total_asset familysize16

	//缺失值替换为.
for var _all: replace X =. if inlist(X, -10, -9, -8, -2, -1)

	//重新赋值
recode fk1l  (1 = 1 "是")(5 = 0 "否"), gen(agri)
recode ft200 (1 = 1 "是")(5 = 0 "否"), gen(finp)

	//存储家庭变量
drop fk1l ft200
rename (provcd16 countyid16 cid16 urban16 familysize16) ///
	(provcd countyid cid urban familysize)
gen year = 2016
save "$temp_data/family_2016.dta", replace 

	//CFPS2018数据
use "$temp_data/family_2018.dta", clear
rename (provcd18 countyid18 cid18 urban18 familysize18) ///
	(provcd countyid cid urban familysize)
gen year = 2018
save "$temp_data/family_2018.dta", replace

	//合并数据
use "$temp_data/family_2016.dta", clear
append using "$temp_data/family_2018.dta"
drop fid18
order fid16 year
*xtset fid16 year

	//标记分家样本
duplicates tag fid16 year, gen(num)
tab num

	//删除全部分家样本
keep if num == 0
xtset fid16 year
drop num

	//保留分家中的一家样本
duplicates drop fid16 year, force
xtset fid16 year

	//构建平衡面板
bys fid16: egen num = count(fid16)
tab num
keep if num == 2
drop num
xtset fid16 year

	//构造社区均值
bys cid year: egen cidnum = count(cid)
bys cid year: egen finpnum = sum(finp)
gen average = (finpnum - finp) / (cidnum -1)

	//构造对照组与处理组
xtset fid16 year
bys fid16: gen treat = finp[_n+1] - finp[_n]
bys fid16: replace treat = treat[_n-1] if mi(treat)
tab treat

drop if treat == -1
gen post = (year == 2018)
save "$working_data/panel2016_2018.dta", replace

7. 相关推文

Note:产生如下推文列表的 Stata 命令为:
lianxh 普林斯顿 数据处理 reshape 合并
安装最新版 lianxh 命令:
ssc install lianxh, replace

相关课程

免费公开课

最新课程-直播课

专题 嘉宾 直播/回看视频
最新专题 文本分析、机器学习、效率专题、生存分析等
研究设计 连玉君 我的特斯拉-实证研究设计-幻灯片-
面板模型 连玉君 动态面板模型-幻灯片-
面板模型 连玉君 直击面板数据模型 [免费公开课,2小时]
  • Note: 部分课程的资料,PPT 等可以前往 连享会-直播课 主页查看,下载。

课程主页

课程主页

关于我们

  • Stata连享会 由中山大学连玉君老师团队创办,定期分享实证分析经验。
  • 连享会-主页知乎专栏,400+ 推文,实证分析不再抓狂。直播间 有很多视频课程,可以随时观看。
  • 公众号关键词搜索/回复 功能已经上线。大家可以在公众号左下角点击键盘图标,输入简要关键词,以便快速呈现历史推文,获取工具软件和数据下载。常见关键词:课程, 直播, 视频, 客服, 模型设定, 研究设计, stata, plus, 绘图, 编程, 面板, 论文重现, 可视化, RDD, DID, PSM, 合成控制法

连享会小程序:扫一扫,看推文,看视频……

扫码加入连享会微信群,提问交流更方便

✏ 连享会-常见问题解答:
https://gitee.com/lianxh/Course/wikis

New! lianxh 命令发布了:
随时搜索连享会推文、Stata 资源,安装命令如下:
. ssc install lianxh
使用详情参见帮助文件 (有惊喜):
. help lianxh