查看原文
其他

Stata:用 mapch 命令绘制“事件链”

Stata连享会 Stata连享会 2020-02-10

作者:高娜娜 (中南财经政法大学)

Stata 连享会:知乎 | 简书 | 码云

python 爬虫与文本分析专题-现场班


连享会-Python爬虫与文本分析现场班-山西大学 2019.5.17-19

一起学空间计量……

空间计量专题-西安 2019.6.27-30

特别说明

文中包含的链接在微信中无法生效。请点击本文底部左下角的【阅读原文】,转入本文【简书版】

参考文献:Vanlaar W. A shortcut through long loops: An illustration of two alternatives to looping over observations[J]. Stata Journal, 2008, 8(4): 540-553. [PDF]

1. 问题背景

在进行数据处理的时候,我们可能会遇到这样的问题:我们试图将两份数据合并在一起,但是用来合并的唯一可识别的变量却是动态变化的,导致无法依据该变量实现完全的合并。

例如,个人的驾驶执照号码发生过变化,在两份与驾驶执照号码有关的数据中,由于数据搜集的过程不同,其中一份数据使用旧的驾驶执照号码,如下图的 master data,第1、2、3 行代表着同一个人,但是有多个驾驶执照号码,每个驾驶执照号码对应一次交通事故。而另一份数据使用最新的驾驶执照号码,如下图的 using data,每个驾驶执照号码对应个人的性别和爱好。

image.png

为了成功合并数据,我们需要了解驾驶执照号码的变化过程( 如下图 sorting data ),并得到旧驾驶执照号码( 如下图 mapch data  中的 old 变量 )和最终驾驶执照号码( 如下图 mapch data 中的 recent 变量 )的对应关系。

驾驶执照管理部门能够提供驾驶执照号码的变动,即旧驾驶执照号码( 如下图 sorting data 中的 old 变量 )、更新的驾驶执照号码( 如下图 sorting data 中的 updated 变量 )、驾驶执照号码变动的时间( 如下图 sorting data 中的 date 变量 )。例如,在 sorting data 中,第  1、2 行是同一个人两次更改驾驶执照号码。

image.png

如果数据量比较少或者只有少量的驾驶执照号码发生了变化,我们可以手动寻找这些变化,但是如果数据量比较大且大部分人都改变过驾驶执照号码,并且许多人不止改变过一次,用眼寻找的方法将非常耗时。

上图中 sorting data 是我们已经整理好的驾驶执照号码变更过程,比如第 1、2、3 是同一个人驾驶执照号码变化的过程,即 “A-B-C-D”,而实际中,数据的不同行之间是杂乱的,如上图中 original data 所示。

因此,为了得到能够进行数据合并的 mapch data,首先需要对 original data 进行排序,识别每个驾驶员驾驶执照号码的更改过程,即绘制  “事件链” ( mapping the chains of events ),然后需要创建一个新变量 recentrecent 的值为每个 “事件链” 的最终驾驶执照号码。

当我们得到 mapch data 后,便可以根据旧驾驶执照号码( old )将 mapch data 和 master data 合并在一起,然后根据更新的驾驶执照号码( recent )将 master data 和 using data 合并在一起。

在 Stata 中,我们可以通过数据追加、识别和合并一步一步得到最终的 “事件链”,也可以用 mapch 命令直接得到 “事件链” 的信息。下面先用分步的方法识别 “事件链”,再用  mapch 命令实现 “事件链”。

2.分步实现

第一步:构造数据

在 original data 中创建变量 link 并等于 updated 变量,保存此数据;然后在 original data 中创建变量 link 并等于 old 变量;将两份数据追加在一起。根据 linkdate 排序后的结果如下所示。

  1. use original,clear

  2. generate link = updated

  3. save original1, replace


  4. use original,clear

  5. generate link = old

  6. append using original1

  7. sort link date

  8. list

  1. +---------------------------------+

  2. | old updated date link |

  3. |---------------------------------|

  4. 1. | A B 2017/3/2 A |

  5. 2. | A B 2017/3/2 B |

  6. 3. | B C 2018/3/2 B |

  7. 4. | B C 2018/3/2 C |

  8. 5. | C D 2019/3/2 C |

  9. |---------------------------------|

  10. 6. | C D 2019/3/2 D |

  11. 7. | E F 2018/3/2 E |

  12. 8. | E F 2018/3/2 F |

  13. 9. | G H 2017/3/2 G |

  14. 10. | G H 2017/3/2 H |

  15. |---------------------------------|

  16. 11. | O N 2015/3/2 N |

  17. 12. | P O 2014/3/2 O |

  18. 13. | O N 2015/3/2 O |

  19. 14. | Q P 2013/3/2 P |

  20. 15. | P O 2014/3/2 P |

  21. |---------------------------------|

  22. 16. | Q P 2013/3/2 Q |

  23. 17. | X1 X2 2011/3/2 X1 |

  24. 18. | X1 X2 2011/3/2 X2 |

  25. 19. | X2 X3 2012/3/2 X2 |

  26. 20. | X2 X3 2012/3/2 X3 |

  27. +---------------------------------+

