查看原文
其他

Statalist上的“火云邪神”

数据分析团队 Stata and Python数据分析 2022-03-15

本文作者:李朋冲

文字编辑:李钊颖

技术总编:李朋冲


Stata是一个统计软件,它拥有强大的数据管理和计量统计功能,更为重要的是,它的程序语言也非常简单、清晰——基本上我们可以从命令的名称猜到它的大致用途。即便一下子懵圈了,也不要紧,Stata有一个便捷的帮助体系来帮我们清醒”——searchfindithelp,可以帮助用户迅速找到实现某项功能的命令,并了解它的具体用法。特别是help,简直可以说是武林秘籍,有了“help”,我们就可以学习到全球所有Stata开发者提供的独门绝技”——“洪家铁线拳五郎八卦棍十二路谭腿”…总有一款适合你!



不过,每个月,又或是每天,总有一些数据清洗或者计量上的问题困扰着你——你困惑,失眠,掉头发,这时你绝对不会唱:来啊,快活啊,反正有,大把头发!一个星期过去了,两个星期过去了,你卡壳了,诸事无进展,不敢接老师电话只是“初级水平”,“更高层次”的表现会是怀疑并否定自己:我适合写论文,做学术吗?为何一事无成?

 

不要这样嘛!有啥子事解决不了呢!要知道,有问题的地方,总会有转机,天无绝人之路!

 

当我们走在研究的路上时,我们应当唱起Michael的歌You Are Not Alone,也应该去听一听安菲尔德球场上空万人唱响的You Will Never Walk Alone!要知道,我们遇到的问题,说不定别人也早已遇到过,如果足够幸运,或许已经有一些解决方法,抑或是可以给我们一些启发;即使是在探索一个未知的领域,也一定有一些先行者到达了这个领域的边界,做出了一些试探,正是这一步一步的试探,指引了后来者前行的道路。这个世界上,总会有一些好心人,他们乐于分享,乐于助人,乐于为后进者指引方向,这些人都是品德高尚的人!如果有这样一个地方,里面的每个人都很热心,那真是令人开心的一件事!

 

