Stata 数据管理(二):长宽数据转换、数据合并及其它数据管理操作(附动图演示)
为了让大家更好的理解本文的内容,欢迎各位培训班会员参加明晚 8 点的直播课 「Stata 数据管理(二):长宽数据转换、数据合并及其它数据管理操作」
继续上次课的内容,本次课我们将讲解长宽数据转换、数据合并等操作的命令。
长宽数据转换
通常我们会遇到两种类似的数据,宽型数据(宽面板):
use nestates, clear
keep if inlist(year, 1990, 1995, 2000)
drop dpi*
keep year popCT popMA popRI
order year
xpose, clear
drop in 1
generate str state = "CT" in 1
replace state = "MA" in 2
replace state = "RI" in 3
rename v1 pop1990
rename v2 pop1995
rename v3 pop2000
order state
list, noobs
*> +-------------------------------------+
*> | state pop1990 pop1995 pop2000 |
*> |-------------------------------------|
*> | CT 3291967 3324144 3411750 |
*> | MA 6022639 6141445 6362076 |
*> | RI 1005995 1017002 1050664 |
*> +-------------------------------------+
使用 reshape long 可以非常轻松的将这个数据转换成长型数据(长面板):
reshape long pop, i(state) j(year)
list, noobs
*> +------------------------+
*> | state year pop |
*> |------------------------|
*> | CT 1990 3291967 |
*> | CT 1995 3324144 |
*> | CT 2000 3411750 |
*> | MA 1990 6022639 |
*> | MA 1995 6141445 |
*> |------------------------|
*> | MA 2000 6362076 |
*> | RI 1990 1005995 |
*> | RI 1995 1017002 |
*> | RI 2000 1050664 |
*> +------------------------+
长数据可以直接设置成面板数据:
encode state, gen(statenum)
xtset statenum year
*> panel variable: statenum (strongly balanced)
*> time variable: year, 1990 to 2000, but with gaps
*> delta: 1 unit
反过来,使用 reshape wide 可以把这个数据转换成宽型的:
reshape wide pop, i(state) j(year)
list, noobs
*> +------------------------------------------------+
*> | state pop1990 pop1995 pop2000 statenum |
*> |------------------------------------------------|
*> | CT 3291967 3324144 3411750 CT |
*> | MA 6022639 6141445 6362076 MA |
*> | RI 1005995 1017002 1050664 RI |
*> +------------------------------------------------+
宽数据更方便绘图和跨年份运算。
对于这样的一个案例实际上我们使用 tidy 包中的 gather 和 spread 更方便:
*- ssc install tidy
*- 宽变长
gather pop*
ren variable year
replace year = subinstr(year, "pop", "", .)
destring year, replace
ren value pop
list, noobs
*> +-----------------------------------+
*> | state statenum year pop |
*> |-----------------------------------|
*> | CT CT 1990 3291967 |
*> | CT CT 1995 3324144 |
*> | CT CT 2000 3411750 |
*> | MA MA 1990 6022639 |
*> | MA MA 1995 6141445 |
*> |-----------------------------------|
*> | MA MA 2000 6362076 |
*> | RI RI 1990 1005995 |
*> | RI RI 1995 1017002 |
*> | RI RI 2000 1050664 |
*> +-----------------------------------+
*- 长变宽
spread year pop
list, noobs
*> +------------------------------------------------+
*> | state statenum pop1990 pop1995 pop2000 |
*> |------------------------------------------------|
*> | CT CT 3291967 3324144 3411750 |
*> | MA MA 6022639 6141445 6362076 |
*> | RI RI 1005995 1017002 1050664 |
*> +------------------------------------------------+
我们再看一个复杂一些的案例:
use mathpnl_wide, clear
desc
*> Contains data from mathpnl_wide.dta
*> obs: 550 modified mathpnl.dta from Wooldridge (2000)
*> vars: 21 9 Oct 2015 12:49
*> size: 37,400
*> -----------------------------------------------------------------------------------------
*> storage display value
*> variable name type format label variable label
*> -----------------------------------------------------------------------------------------
*> distid float %9.0g district identifier
*> expp1992 int %9.0g 1992 expp
*> revpp1992 int %9.0g 1992 revpp
*> avgsal1992 float %9.0g 1992 avgsal
*> math4score1992 float %9.0g 1992 math4score
*> math7score1992 float %9.0g 1992 math7score
*> expp1994 int %9.0g 1994 expp
*> revpp1994 int %9.0g 1994 revpp
*> avgsal1994 float %9.0g 1994 avgsal
*> math4score1994 float %9.0g 1994 math4score
*> math7score1994 float %9.0g 1994 math7score
*> expp1996 int %9.0g 1996 expp
*> revpp1996 int %9.0g 1996 revpp
*> avgsal1996 float %9.0g 1996 avgsal
*> math4score1996 float %9.0g 1996 math4score
*> math7score1996 float %9.0g 1996 math7score
*> expp1998 int %9.0g 1998 expp
*> revpp1998 int %9.0g 1998 revpp
*> avgsal1998 float %9.0g 1998 avgsal
*> math4score1998 float %9.0g 1998 math4score
*> math7score1998 float %9.0g 1998 math7score
*> -----------------------------------------------------------------------------------------
*> Sorted by: distid
如何把这个数据变成长数据呢?
reshape long expp revpp avgsal math4score math7score, i(distid) j(year)
desc
*> Contains data
*> obs: 2,200 modified mathpnl.dta from Wooldridge (2000)
*> vars: 7
*> size: 48,400
*> -----------------------------------------------------------------------------------------
*> storage display value
*> variable name type format label variable label
*> -----------------------------------------------------------------------------------------
*> distid float %9.0g district identifier
*> year int %9.0g
*> expp int %9.0g
*> revpp int %9.0g
*> avgsal float %9.0g
*> math4score float %9.0g
*> math7score float %9.0g
*> -----------------------------------------------------------------------------------------
*> Sorted by: distid year
*> Note: Dataset has changed since last saved.
那么我们这个时候能不能用 gather 和 spread 完成这个操作呢?
use mathpnl_wide, clear
gather expp1992 - math7score1998
gen year = ustrregexs(0) if ustrregexm(variable, "199\d")
replace variable = subinstr(variable, year, "", .)
destring year, replace
spread variable value
compress
结果和上面的 reshape long 一样。灵活的搭配 spread 和 gather 可以完成 reshape 的全部功能,虽然看起来代码更复杂,但是实际 gather 和 spread 更容易理解。
类似,reshape wide 可以把这个数据恢复成宽数据:
reshape wide expp revpp avgsal math4score math7score, i(distid) j(year)
使用 gather 和 spread 可以这样:
*- 准备数据
use mathpnl_wide, clear
reshape long expp revpp avgsal math4score math7score, i(distid) j(year)
*- 开始使用 spread 和 gather
gather expp - math7score
replace variable = variable + string(year)
drop year
spread variable value
实际使用中我们还有可能遇到这样的情况:
*- 准备数据
use mathpnl_wide, clear
keep distid expp*
keep in 1/20
foreach i of varlist _all {
ren `i' `i'mm
}
list
*> +------------------------------------------------------+
*> | distidmm expp~2mm expp~4mm expp~6mm expp~8mm |
*> |------------------------------------------------------|
*> 1. | 1010 4227 5214 6155 6476 |
*> 2. | 2010 3445 3583 8632 9424 |
*> 3. | 2020 6379 7412 9102 10539 |
*> 4. | 2070 3631 3776 4659 5112 |
*> 5. | 2080 3992 5267 6224 6344 |
*> |------------------------------------------------------|
*> 6. | 3000 3469 4081 5162 5626 |
*> 7. | 3010 3754 4460 5532 5577 |
*> 8. | 3020 3398 3954 4777 5305 |
*> 9. | 3030 3497 3893 5041 5580 |
*> 10. | 3040 3679 4483 5122 5681 |
*> |------------------------------------------------------|
*> 11. | 3050 4068 4625 5371 6076 |
*> 12. | 3060 3753 4417 5354 5913 |
*> 13. | 3070 3430 3807 5082 5568 |
*> 14. | 3080 5856 5964 6841 7209 |
*> 15. | 3100 4121 3966 4803 5045 |
*> |------------------------------------------------------|
*> 16. | 3440 2170 3006 6902 7657 |
*> 17. | 4000 3705 4543 3515 3887 |
*> 18. | 4010 3567 4115 5118 5630 |
*> 19. | 5010 4340 5267 6027 7035 |
*> 20. | 5035 4311 5270 6171 6225 |
*> +------------------------------------------------------+
也就是说我们需要的年份在变量名称的中间,这个时候我们可以这样:
reshape long expp@mm, i(distidmm) j(year)
使用 @ 表示 year 的位置即可。
为了便于大家理解 gather 和 spread,我做了个动图:
数据合并
append:数据拼接
例如这四个数据:
mathpnl1992.dta mathpnl1994.dta mathpnl1996.dta mathpnl1998.dta
这四个数据集都有相同的变量,我们可以使用 append 命令将他们像堆积木一样堆起来:
use mathpnl1992, clear
append using mathpnl1994
append using mathpnl1996
append using mathpnl1998
每个数据集有 550 个观测值,堆叠起来后的数据集有 2200 个观测值,正好。
为了便于大家理解 append 命令的功能,我做了个动图:
使用 merge 命令进行数据匹配
如果两个数据集都有某个变量,我们就可以使用这个变量连接两个数据集成一个数据集,例如我们先使用 cntrade 命令下载平安银行和万科的股票交易数据:
cntrade 最近挂了,大家可以直接用附件中的 000001.dta 和 000002.dta。
*- cntrade 1
*- keep date clsprc
*- ren clsprc 平安银行
*- save 000001, replace
*- cntrade 2
*- keep date clsprc
*- ren clsprc 万科A
*- save 000002, replace
这样我们就得到了两个数据集:000001.dta 和 000002.dta,两个数据集都有 date 变量,并且 date 变量都是没有重复值的变量,所以我们可以使用 merge 1:1 就行匹配合并:
use 000001, clear
merge 1:1 date using 000002
list in 1/20
*> +-------------------------------------------------+
*> | date 平安银行 万科A _merge |
*> |-------------------------------------------------|
*> 1. | 1991-01-02 67.41 15.63 matched (3) |
*> 2. | 1991-01-03 66.4 15.39 matched (3) |
*> 3. | 1991-01-04 66.07 15.31 matched (3) |
*> 4. | 1991-01-07 66.42 15.47 matched (3) |
*> 5. | 1991-01-09 65.76 15.31 matched (3) |
*> |-------------------------------------------------|
*> 6. | 1991-01-10 65.43 15.23 matched (3) |
*> 7. | 1991-01-11 65.1 15.15 matched (3) |
*> 8. | 1991-01-14 64.45 14.99 matched (3) |
*> 9. | 1991-01-15 64.13 14.92 matched (3) |
*> 10. | 1991-01-16 63.81 14.85 matched (3) |
*> |-------------------------------------------------|
*> 11. | 1991-01-17 63.49 14.78 matched (3) |
*> 12. | 1991-01-18 63.2 14.71 matched (3) |
*> 13. | 1991-01-21 62.54 . master only (1) |
*> 14. | 1991-01-22 62.23 . master only (1) |
*> 15. | 1991-01-23 61.92 . master only (1) |
*> |-------------------------------------------------|
*> 16. | 1991-01-24 61.62 . master only (1) |
*> 17. | 1991-01-25 61.3 . master only (1) |
*> 18. | 1991-01-29 60.39 14.58 matched (3) |
*> 19. | 1991-01-30 60.09 14.51 matched (3) |
*> 20. | 1991-01-31 60.39 . master only (1) |
*> +-------------------------------------------------+
合并后的数据集里面还有一个 _merge 变量,这个变量的作用是声明每个观测值是来源于哪个数据集,这个的 000001.dta 叫 master 数据,000002.dta 叫 using 数据。
除了 merge 1:1 还有 merge m:1 和 merge 1:m,下面的动图可以帮助你理解什么时候该用哪个:
其它的数据管理命令
fillin
fillin 可以用于补充缺失的组合,例如:
webuse fillin1, clear
list
*> +------------------------------------------+
*> | sex race age_gr~p x1 x2 |
*> |------------------------------------------|
*> 1. | female white 20-24 20393 14.5 |
*> 2. | male white 25-29 32750 12.7 |
*> 3. | female black 30-34 39399 14.2 |
*> +------------------------------------------+
fillin sex race age_group
list
*> +----------------------------------------------------+
*> | sex race age_gr~p x1 x2 _fillin |
*> |----------------------------------------------------|
*> 1. | female white 20-24 20393 14.5 0 |
*> 2. | female white 25-29 . . 1 |
*> 3. | female white 30-34 . . 1 |
*> 4. | female black 20-24 . . 1 |
*> 5. | female black 25-29 . . 1 |
*> |----------------------------------------------------|
*> 6. | female black 30-34 39399 14.2 0 |
*> 7. | male white 20-24 . . 1 |
*> 8. | male white 25-29 32750 12.7 0 |
*> 9. | male white 30-34 . . 1 |
*> 10. | male black 20-24 . . 1 |
*> |----------------------------------------------------|
*> 11. | male black 25-29 . . 1 |
*> 12. | male black 30-34 . . 1 |
*> +----------------------------------------------------+
cross
cross 可以创建两个数据集的交叉数据集,这个交叉数据集的每个观测值都是原来的两个数据集的观测值的组合。
clear all
*- 创建 sex 数据集
input str6 sex str6 height
"male" 180
"female" 181
"end" 182
end
save sex, replace
clear all
input agecat
20
30
40
end
*- 创建交叉数据集
cross using sex
list
*> +--------------------------+
*> | agecat sex height |
*> |--------------------------|
*> 1. | 20 male 180 |
*> 2. | 30 male 180 |
*> 3. | 40 male 180 |
*> 4. | 20 female 181 |
*> 5. | 20 end 182 |
*> |--------------------------|
*> 6. | 30 female 181 |
*> 7. | 30 end 182 |
*> 8. | 40 female 181 |
*> 9. | 40 end 182 |
*> +--------------------------+
stack
stack 命令可以堆叠变量。
webuse stackxmpl, clear
list
*> +---------------+
*> | a b c d |
*> |---------------|
*> 1. | 1 2 3 4 |
*> 2. | 5 6 7 8 |
*> +---------------+
stack a b c d, into(e f) clear
list
*> +----------------+
*> | _stack e f |
*> |----------------|
*> 1. | 1 1 2 |
*> 2. | 1 5 6 |
*> 3. | 2 3 4 |
*> 4. | 2 7 8 |
*> +----------------+
这个操作可能在绘图的时候非常有用,例如:
use mathpnl_wide, clear
stack expp1992 avgsal1992 expp1998 avgsal1998, into(expp sal) wide clear
tw sc expp1992 expp1998 sal, m(oh x) ///
title("School District Expenditures vs. Average Salaries") ///
xtitle("Average salaries") ytitle("Expenditures") ///
legend(position(5) ring(0) label(1 "1992") label(2 "1998"))
separate
separate 可以将某个变量按照分组变量分成多个变量:
use mathpnl_long, clear
separate expp, by(year) veryshortlabel
summarize expp*
*> Variable | Obs Mean Std. Dev. Min Max
*> -------------+---------------------------------------------------------
*> expp | 2,200 5205.833 1255.723 946 13982
*> expp1992 | 550 4181.165 933.6362 946 9041
*> expp1994 | 550 4752.56 993.0163 1147 10461
*> expp1996 | 550 5737.953 981.382 2729 10449
*> expp1998 | 550 6151.653 1028.371 3811 13982
joinby
joinby 可以把两个数据集按照指定变量进行相互组合:
webuse child, clear
save child, replace
webuse parent, clear
joinby family_id using child
xpose 与 sxpose
xpose 命令可以用于数值型变量的转置:
webuse xposexmpl, clear
xpose, clear varname
sxpose 命令可以用于字符型变量的转置:
webuse xposexmpl, clear
tostring _all, replace force
sxpose, clear
这次的内容就到这里啦!
直播信息
为了让大家更好的理解上面的内容,欢迎各位培训班会员参加明晚 8 点的直播课 「Stata 数据管理(二):长宽数据转换、数据合并及其它数据管理操作」
直播地址:腾讯会议(需要报名 RStata 培训班参加) 讲义材料:需要报名 RStata 培训班,详情可阅读:一起来学习 R 语言和 Stata 啦!学习过程中遇到的问题也可以随时提问!
更多关于 RStata 会员的更多信息可添加微信号 r_stata 咨询:
附件下载(点击文末的阅读原文即可跳转):https://rstata.duanshu.com/#/brief/course/57571646805e4786b622867cfd5ad1d9