第二步:识别 two-step 链

之所以不从识别 one-step 链开始,是因为 original data 本身已经包含了 one-step 链的信息,我们可以通过合并的方式将 original data 中的 one-step 链的信息复制下来。

通过第一步,新构造的数据中增加了两种噪音,即无效的行( 如第 1、16 行 )和 one-step 链的行( 如第 8、10 行 ),需要清理掉这些不需要的数据。

我们根据 link 产生变量 test1test1 的值是 link 中每个值的个数( 如 “B” 在 link 中出现了 2 次 )。如果 test1 的值不等于2,那么该行或者是 one-step 链,如 link 第 8 行是 “E-F” 的 one-step 链;或者该行对应的 link 值是整条 “事件链” 的最开始的状态,如 link 第 1 行的 A 是 “A-B-C-D” 的开始状态。

  1. bysort link: generate test1=_N

  2. list

  1. +-----------------------------------------+

  2. | old updated date link test1 |

  3. |-----------------------------------------|

  4. 1. | A B 2017/3/2 A 1 |

  5. 2. | A B 2017/3/2 B 2 |

  6. 3. | B C 2018/3/2 B 2 |

  7. 4. | B C 2018/3/2 C 2 |

  8. 5. | C D 2019/3/2 C 2 |

  9. |-----------------------------------------|

  10. 6. | C D 2019/3/2 D 1 |

  11. 7. | E F 2018/3/2 E 1 |

  12. 8. | E F 2018/3/2 F 1 |

  13. 9. | G H 2017/3/2 G 1 |

  14. 10. | G H 2017/3/2 H 1 |

  15. |-----------------------------------------|

  16. 11. | O N 2015/3/2 N 1 |

  17. 12. | P O 2014/3/2 O 2 |

  18. 13. | O N 2015/3/2 O 2 |

  19. 14. | Q P 2013/3/2 P 2 |

  20. 15. | P O 2014/3/2 P 2 |

  21. |-----------------------------------------|

  22. 16. | Q P 2013/3/2 Q 1 |

  23. 17. | X1 X2 2011/3/2 X1 1 |

  24. 18. | X1 X2 2011/3/2 X2 2 |

  25. 19. | X2 X3 2012/3/2 X2 2 |

  26. 20. | X2 X3 2012/3/2 X3 1 |

  27. +-----------------------------------------+

  1. drop if test1!=2

  2. list

  1. +-----------------------------------------+

  2. | old updated date link test1 |

  3. |-----------------------------------------|

  4. 1. | A B 2017/3/2 B 2 |

  5. 2. | B C 2018/3/2 B 2 |

  6. 3. | B C 2018/3/2 C 2 |

  7. 4. | C D 2019/3/2 C 2 |

  8. 5. | P O 2014/3/2 O 2 |

  9. |-----------------------------------------|

  10. 6. | O N 2015/3/2 O 2 |

  11. 7. | Q P 2013/3/2 P 2 |

  12. 8. | P O 2014/3/2 P 2 |

  13. 9. | X1 X2 2011/3/2 X2 2 |

  14. 10. | X2 X3 2012/3/2 X2 2 |

  15. +-----------------------------------------+

现在,我们已经识别了至少包含两个链的 “事件链”,link 中的值均都出现了 2 次,接下来我们创建另一个包含 updated 信息的变量 recent