我们在前面提到了“help”,还有另一个能量场我们没有说到——STATALIST(网址:https://www.statalist.org/)。(输入网址后,进入主页,点击Enter the forum,在新页面中点击general,即可查看各种问题。如果要发帖,需要注册一个账号。)



这是Stata的一个论坛,以前是一个邮箱系统,专门接收Stata用户发送的问题,后来数量太多,就升级为论坛了。在这里,所有的用户都可以按照一定的规则提交自己在数据整理和计量统计上的问题,会有众多的用户一起帮忙想办法解决。大家在这个论坛上互相帮助,即使未曾谋面,也可以感受到helper的热情。这里没有谁鄙视别人问的问题太low,也没有谩骂与争吵,有的,只是帮忙解决问题的热心肠!

 

论坛里,有众多“能量极强、法术无边”的“终极大BOSS”,他们有的是著作等身的终身教授,有的是正在学术上升期的青年教师。他们每天都会在论坛里查看各种问题,帮忙解决,“大神一出手,就知有没有”!秋风扫落叶,迅速解决各种疑难杂症,可谓药到病除,浑身上下十万个毛孔,没有一个不舒畅的!

 

我基本上每天都会登录到Statalist上,去查看一些数据处理技巧,当然了,顺带也学一下英语。基本上我们遇到的数据处理问题,论坛上都已经有人问过并且得到了很好的解答。有的时候,这些教授们还会“神仙打架”,给出不同的处理程序,但能从字里行间看到他们应该是惺惺相惜,毕竟,江湖中有一些和自己段位相同并且同样热衷于帮助后辈的高手,确是乐事一件。

 

有几个名字,经常出现在“Last Post”这一栏中(最近一次回帖),基本上每一页都可以见到(如下图)。在此介绍这几位令人敬仰的教授,当然,论坛上还有很多这样非常热心的学者,他们受得起任何的赞美与尊敬!



左一:Nick Cox教授,英国杜伦大学,主要处理环境数据和社会科学数据。兴趣包括:Stata图形,探索性数据分析,广义线性模型,数据分布、转换、方向性数据分析。Cox教授是Stata的活跃用户,在Stata上发布多个命令并在StataForum上广泛编写程序,帮助解决问题。也在Stata Journal上发表许多文章。截至目前,已在论坛上回帖将近19000次,日均回帖量9.43个。

左二:Clyde Schechter教授,纽约阿尔伯特爱因斯坦医学院,主要研究流行病学、癌症预防和仿真建模。Schechter教授回帖近16500次,日均回帖8.22篇。

右二:Carlo Lazzaro教授,意大利米兰卫生经济学家和研究主任,曾经是一名网球运动员。研究领域:医疗保健计划的经济效益评估,医药经济学,卫生经济学。Lazzaro教授对专业研究领域的统计(包括生物统计学和健康计量经济学)以及贝叶斯统计十分感兴趣。回帖量9200多篇,日均4.6篇。

右一:Richard Williams教授,美国圣母大学,主要从事社会学研究,研究兴趣包括计量统计方法、人口统计学和城市社会学。Williams教授获得2015年Stata Journal编辑奖,目前回帖量4000多篇。

 

以上列举的只是其中的几位大神,还有很多数据处理和计量水平都非常高超的学者,在此,我们有必要说出这两句话:

 


如果我们有什么问题,可以在论坛上搜索,去看一看大家都是怎么解答的。论坛上的问题现在已经有1000多页了,每页50个问题。这么丰富的一个资料库,足以囊括常见的数据处理和计量问题,并且还有高质量的解答。其实大家也可以抓取一些页面,就放在那里,有需要了就去搜索一下,找到链接,直接打开就可以了。

 

我们以第一页为例,给大家介绍一下如何获取里面的信息。

 

在上面已经通过截图给大家展示如何进入论坛的general部分,它的网址是:https://www.statalist.org/forums/forum/general-stata-discussion/general

我们点击下一个页面,会看到:https://www.statalist.org/forums/forum/general-stata-discussion/general/page2

其实第一页的链接也可以是:https://www.statalist.org/forums/forum/general-stata-discussion/general/page1

那我们想要获取哪些信息呢?我们来看下面的截图:



首先是帖子的title,我们观察到,title是带有链接的,我们把链接也抓下来;接着是下方的发帖人(starter),点击之后会显示每个注册用户的个人简介(starter_profile):



然后是发帖时间(start_time),中间两栏是回帖数量和浏览量(posts_count和views_count),右侧“Last Post”一栏是最近一次回帖人姓名和链接(last_helper和last_helper_profile)以及最近一次回帖时间(last_post_time)。

 

之所以要获取回帖时间,是因为,当我们在论坛上发帖子的时候,要查看一下比较活跃的helper,他们的活动时间一般是在什么时候,这样我们发帖子,也会有更大的几率被看到。

 

综上,要获得的信息包括:title、post_url、starter、starter_profile、start_time、posts_count、views_count、last_helper、last_helper_profile、last_post_time。

 

 

老规矩,第一步要先查看源代码,看看里面有没有我们需要的信息。我们在源代码里搜索当前页的第二个标题(第一个是Stata16发布的广告,我们不管它):Probit model,outcome does not vary



我们看到,源代码里包含我们所需要的10个信息。我们以这个帖子为例,看一下包含有title和url的源代码:

 

<a href="https://www.statalist.org/forums/forum/general-stata-discussion/general/1518764-probit-model-outcome-does-not-vary"class="topic-title js-topic-title">Probit model, outcome does not vary</a>

 

我们需要的就是未标记为绿色的部分,在源代码里查看一下“topic-title js-topic-title”:



一共有51处,其中第1个是Stata16发布的帖子,页面上显示“Sticky”,也就是说,这个帖子会出现在后续所有页面的源代码里,即使页面上并不显示它。我们在后续的处理中,删除这个帖子即可。

 

包含有以上10类信息的源代码如下,我们进行分析:

 

1<a href="https://www.statalist.org/forums/forum/general-stata-discussion/general/1518764-probit-model-outcome-does-not-vary" class="topic-title js-topic-title">Probit model,outcome does not vary</a>

2by <ahref="https://www.statalist.org/forums/member/38621-aaron-nagel">Aaron Nagel</a>

3Started by <a href="https://www.statalist.org/forums/member/38621-aaron-nagel">Aaron Nagel</a>, <spanclass="date">Today, 15:17</span>

4<divclass="posts-count">3

5<divclass="views-count">13

6by <ahref="https://www.statalist.org/forums/member/288-richard-williams">Richard Williams</a>

7<spanclass="post-date">Today,20:40</span>

 

我们观察到:

在(1)中,我们可以用“topic-title js-topic-title”标签来界定帖子的名称和链接。

在(2)、(3)和(6)中,都有“by <a href="”标签,我们可以用来保留发帖人姓名、发帖人简介、发帖时间以及回帖人、回帖人简介等信息。

在(4)中,用“<div>”标签保留回帖量信息。

在(5)中,用“<div>”标签保留浏览量信息。

在(7)中,用“<span>”标签保留最近一次回帖时间的信息。

 

 

经过了以上分析之后,我们使用copy命令获取网页源代码并导入Stata:
clearefolder statalist, cd(d:/) //在D盘创建statalist文件夹,并切换到该路径下
copy "https://www.statalist.org/forums/forum/general-stata-discussion/general/page1" temp1.txt, replaceinfix strL v 1-100000 using temp1.txt, clear
导入之后,我们首先查看一下,使用以上的标签可否明确界定出我们所需要的信息:
count if ustrregexm(v, "topic-title js-topic-title") //帖子题目和链接count if ustrregexm(v, `"by <a href="(.+?)">(.+?)</a>"') //发帖人信息,发帖时间和回帖人信息count if ustrregexm(v, `"<div class="posts-count">"') //回帖数量count if ustrregexm(v, `"<div class="views-count">"') //浏览量count if ustrregexm(v, `"<span class="post-date">"') //最近一次回帖时间


我们注意到,有一个数字是153,这是因为`"by <ahref="(.+?)">(.+?)</a>"'可以匹配到一个帖子的3行信息,这样,一共有51个帖子。这个结果与图上其他HTML标签匹配到的数量相同。所以,我们可以使用这些标签保留有用信息。


keep if ustrregexm(v, `"topic-title js-topic-title"') | /// ustrregexm(v, `"by <a href="(.+?)">(.+?)</a>"') | ///    ustrregexm(v, `"<div class="posts-count">"') |  ///  ustrregexm(v, `"<div class="views-count">"') | ///    ustrregexm(v, `"<span class="post-date">"') 

