查看原文
其他

Stata 数据管理(二):长宽数据转换、数据合并及其它数据管理操作(附动图演示)

RStata RStata 2023-10-24

为了让大家更好的理解本文的内容,欢迎各位培训班会员参加明晚 8 点的直播课 「Stata 数据管理(二):长宽数据转换、数据合并及其它数据管理操作」


继续上次课的内容,本次课我们将讲解长宽数据转换、数据合并等操作的命令。

长宽数据转换

通常我们会遇到两种类似的数据,宽型数据(宽面板):

use nestates, clear
keep if inlist(year, 1990, 1995, 2000)
drop dpi*
keep year popCT popMA popRI
order year
xposeclear
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 
xposeclear varname

sxpose 命令可以用于字符型变量的转置:

webuse xposexmpl, clear
tostring _all, replace force 
sxpose, clear

这次的内容就到这里啦!

直播信息

为了让大家更好的理解上面的内容,欢迎各位培训班会员参加明晚 8 点的直播课 「Stata 数据管理(二):长宽数据转换、数据合并及其它数据管理操作」

  1. 直播地址:腾讯会议(需要报名 RStata 培训班参加)
  2. 讲义材料:需要报名 RStata 培训班,详情可阅读:一起来学习 R 语言和 Stata 啦!学习过程中遇到的问题也可以随时提问!

更多关于 RStata 会员的更多信息可添加微信号 r_stata 咨询:

附件下载(点击文末的阅读原文即可跳转):https://rstata.duanshu.com/#/brief/course/57571646805e4786b622867cfd5ad1d9


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存