recent 变量的值等于每个 link 变量值对应的 updated 变量排序最大的那个值。例如,第1和2行的 link 值为 “B”,其对应的 updated 变量的第 1 和 2 行分别为 “B” 和 “C”,而行数较大的第2行的值为 “C”,因此 link 值为 “B” 时 recent 变量的值为 “C”。

  1. generate str2 recent=""

  2. bysort link: replace recent=updated[_N]

  3. list


  1. +--------------------------------------------------+

  2. | old updated date link test1 recent |

  3. |--------------------------------------------------|

  4. 1. | A B 2017/3/2 B 2 C |

  5. 2. | B C 2018/3/2 B 2 C |

  6. 3. | B C 2018/3/2 C 2 D |

  7. 4. | C D 2019/3/2 C 2 D |

  8. 5. | P O 2014/3/2 O 2 N |

  9. |--------------------------------------------------|

  10. 6. | O N 2015/3/2 O 2 N |

  11. 7. | Q P 2013/3/2 P 2 O |

  12. 8. | P O 2014/3/2 P 2 O |

  13. 9. | X1 X2 2011/3/2 X2 2 X3 |

  14. 10. | X2 X3 2012/3/2 X2 2 X3 |

  15. +--------------------------------------------------+

此时,我们已经完成 two-steps 链的识别。例如,从 oldlink 再到 recent,第1行识别出了 “A-B-C”,第9行识别出了 “X1-X2-X3”。

第三步:识别 three-step 链

在本文的例子中,要将 recent 的值替换为 “事件链” 的结束值,需重复使用识别 two-step 链的步骤。

在重复上述步骤之前,需要清除 two-step 链识别中产生的噪音。例如,上图中的第 2 行、第 8  行的 “事件链” 呈现的是 “B-B-C”、“P-P-O”,而我们需要的是 3 行和第 5 行的 “B-C-D”、“P-O-N”。为了标识出第 2、8 行,我们根据 old 变量产生变量 test2test2 的值是 old 不同值的个数,test2 的值大于1并且该行对应的 recent 的值和 updated 的值相等时,应该删除该行。

  1. bysort old: generate test3=_N

  2. list

  1. +----------------------------------------------------------+

  2. | old updated date link test1 recent test2 |

  3. |----------------------------------------------------------|

  4. 1. | A B 2017/3/2 B 2 C 1 |

  5. 2. | B C 2018/3/2 B 2 C 2 |

  6. 3. | B C 2018/3/2 C 2 D 2 |

  7. 4. | C D 2019/3/2 C 2 D 1 |

  8. 5. | O N 2015/3/2 O 2 N 1 |

  9. |----------------------------------------------------------|

  10. 6. | P O 2014/3/2 O 2 N 2 |

  11. 7. | P O 2014/3/2 P 2 O 2 |

  12. 8. | Q P 2013/3/2 P 2 O 1 |

  13. 9. | X1 X2 2011/3/2 X2 2 X3 1 |

  14. 10. | X2 X3 2012/3/2 X2 2 X3 1 |

  15. +----------------------------------------------------------+

  1. bysort old: drop if updated==recent & test2>1

  2. drop updated test1 test2

  3. save original2, replace

  4. list

  1. +--------------------------------+

  2. | old date link recent |

  3. |--------------------------------|

  4. 1. | A 2017/3/2 B C |

  5. 2. | B 2018/3/2 C D |

  6. 3. | C 2019/3/2 C D |

  7. 4. | O 2015/3/2 O N |

  8. 5. | P 2014/3/2 O N |

  9. |--------------------------------|

  10. 6. | Q 2013/3/2 P O |

  11. 7. | X1 2011/3/2 X2 X3 |

  12. 8. | X2 2012/3/2 X2 X3 |

  13. +--------------------------------+