当然,这些标签也可以用“或”的形式合在一起,这里是为了更清楚地显示,所以进行了分离。运行完这段程序之后,数据集里还有357行观测值,每一个帖子对应7行信息。



我们进行如下操作:

forvalues j = 1/6{ gen v`j' = v[_n + `j']}keep if mod(_n,7) == 1


这样,数据集中只剩下51个观测值。我们在前面提到,第1个帖子是Stata16发布的内容,我们不需要这个,删除掉。

drop in 1

下面开始从里面提取信息,以第一行观测值为例,从v-v6依次介绍:

 

1<ahref="https://www.statalist.org/forums/forum/general-stata-discussion/general/1518764-probit-model-outcome-does-not-vary"class="topic-title js-topic-title">Probit model, outcome does notvary</a>
v用正则表达式表示为:`"<a href="(.+?)".+?>(.+?)</a>"',第1个子表达式表示链接,第2个子表达式表示帖子题目。在这里用到了绝对引用`""'和正则表达式的懒惰模式。
 
2by <ahref="https://www.statalist.org/forums/member/38621-aaron-nagel">AaronNagel</a>
v1用正则表达式表示为:`"by <ahref="(.+?)">(.+?)</a>"',第1个子表达式表示发帖者简介,第2个子表达式表示发帖人姓名。
3Started by <ahref="https://www.statalist.org/forums/member/38621-aaron-nagel">AaronNagel</a>, <span>Today, 15:17</span>
我们只想从v2中获得发帖时间,因此,v2可表示为:`".+">(.+?)</span>"',在此用到了贪婪模式和懒惰模式,这里只有1个子表达式,匹配到的是发帖时间。贪婪模式下,.+匹配到的是第二个“">”左边的所有信息:Started by <ahref="https://www.statalist.org/forums/member/38621-aaron-nagel">AaronNagel</a>, <span class="date
 
4<div>3
v3可表示为:`".+(\d+)"',子表达式表示回帖量。
 
5<div>13
v4v3类似:`".+(\d+)"'
 
6by <a href="https://www.statalist.org/forums/member/288-richard-williams">RichardWilliams</a>
v5v1类似:`"by <ahref="(.+?)">(.+?)</a>"'
 
7<span>Today,20:40</span>
v6表示为:`".+">(.+?)</span>"'

 

 

经过上述分析之后,我们运行以下程序,获得我们需要的10个变量:
gen post_url = ustrregexs(1) if ustrregexm(v, `"<a href="(.+?)".+?>(.+?)</a>"')gen title = ustrregexs(2) if ustrregexm(v, `"<a href="(.+?)".+?>(.+?)</a>"')
gen starter_profile = ustrregexs(1) if ustrregexm(v1, `"by <a href="(.+?)">(.+?)</a>"')gen starter = ustrregexs(2) if ustrregexm(v1, `"by <a href="(.+?)">(.+?)</a>"')
gen start_time = ustrregexs(1) if ustrregexm(v2, `".+">(.+?)</span>"')
gen posts_count = ustrregexs(1) if ustrregexm(v3, `".+(\d+)"')
gen views_count = ustrregexs(1) if ustrregexm(v4, `".+(\d+)"')
gen last_helper_profile = ustrregexs(1) if ustrregexm(v5, `"by <a href="(.+?)">(.+?)</a>"')gen last_helper = ustrregexs(2) if ustrregexm(v5, `"by <a href="(.+?)">(.+?)</a>"')
gen last_post_time = ustrregexs(1) if ustrregexm(v6, `".+">(.+?)</span>"')
drop v-v6

查看数据集,我们看到title变量下的第8行:Createa variable that is equal to 1 if &quot;the below value&quot; bigger than&quot;the above value&quot; 里面有这样的字符串:“&quot;”,这其实是一个英文引号“"”。如果不清楚,查看一下原始网页。我们对它进行替换:

replace title = subinstr(title, `"&quot;"', `"""', .) //将引号正常显示出来

在start_time和last_post_time变量下,有很多“Today”和“Yesterday”,我们当然可以看看今天和昨天是什么日子,然后进行替换。但如果程序是在无人照看的时候运行的,这又该怎么解决呢?(我们暂不考虑和美国的时差)

 

我们知道,Stata中有几类返回值:r类、e类、c类。c类返回值显示的是一些系统参数和设置,日期和时间也包含在其中。



使用宏扩展函数,将日期显示出来:

local date: disp %dd-m-CY date(c(current_date), "DMY")di "`date'"local date: disp %dd-m-CY date(c(current_date), "DMY")-1di "`date'"

下面我们就使用宏扩展函数,对“Today”和“Yesterday”进行替换,同时为了保持变量下字符串类型的一致性,把“-”替换为一个空格:

foreach var in start_time last_post_time{ local date: disp %dd-m-CY date(c(current_date), "DMY") replace `var' = subinstr(`var', "Today", "`date'", .)
local date1: disp %dd-m-CY date(c(current_date), "DMY")-1 replace `var' = subinstr(`var', "Yesterday", "`date1'", .) replace `var' = subinstr(`var', "-", " ", .) }save page1.dta, replace

这样我们得到了一个页面50个帖子的链接及标题,如果想要得到其他页面的,在外层对页码进行循环即可。完整程序如下:

clearefolder statalist, cd(d:/)
copy "https://www.statalist.org/forums/forum/general-stata-discussion/general/page1" temp1.txt, replace infix strL v 1-100000 using temp1.txt, clear
/*以下程序读者可自行选择是否运行count if ustrregexm(v, "topic-title js-topic-title") //帖子题目和链接count if ustrregexm(v, `"by <a href="(.+?)">(.+?)</a>"') //发帖人信息,发帖时间和回帖人信息count if ustrregexm(v, `"<div class="posts-count">"') //回帖数量count if ustrregexm(v, `"<div class="views-count">"') //浏览量count if ustrregexm(v, `"<span class="post-date">"') //最近一次回帖时间*/
keep if ustrregexm(v, `"topic-title js-topic-title"') | /// ustrregexm(v, `"by <a href="(.+?)">(.+?)</a>"') | ///    ustrregexm(v, `"<div class="posts-count">"') |  ///  ustrregexm(v, `"<div class="views-count">"') | /// ustrregexm(v, `"<span class="post-date">"')
forvalues j = 1/6{ gen v`j' = v[_n + `j']}keep if mod(_n,7) == 1drop in 1
***从v-v6中提取所需信息gen post_url = ustrregexs(1) if ustrregexm(v, `"<a href="(.+?)".+?>(.+?)</a>"')gen title = ustrregexs(2) if ustrregexm(v, `"<a href="(.+?)".+?>(.+?)</a>"')
gen starter_profile = ustrregexs(1) if ustrregexm(v1, `"by <a href="(.+?)">(.+?)</a>"')gen starter = ustrregexs(2) if ustrregexm(v1, `"by <a href="(.+?)">(.+?)</a>"')
gen start_time = ustrregexs(1) if ustrregexm(v2, `".+">(.+?)</span>"')
gen posts_count = ustrregexs(1) if ustrregexm(v3, `".+(\d+)"')
gen views_count = ustrregexs(1) if ustrregexm(v4, `".+(\d+)"')
gen last_helper_profile = ustrregexs(1) if ustrregexm(v5, `"by <a href="(.+?)">(.+?)</a>"')gen last_helper = ustrregexs(2) if ustrregexm(v5, `"by <a href="(.+?)">(.+?)</a>"')
gen last_post_time = ustrregexs(1) if ustrregexm(v6, `".+">(.+?)</span>"')
drop v-v6
***对变量中的字符串进行替换replace title = subinstr(title, `"&quot;"', `"""', .) //将引号正常显示出来
foreach var in start_time last_post_time{ local date: disp %dd-m-CY date(c(current_date), "DMY") replace `var' = subinstr(`var', "Today", "`date'", .)
local date1: disp %dd-m-CY date(c(current_date), "DMY")-1 replace `var' = subinstr(`var', "Yesterday", "`date1'", .) replace `var' = subinstr(`var', "-", " ", .) }
save page1.dta, replace

Statalist绝对是一个值得一逛的高水平论坛,在这里,你总能找到和你遇到相似问题的研究者。像上面介绍的几位教授,他们可以一下子看出你的问题出在哪里,即使,你没有发示例数据或者描述问题不甚清楚,当然,免不了被他们批评一顿!哈哈哈!

 

如果,在中文论坛上,大家没有找到问题的解决方法,不妨到Statalist上发个英文帖子,说不定,掐准了点儿发,一会儿Cox教授或者Schechter教授就会为你指点迷津!

 

最后,以一段话作为结尾:

 

现在,论文越来越不好发,到处都是拒稿

有拒稿的地方,一定有问题

有问题,那就一定会有转机

有个论坛,叫Statalist

上面有很多大牛,专门替人解除烦恼

 

看来,你的年纪应该也有二十多快三十了

呐,这几年读研、读博、工作

总有些数据处理的痛是你不愿意再提,有些实证你也不想再做

你觉得,有些审稿人曾经虐你千千万万遍,他们对不起你

也许你想过…

不写了

但是你不敢

哈,又或是你觉得自己确实有问题

 

其实,梳理一下思路,整理一下问题,发个英文帖子

也不是很难

呐,有一些学者,眼神非常犀利

碰巧乐于助人,也有一些空闲时间

只要你好好看一看发帖须知,把问题描述清楚

他们一定可以帮你解决问题

哈,你尽管考虑一下


——《东邪西毒》



对我们的推文累计打赏超过1000元,我们即可给您开具发票,发票类别为“咨询费”。用心做事,不负您的支持!
往期推文推荐
爬虫实战程序的函数封装
Zipfile(二)
利用collapse命令转化原始数据
Stata中的数值型
爬虫实战——聚募网股权众筹信息爬取
duplicates drop之前,我们要做什么?
类型内置函数-type() isinstance()
数据含义记不住?—— label“大神”来帮忙

实战演练-如何获取众筹项目的团队信息

Zipfile(一)

tabplot命令

Jupyter Notebook不为人知的秘密

字符串方法(三)

encode 和decode——带你探索编码与解码的世界

字符串方法(二)

如何快速生成分组变量?

用Stata实现数据标准化


关于我们

微信公众号“Stata and Python数据分析”分享实用的stata、python等软件的数据处理知识,欢迎转载、打赏。我们是由李春涛教授领导下的研究生及本科生组成的大数据处理和分析团队。

此外,欢迎大家踊跃投稿,介绍一些关于stata和python的数据处理和分析技巧。
投稿邮箱:statatraining@163.com
投稿要求:
1)必须原创,禁止抄袭;
2)必须准确,详细,有例子,有截图;
注意事项:
1)所有投稿都会经过本公众号运营团队成员的审核,审核通过才可录用,一经录用,会在该推文里为作者署名,并有赏金分成。
2)邮件请注明投稿,邮件名称为“投稿+推文名称”。
3)应广大读者要求,现开通有偿问答服务,如果大家遇到有关数据处理、分析等问题,可以在公众号中提出,只需支付少量赏金,我们会在后期的推文里给予解答。

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

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