绘制《倚天屠龙记》地名词云图
本文作者:张 宇
本文编辑:张 宇
技术总编:李春涛
爬虫俱乐部是您身边的科研助手,能够为您在数据处理、实证研究中提供帮助。承蒙30000+粉丝的支持与厚爱,我们在腾讯课堂推出了网络视频课程,专注于数据整理、网络爬虫、循环命令编制和结果输出…李老师及团队精彩地讲解,深入浅出,注重案例与实战,让您更加快速高效地掌握Stata技巧及数据处理的精髓,而且可以无限次重复观看,百分百好评,简单易学,一个月让您从入门到精通。绝对物超所值!观看学习网址:https://ke.qq.com/course/286526?tuin=1b60b462,敬请关注!
“飞雪连天射白鹿,笑书神侠倚碧鸳”未陪伴他一生,却是他一生的写照,他是金庸,他是大侠……愿天堂也有刀剑相伴,让金庸先生能快意恩仇。他的书针砭时弊,他的言语字字珠玑,书是他的刀剑,是我们的宝藏。尤记“红颜弹指老,刹那芳华,与其天涯思苦,恋恋不舍,莫若相忘于江湖”,但这相思苦,真的能相忘于江湖吗?又闻“只要有人的地方就有恩怨,有恩怨就会有江湖,人就是江湖”,这江湖恩怨,又何日才能平息?金庸先生这一去,留下了他此生的“刀剑”,给后来人细细品味……
我们曾因“侠之大者,为国为民”路见不平,拔刀相助;也曾因“他强由他强,清风拂山岗”坚守信念,顽强不屈;更因小说里故事发生的地点,不远万里,去品味祖国山河的诗情画意。金庸先生的小说是刀剑,蕴藏着无与伦比的力量;也是瑰宝,等待着我们去挖掘,去研究。
今天,让我们用stata来实现自然语言处理中最基础的词频统计,得出《倚天屠龙记》中地名出现的次数,回忆地名身上承载的故事,感受那蓬勃宏伟的武侠世界。
第一步:准备分词所用的材料
1、文本
从金庸网(http://www.jinyongwang.com/yi/)上找到《倚天屠龙记》的文本,观察不同章节文本链接的区别。
可以看到,各章节的链接只有在标红的部分有区别,我们使用forvalues循环配合局部宏,就可以将所有的文本抓取下来。程序如下:
clear
cap mkdir D:/金庸小说倚天屠龙记地名/
cd D:/金庸小说倚天屠龙记地名/
local i = 443
forvalues z = 1/40 {
cap copy "http://www.jinyongwang.com/yi/`i'.html" "第`z'章.txt", replace
while _rc != 0 {
sleep 5000
cap copy "http://www.jinyongwang.com/yi/`i'.html" "第`z'章.txt", replace
}
local i = `i' + 1
di `i'
}
部分结果如下:
这些文本文档中存储的是《倚天屠龙记》每一章节文本的源代码。接下来,将源代码中的文字提取出来,合并到一个单元格中,保存成dta格式的文件,程序如下:
forvalues z = 1/40 {
clear
set obs 1
gen v = fileread("第`z'章.txt") //将txt文档读入stata的一个单元格。
replace v = ustrregexra(v,"\s","") //利用正则表达式将文本中所有的空格、回车符、换行符删除。
split v ,p(`"(adsbygoogle=window.adsbygoogle||[])"') //选择合适的标签拆分文本
keep v2
split v2 ,p("<p>")
drop v2 v21
sxpose , clear
replace _var1 = ustrregexra(_var1,"<.*?>","")
forvalues i = 2/`=_N' {
replace _var1 = _var1[1]+ _var1[`i'] in 1
} //把每一章的内容汇总到第一个单元格
keep in 1
save "第`z'章.dta", replace
}
2、分词字典
用户自定义的分词字典将对分词结果产生至关重要的积极影响。统计地名的词频,就需要尽可能多地将小说中的地名添加到词典中,使我们对地名的分词结果更加精确。
小编在“发现中国”网站上找到一份金庸小说爱好者制作超详细金庸地图(https://www.ageeye.cn/map/12518/?static=0#3/0/0/116.9659/38.7222):
网页中包含了金庸小说中的部分地名,此时可将其抓取下来,作为我们的分词字典。但是,查看网页源代码后发现源代码中找不到任何关于地名的信息,说明此链接不是网页的真实链接。使用推文《一起来揪出网页真是链接!》中的方法找到网页的真实链接,用copy命令将网页源代码抓取下来:
clear
cap mkdir D:/金庸小说地名/
cd D:/金庸小说地名/
copy "https://ageeye-mapjson.oss-cn-hangzhou.aliyuncs.com/map/1385/12518/8bdb77f23abf49a3b61d98bf73d5c403.json" temp.txt, replace
shellout temp.txt
结果如下:
文本文档显示的是汉字的十六进制unicode编码,先将其转为汉字:
clear all
tempname handle //使用临时句柄和file命令写一个python程序。
file open `handle' using 转码.py, replace text write
file write `handle' `"# -*- coding: utf-8 -*-"' _n
file write `handle' `"import json "' _n
file write `handle' `"with open('D:/金庸小说地名/temp.txt') as fp :"' _n
file write `handle' `" a = fp.read() "' _n
file write `handle' `"b = json.loads(a)"' _n
file write `handle' `"print(b)"' _n
file close `handle'
! python 转码.py > temp2.txt
shellout temp2.txt
部分结果如下:
键值对中“name”所对应的值就是我们需要的地名,将其提取出来,保存成dta文件:
clear
set obs 1
gen v = fileread("temp2.txt")
replace v = ustrfrom(v, "gb18030" ,1)
replace v = ustrregexra(v, "\s", "")
split v , p("</p><p><ahref=")
drop v
sxpose , clear
split _var1, p(`"'name'"')
drop _var1 _var11
foreach c of varlist _all {
replace `c' = ustrregexs(1) if ustrregexm(`c',`":'(.*?)'"')
}
stack * , into(v) clear
keep v
keep if ustrregexm(v,"[\u4e00-\u9fa5]")
rename v place
gen uone = usubstr(place, 1,1)
replace place = ustrregexrf(place , "疑","") if uone == "疑" //地名中有些内容存疑,添加分词字典时需要将“疑”字去掉。
keep place
save 金庸小说地名_temp.dta, replace
结果如下:
再手工整理部分地名,保存为“金庸小说地名.txt”,将这两部分分词字典合并。程序如下:
clear
insheet using 金庸小说地名.txt
replace v1 = ustrfrom(v1,"gb18030",1)
rename v1 place
append using 金庸小说地名_temp.dta
duplicates drop
compress
outsheet using 金庸小说地名_分词字典.txt, replace noname noquote //保存成没有重复值的txt文件。
save 金庸小说地名.dta, replace //再将地名保存成dta文件。
结果如下:
我们将在后面的分词程序中用到上述生成的分词字典。
3、停用词表
手工查找停用词表。在分词结果中会存在很多停用词,我们利用停用词表将其删除。
需要准备全部材料如下图所示:
第二步:分词
1、使用python中的jieba分词系统,对每一章节的文本进行分词,分词结果保存成txt文件
程序如下:
clear
cap mkdir D:\金庸小说倚天屠龙记地名/添加字典进行分词
cd D:\金庸小说倚天屠龙记地名/添加字典进行分词
forvalues z = 1/40 {
use "d:/金庸小说倚天屠龙记地名/第`z'章.dta",clear
local a = _var1[1] //局部宏`a'存储了某一章的内容。
di "`a' "
clear all
tempname handle //使用临时句柄和file命令写一个python程序。
file open `handle' using 分词.py, replace text write
file write `handle' `"# -*- coding: utf-8-*-"' _n
file write `handle' `"import jieba.posseg"' _n
file write `handle' `"jieba.load_userdict("D:/金庸小说地名/金庸小说地名_分词字典.txt")"' _n //添加用户自定义的分词字典。
file write `handle' `"string = "`a'""' _n //局部宏`a'在这里调用,表示对`a'里边的内容进行分词。
file write `handle' `"seg_list = jieba.posseg.cut(string)"' _n
file write `handle' `"for word in seg_list:"' _n
file write `handle' `" print(word.word, word.flag)"' //打印出分词结果,分词结果中包含关键词和词性。
file close `handle'
! python 分词.py > 第`z'章_分词结果.txt //在stata中调用python,运行py格式的文件。把分词结果保存到txt文件中。
}
2、将分词结果的txt文件读入stata,转换为dta文件
打开一个txt文件观察一下:
可见,分词结果包含关键词和其对应的词性,两者由空格分隔开。我们将其读入stata,并按照空格拆分。
程序如下:
forvalues z = 1/40 {
import delimited using 第`z'章_分词结果.txt, clear encoding("gb18030") ///
varnames(nonames) delimiter(`"afkadjfakldsfj;a'a"', asstring) //读取分词结果的txt文件。
cap split v1,p("") //将关键词和它所对应的词性分隔开。
if _rc != 0 {
continue
}
drop v1
rename v11 keyword
gen chap = `z' //标注关键词来自哪个章节。
save 第`z'章_分词结果.dta, replace
}
在这个过程中,我们还额外生成了一个变量——chap,代表这个关键词是来自哪一章节。这样做的好处是:如果要对每个章节分别进行研究,就可以根据chap变量进行筛选。
结果如下:
有了chap这个变量之后,可以清楚的知道上图为第40章的分词结果。
第三步:词频统计
首先,我们将所有的分词结果纵向合并:
clear
local files : dir "." file "第*章_分词结果.dta"
append using `files' //使用append对数据进行纵向合并
其次,删除停用词:
preserve
insheet using D:\金庸小说地名/停用词表.txt, clear //将停用词表读入stata。
levelsof v1,local(keyword) //将停用词封装在局部宏`keyword'中。
restore
foreach word in `keyword' {
drop if keyword == "`word'"
} //删除停用词。
最后,词频统计,并与字典中的地名匹配:
bysort keyword:gen frequency = _N
duplicates drop keyword , force //统计出《倚天屠龙记》中所有词的词频。
rename keyword place //修改变量名,为的是与分词字典横向合并。
merge 1:1 place using D:\金庸小说地名/金庸小说地名.dta
keep if _m == 3
drop _m
gsort -frequency //按照词频倒序排列。
结果如下:
变量place是书中出现的地名关键词;变量frequency是关键词在书中出现的次数,“少林寺”出现次数最多,共271次,可见《倚天屠龙记》中的故事与少林寺有千丝万缕的联系;而第二个变量则是关键词的词性,它是由jieba分词自动给出,下边给出一部分词性标注和词性说明供读者参考:
从词性表中可以看到,词性标注为“ns”代表这个词为地名。但是我们将分词结果与地名字典进行横向合并之后,发现有一些地名的词性标注不是“ns”。所以,如果单纯只保留词性为“ns”的观测值,是无法准确观测到书中出现的地名的。这就是要把分词结果与字典横向匹配的原因。所以,要使结果更加精确,就需要分词字典尽可能完善。
《倚天屠龙记》关于地名的词频统计已然呈现在眼前。看到这些耳熟能详的地名,你是否想起光明顶上,乾坤大挪移威震天下;华山君子剑名扬四海;武当七侠兄弟一心;蝶谷医仙一诺千金……
第四步:根据地名和其词频绘制词云图,实现关键词、词频的可视化
程序如下:
wordcloud place frequency using 词云.html , replace ///
size(15 80) range(3840 2160) //生成词云图。
shellout 词云.html
命令wordcloud后跟两个变量,第一个变量是关键词,第二个变量是词频。
词云图如下图所示:
词云图展示出来的是关键词,当把鼠标放在关键词上时,会显示出关键词的词频,这样一来,就实现了数据的可视化。关于文本的词频统计到这里就结束了,爬虫俱乐部后续将继续为您推出一系列自然语言处理的案例,敬请期待!
烟波浩渺,一转眼,金庸先生已离去多日。愿金庸先生一路保重,吾辈定执你留下的刀剑,书写天地间的壮丽诗篇。愿你于天地间遇一位张无忌为你一生画眉,愿你在红尘中携一个赵敏相随天涯。芳华无限,与君共享。
有问题,不要怕!访问
http://www.wuhanstring.com/uploads/5_aboutus/爬虫俱乐部-用户问题登记表.docx (复制到浏览器中)下载爬虫俱乐部用户问题登记表并按要求填写后发送至邮箱statatraining@163.com,我们会及时为您解答哟~
爬虫俱乐部的github主站正式上线了!我们的网站地址是:https://stata-club.github.io,粉丝们可以通过该网站访问过去的推文哟~
爬虫俱乐部隆重推出数据定制及处理业务,您有任何网页数据获取及处理方面的难题,请发邮件至我们邮箱statatraining@163.com,届时会有俱乐部高级会员为您排忧解难!
对爬虫俱乐部的推文累计打赏超过1000元我们即可给您开具发票,发票类别为“咨询费”。用心做事,只为做您更贴心的小爬虫!
往期推文推荐
关于我们
微信公众号“爬虫俱乐部”分享实用的stata命令,欢迎转载、打赏。爬虫俱乐部是由李春涛教授领导下的研究生及本科生组成的大数据分析和数据挖掘团队。
此外,欢迎大家踊跃投稿,介绍一些关于stata的数据处理和分析技巧。
投稿邮箱:statatraining@163.com
投稿要求:
1)必须原创,禁止抄袭;
2)必须准确,详细,有例子,有截图;
注意事项:
1)所有投稿都会经过本公众号运营团队成员的审核,审核通过才可录用,一经录用,会在该推文里为作者署名,并有赏金分成。
2)邮件请注明投稿,邮件名称为“投稿+推文名称”。
3)应广大读者要求,现开通有偿问答服务,如果大家遇到关于stata分析数据的问题,可以在公众号中提出,只需支付少量赏金,我们会在后期的推文里给予解答。