接下来通过重复识别 two-step 链的方法识别 three-step 链,即将上图中 recent 的第 1 行和第 6 行分别更改为 D 和 N。

  1. use original2,clear

  2. generate link2 = recent

  3. save original3, replace


  4. use original2,clear

  5. generate link2 = link

  6. append using original3

  7. sort link2 date

  8. list

  1. +----------------------------------------+

  2. | old date link recent link2 |

  3. |----------------------------------------|

  4. 1. | A 2017/3/2 B C B |

  5. 2. | A 2017/3/2 B C C |

  6. 3. | B 2018/3/2 C D C |

  7. 4. | C 2019/3/2 C D C |

  8. 5. | B 2018/3/2 C D D |

  9. |----------------------------------------|

  10. 6. | C 2019/3/2 C D D |

  11. 7. | P 2014/3/2 O N N |

  12. 8. | O 2015/3/2 O N N |

  13. 9. | Q 2013/3/2 P O O |

  14. 10. | P 2014/3/2 O N O |

  15. |----------------------------------------|

  16. 11. | O 2015/3/2 O N O |

  17. 12. | Q 2013/3/2 P O P |

  18. 13. | X1 2011/3/2 X2 X3 X2 |

  19. 14. | X2 2012/3/2 X2 X3 X2 |

  20. 15. | X1 2011/3/2 X2 X3 X3 |

  21. |----------------------------------------|

  22. 16. | X2 2012/3/2 X2 X3 X3 |

  23. +----------------------------------------+

  1. bysort link2: generate test1=_N

  2. list

  1. +------------------------------------------------+

  2. | old date link recent link2 test1 |

  3. |------------------------------------------------|

  4. 1. | A 2017/3/2 B C B 1 |

  5. 2. | A 2017/3/2 B C C 3 |

  6. 3. | B 2018/3/2 C D C 3 |

  7. 4. | C 2019/3/2 C D C 3 |

  8. 5. | B 2018/3/2 C D D 2 |

  9. |------------------------------------------------|

  10. 6. | C 2019/3/2 C D D 2 |

  11. 7. | P 2014/3/2 O N N 2 |

  12. 8. | O 2015/3/2 O N N 2 |

  13. 9. | Q 2013/3/2 P O O 3 |

  14. 10. | P 2014/3/2 O N O 3 |

  15. |------------------------------------------------|

  16. 11. | O 2015/3/2 O N O 3 |

  17. 12. | Q 2013/3/2 P O P 1 |

  18. 13. | X1 2011/3/2 X2 X3 X2 2 |

  19. 14. | X2 2012/3/2 X2 X3 X2 2 |

  20. 15. | X1 2011/3/2 X2 X3 X3 2 |

  21. |------------------------------------------------|

  22. 16. | X2 2012/3/2 X2 X3 X3 2 |

  23. +------------------------------------------------+

  1. drop if test1!=3

  2. list

  1. +------------------------------------------------+

  2. | old date link recent link2 test1 |

  3. |------------------------------------------------|

  4. 1. | A 2017/3/2 B C C 3 |

  5. 2. | B 2018/3/2 C D C 3 |

  6. 3. | C 2019/3/2 C D C 3 |

  7. 4. | Q 2013/3/2 P O O 3 |

  8. 5. | P 2014/3/2 O N O 3 |

  9. |------------------------------------------------|

  10. 6. | O 2015/3/2 O N O 3 |

  11. +------------------------------------------------+

  1. generate str2 recent2=""

  2. bysort link2: replace recent2=recent[_N]

  3. drop test1

  4. save original4.dta,replace

  5. list

  1. +--------------------------------------------------+

  2. | old date link recent link2 recent2 |

  3. |--------------------------------------------------|

  4. 1. | A 2017/3/2 B C C D |

  5. 2. | B 2018/3/2 C D C D |

  6. 3. | C 2019/3/2 C D C D |

  7. 4. | Q 2013/3/2 P O O N |

  8. 5. | P 2014/3/2 O N O N |

  9. |--------------------------------------------------|

  10. 6. | O 2015/3/2 O N O N |

  11. +--------------------------------------------------+

第四步:形成完整的 mapch data

通过上述数据处理,我们已经形成了包含 one-step 链信息的 original.dta,包含 two-step 链信息的 original2.dta,包含 three-step 链信息的 original4.dta,接下来只需将这些数据合并即可。

  1. use original,clear

  2. merge 1:1 old using original2.dta

  3. drop _merge

  4. replace recent=updated if recent==""

  5. merge 1:1 old using original4.dta

  6. drop _merge

  7. replace recent2=recent if recent2==""

  8. list

  1. +------------------------------------------------------------+

  2. | old updated date link recent link2 recent2 |

  3. |------------------------------------------------------------|

  4. 1. | A B 2017/3/2 B C C D |

  5. 2. | B C 2018/3/2 C D C D |

  6. 3. | C D 2019/3/2 C D C D |

  7. 4. | E F 2018/3/2 F F |

  8. 5. | G H 2017/3/2 H H |

  9. |------------------------------------------------------------|

  10. 6. | O N 2015/3/2 O N O N |

  11. 7. | P O 2014/3/2 O N O N |

  12. 8. | Q P 2013/3/2 P O O N |

  13. 9. | X1 X2 2011/3/2 X2 X3 X3 |

  14. 10. | X2 X3 2012/3/2 X2 X3 X3 |

  15. +------------------------------------------------------------+

其中,从 oldrecent2 便是“事件链”的最终映射。

3. 使用 mapch 命令:更高效的处理方式

mapch  是  “map chains of events”  的简写,语法如下:

  1. mapch begin end [time]

其中,begin 是一个事件的开始状态,正如上文中的 old 变量,end 是一个事件的结束状态,正如上文中的 updated 变量。mapch  可以在包含事件发生时间的数据中使用,也可以应用于没有事件发生时间的数据。

使用 mapch 命令会产生两个新的变量 recentNoofEventsrecent 对应出了 begin 变量的结束状态,NoofEvents 包含从 beginrecent 每个链的事件数,同时还报告出了 n-step 链的频率。

  1. use original,clear

  2. mapch old updated date

  1. ********************

  2. * Mapping complete *

  3. ********************


  4. Frequency of NoOfEvents:


  5. NoOfEvents | Freq. Percent Cum.

  6. ------------+-----------------------------------

  7. 1 | 2 20.00 20.00

  8. 2 | 2 20.00 40.00

  9. 3 | 6 60.00 100.00

  10. ------------+-----------------------------------

  11. Total | 10 100.00


  12. The number of 1-step chains is equal to 2/1

  13. The number of 2-step chains is equal to 2/2

  14. The number of 3-step chains is equal to 6/3

  1. +----------------------------------------------+

  2. | old updated date recent NoOfEv~s |

  3. |----------------------------------------------|

  4. 1. | A B 2017/3/2 D 3 |

  5. 2. | B C 2018/3/2 D 3 |

  6. 3. | C D 2019/3/2 D 3 |

  7. 4. | E F 2018/3/2 F 1 |

  8. 5. | G H 2017/3/2 H 1 |

  9. |----------------------------------------------|

  10. 6. | Q P 2013/3/2 N 3 |

  11. 7. | P O 2014/3/2 N 3 |

  12. 8. | O N 2015/3/2 N 3 |

  13. 9. | X1 X2 2011/3/2 X3 2 |

  14. 10. | X2 X3 2012/3/2 X3 2 |

  15. +----------------------------------------------+

如果数据中不包含事件发生的时间,那么使用 mapch  命令后会产生一个 date 变量。

  1. use original,clear

  2. drop date

  3. mapch old updated

  4. list

  1. +------------------------------------------+

  2. | old updated date recent NoOfEv~s |

  3. |------------------------------------------|

  4. 1. | A B 1 D 3 |

  5. 2. | B C 2 D 3 |

  6. 3. | C D 3 D 3 |

  7. 4. | E F . F 1 |

  8. 5. | G H . H 1 |

  9. |------------------------------------------|

  10. 6. | Q P 1 N 3 |

  11. 7. | P O 2 N 3 |

  12. 8. | O N 3 N 3 |

  13. 9. | X1 X2 1 X3 2 |

  14. 10. | X2 X3 2 X3 2 |

  15. +------------------------------------------+

参考文献:Vanlaar W. A shortcut through long loops: An illustration of two alternatives to looping over observations[J]. Stata Journal, 2008, 8(4): 540-553. [PDF]

关于我们

  • Stata 连享会(公众号:StataChina)】由中山大学连玉君老师团队创办,旨在定期与大家分享 Stata 应用的各种经验和技巧。

  • 公众号推文同步发布于 CSDN-Stata连享会 、简书-Stata连享会 和 知乎-连玉君Stata专栏。可以在上述网站中搜索关键词StataStata连享会后关注我们。

  • 点击推文底部【阅读原文】可以查看推文中的链接并下载相关资料。

  • Stata连享会 精彩推文1  || 精彩推文2

联系我们

  • 欢迎赐稿: 欢迎将您的文章或笔记投稿至Stata连享会(公众号: StataChina),我们会保留您的署名;录用稿件达五篇以上,即可免费获得 Stata 现场培训 (初级或高级选其一) 资格。

  • 意见和资料: 欢迎您的宝贵意见,您也可以来信索取推文中提及的程序和数据。

  • 招募英才: 欢迎加入我们的团队,一起学习 Stata。合作编辑或撰写稿件五篇以上,即可免费获得 Stata 现场培训 (初级或高级选其一) 资格。

  • 联系邮件: StataChina@163.com

往期精彩推文


欢迎加入Stata连享会(公众号: StataChina)

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

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