前言

一直以来对自然语言处理和社交网络分析都很感兴趣,前者能帮助我们从文本中获得很多发现,而后者能够让我们对人们和各个事物之间普遍存在的网络般的联系有更多认识。当二者结合,又会有怎样的魔力呢?

作为一个三国迷,我就有了这样的想法:能不能用文本处理的方法,得到《三国演义》中的人物社交网络,再进行分析呢?Python中有很多好工具能够帮助我实践我好奇的想法,现在就开始动手吧。

准备工作

获得《三国演义》的文本。

        chapters = get_sanguo()                 # 文本列表,每个元素为一章的文本
print(chapters[0][:106])

        第一回 宴桃园豪杰三结义 斩黄巾英雄首立功
滚滚长江东逝水,浪花淘尽英雄。是非成败转头空。
青山依旧在,几度夕阳红。
白发渔樵江渚上,惯看秋月春风。一壶浊酒喜相逢。
古今多少事,都付笑谈中

《三国演义》并不是很容易处理的文本,它接近古文,我们会面对古人的字号等一系列别名。比如电脑怎么知道“玄德”指的就是“刘备”呢?那就要我们给它一些知识。

我们人通过学习知道“玄德”是刘备的字,电脑也可以用类似的方法完成这个概念的连接。

我们需要告诉电脑,“刘备”是实体(类似于一个对象的标准名),而“玄德”则是“刘备”的一个指称,告诉的方式,就是提供电脑一个知识库。

        entity_mention_dict, entity_type_dict = get_sanguo_entity_dict()
print("刘备的指称有:",entity_mention_dict["刘备"])

        刘备的指称有: ['刘备', '刘玄德', '玄德', '使君']

除了人的实体和指称以外,我们也能够包括三国势力等别的类型的指称,比如“蜀”又可以叫“蜀汉”,所以知识库里还可以包括实体的类型信息来加以区分。

        print("刘备的类型为",entity_type_dict["刘备"])
print("蜀的类型为",entity_type_dict["蜀"])
print("蜀的指称有",entity_mention_dict["蜀"])刘备的类型为 人名
蜀的类型为 势力
蜀的指称有 ['蜀', '蜀汉']

有了这些知识,理论上我们就可以编程联系起实体的各个绰号啦。不过若是要从头做起的话,其中还会有不少的工作量。

而HarvestText[1]是一个封装了这些步骤的文本处理库,可以帮助我们轻松完成这个任务。

        ht = HarvestText()
ht.add_entities(entity_mention_dict, entity_type_dict)      # 加载模型
print(ht.seg("誓毕,拜玄德为兄,关羽次之,张飞为弟。",standard_name=True))['誓毕', ',', '拜', '刘备', '为兄', ',', '关羽', '次之', ',', '张飞', '为弟', '。']

社交网络建立

成功地把指称统一到标准的实体名以后,我们就可以着手挖掘三国的社交网络了。具体的建立方式是利用邻近共现关系。

每当一对实体在两句话内同时出现,就给它们加一条边。那么建立网络的整个流程就如同下图所示:

我们可以使用HarvestText提供的函数直接完成这个流程,让我们先在第一章的小文本上实践一下:

        # 准备工作
doc = chapters[0].replace("操","曹操")                                  # 由于有时使用缩写,这里做一个微调
ch1_sentences = ht.cut_sentences(doc)     # 分句
doc_ch01 = [ch1_sentences[i]+ch1_sentences[i+1] for i in range(len(ch1_sentences)-1)]  #获得所有的二连句
ht.set_linking_strategy("freq")# 建立网络
G = ht.build_entity_graph(doc_ch01, used_types=["人名"])              # 对所有人物建立网络,即社交网络# 挑选主要人物画图
important_nodes = [node for node in G.nodes if G.degree[node]>=5]
G_sub = G.subgraph(important_nodes).copy()
draw_graph(G_sub,alpha=0.5,node_scale=30,figsize=(6,4))

他们之间具体有什么关系呢?我们可以利用文本摘要得到本章的具体内容:

        stopwords = get_baidu_stopwords()    #过滤停用词以提高质量for i,doc in enumerate(ht.get_summary(doc_ch01, topK=3, stopwords=stopwords)):print(i,doc)玄德见皇甫嵩、朱儁,具道卢植之意。嵩曰:“张梁、张宝势穷力乏,必投广宗去依张角。
时张角贼众十五万,植兵五万,相拒于广宗,未见胜负。植谓玄德曰:“我今围贼在此,贼弟张梁、张宝在颍川,与皇甫嵩、朱儁对垒。
次日,于桃园中,备下乌牛白马祭礼等项,三人焚香再拜而说誓曰:“念刘备、关羽、张飞,虽然异姓,既结为兄弟,则同心协力,

本章的主要内容,看来就是刘关张桃园三结义,并且共抗黄巾贼的故事。

三国全网络绘制

有了小范围实践的基础,我们就可以用同样的方法,整合每个章节的内容,画出一张横跨三国各代的大图。

        G_chapters = []
for chapter in chapters:sentences = ht.cut_sentences(chapter)     # 分句docs = [sentences[i]+sentences[i+1] for i in range(len(sentences)-1)]G_chapters.append(ht.build_entity_graph(docs, used_types=["人名"]))# 合并各张子图
G_global = nx.Graph()
for G0 in G_chapters:for (u,v) in G0.edges:if G_global.has_edge(u,v):G_global[u][v]["weight"] += G0[u][v]["weight"]else:G_global.add_edge(u,v,weight=G0[u][v]["weight"])# 忽略游离的小分支只取最大连通分量
largest_comp = max(nx.connected_components(G_global), key=len)
G_global = G_global.subgraph(largest_comp).copy()
print(nx.info(G_global))Name:
Type: Graph
Number of nodes: 1290
Number of edges: 10096
Average degree:  15.6527

整个社交网络有1290个人那么多,还有上万条边!那么我们要把它画出来几乎是不可能的,那么我们就挑选其中的关键人物来画出一个子集吧。

        important_nodes = [node for node in G_global.nodes if G_global.degree[node]>=30]
G_main = G_global.subgraph(important_nodes).copy()

用pyecharts进行可视化:

        from pyecharts import Graph
nodes = [{"name": "结点1", "value":0, "symbolSize": 10} for i in range(G_main.number_of_nodes())]
for i,name0 in enumerate(G_main.nodes):nodes[i]["name"] = name0nodes[i]["value"] = G_main.degree[name0]nodes[i]["symbolSize"] = G_main.degree[name0] / 10.0
links = [{"source": "", "target": ""} for i in range(G_main.number_of_edges())]
for i,(u,v) in enumerate(G_main.edges):links[i]["source"] = ulinks[i]["target"] = vlinks[i]["value"] = G_main[u][v]["weight"]graph = Graph("三国人物关系力导引图")
graph.add("", nodes, links)
graph.render("./images/三国人物关系力导引图.html")
graph

博客上不能显示交互式图表,这里就给出截图:显示了刘备的邻接结点

整个网络错综复杂,背后是三国故事中无数的南征北伐、尔虞我诈。不过有了计算机的强大算力,我们依然可以从中梳理出某些关键线索,比如:

人物排名-重要性

对这个问题,我们可以用网络中的排序算法解决。

PageRank就是这样的一个典型方法,它本来是搜索引擎利用网站之间的联系对搜索结果进行排序的方法,不过对人物之间的联系也是同理。让我们获得最重要的20大人物:

        page_ranks = pd.Series(nx.algorithms.pagerank(G_global)).sort_values()
page_ranks.tail(20).plot(kind="barh")
plt.show()

《三国演义》当仁不让的主角就是他们了,哪怕你对三国不熟悉,也一定会对这些人物耳熟能详。

人物排名-权力值

这个问题看上去跟上面一个问题很像,但其实还是有区别的。就像人缘最好的人未必是领导一样,能在团队中心起到凝聚作用,使各个成员相互联系合作的人才是最有权力的人。

中心度就是这样的一个指标,看看三国中最有权力的人是哪些吧?

        between = pd.Series(nx.betweenness_centrality(G_global)).sort_values()
between.tail(20).plot(kind="barh")
plt.show()

结果的确和上面的排序有所不同,我们看到刘备、曹操、孙权、袁绍等主公都名列前茅。

而另一个有趣的发现是,司马懿、司马昭、司马师父子三人同样榜上有名,而曹氏的其他后裔则不见其名,可见司马氏之权倾朝野。司马氏之心,似乎就这样被大数据揭示了出来!

社群发现

人物关系有亲疏远近,因此往往会形成一些集团。社交网络分析里的社区发现算法就能够让我们发现这些集团,让我使用community库[2]中的提供的算法来揭示这些关系吧。

        import community                                    # python-louvain
partition = community.best_partition(G_main)         # Louvain算法划分社区
comm_dict = defaultdict(list)
for person in partition:comm_dict[partition[person]].append(person)

在下面3个社区里,我们看到的主要是魏蜀吴三国重臣们(只有一些小“问题”,有趣的是,电脑并不知道他们的所属势力,只是使用算法)。

        draw_community(2)

        community 2: 张辽 曹仁 夏侯惇 徐晃 曹洪 夏侯渊 张郃 许褚 乐进 李典 于禁
荀彧 刘晔 郭嘉 满宠 程昱 荀攸 吕虔 典韦 文聘 董昭 毛玠

        draw_community(4)

        community 4: 曹操 诸葛亮 刘备 关羽 赵云 张飞 马超 黄忠 许昌 孟达[魏] 孙乾曹安民 刘璋 关平 庞德 法正 伊籍 张鲁 刘封 庞统 孟获 严颜 马良 简雍 蔡瑁 陶谦 孔融 刘琮[刘表子] 刘望之 夏侯楙 周仓 陈登

        draw_community(3)

        community 3: 孙权 孙策 周瑜 陆逊 吕蒙 丁奉 周泰 程普 韩当 徐盛 张昭[吴] 马相 黄盖[吴] 潘璋 甘宁 鲁肃 凌统 太史慈 诸葛瑾 韩吴郡 蒋钦 黄祖 阚泽 朱桓 陈武 吕范draw_community(0)

        community 0: 袁绍 吕布 刘表 袁术 董卓 李傕 贾诩 审配 孙坚 郭汜 陈宫 马腾
袁尚 韩遂 公孙瓒 高顺 许攸[袁绍] 臧霸 沮授 郭图 颜良 杨奉 张绣 袁谭 董承
文丑 何进 张邈[魏] 袁熙

还有一些其他社区。比如在这里,我们看到三国前期,孙坚、袁绍、董卓等主公们群雄逐鹿,好不热闹。

        draw_community(1)

        community 1: 司马懿 魏延 姜维 张翼 马岱 廖化 吴懿 司马昭 关兴 吴班 王平
邓芝 邓艾 张苞[蜀] 马忠[吴] 费祎 谯周 马谡 曹真 曹丕 李恢 黄权 钟会 蒋琬司马师 刘巴[蜀] 张嶷 杨洪 许靖 费诗 李严 郭淮 曹休 樊建 秦宓 夏侯霸 杨仪高翔 张南[魏] 华歆 曹爽 郤正 许允[魏] 王朗[司徒] 董厥 杜琼 霍峻 胡济 贾充彭羕 吴兰 诸葛诞 雷铜 孙綝 卓膺 费观 杜义 阎晏 盛勃 刘敏 刘琰 杜祺 上官雝 丁咸 爨习 樊岐 曹芳 周群

这个社区是三国后期的主要人物了。这个网络背后的故事,是司马氏两代三人打败姜维率领的蜀汉群雄,又扫除了曹魏内部的曹家势力,终于登上权力的顶峰。

动态网络

研究社交网络随时间的变化,是个很有意思的任务。而《三国演义》大致按照时间线叙述,且有着极长的时间跨度,顺着故事线往下走,社交网络会发生什么样的变化呢?

这里,我取10章的文本作为跨度,每5章记录一次当前跨度中的社交网络,就相当于留下一张快照,把这些快照连接起来,我们就能够看到一个社交网络变化的动画。

快照还是用networkx得到,而制作动画,我们可以用moviepy。

江山代有才人出,让我们看看在故事发展的各个阶段,都是哪一群人活跃在舞台中央呢?

        import moviepy.editor as mpy
from moviepy.video.io.bindings import mplfig_to_npimage
width, step = 10,5
range0 = range(0,len(G_chapters)-width+1,step)
numFrame, fps = len(range0), 1
duration = numFrame/fps
pos_global = nx.spring_layout(G_main)def make_frame_mpl(t):i = step*int(t*fps)G_part = nx.Graph()for G0 in G_chapters[i:i+width]:for (u,v) in G0.edges:if G_part.has_edge(u,v):G_part[u][v]["weight"] += G0[u][v]["weight"]else:G_part.add_edge(u,v,weight=G0[u][v]["weight"])largest_comp = max(nx.connected_components(G_part), key=len)used_nodes = set(largest_comp) & set(G_main.nodes)G = G_part.subgraph(used_nodes)fig = plt.figure(figsize=(12,8),dpi=100)nx.draw_networkx_nodes(G,pos_global,node_size=[G.degree[x]*10 for x in G.nodes])
#     nx.draw_networkx_edges(G,pos_global)nx.draw_networkx_labels(G,pos_global)plt.xlim([-1,1])plt.ylim([-1,1])plt.axis("off")plt.title(f"第{i+1}到第{i+width+1}章的社交网络")return mplfig_to_npimage(fig)
animation = mpy.VideoClip(make_frame_mpl, duration=duration)animation.write_gif("./images/三国社交网络变化.gif", fps=fps)

美观起见,动画中省略了网络中的边。

随着时间的变化,曾经站在历史舞台中央的人们也渐渐地会渐渐离开,让人不禁唏嘘感叹。正如《三国演义》开篇所言:

古今多少事,都付笑谈中。

今日,小辈利用Python做的一番笑谈也就到此结束吧……

作者简介:blmoistawinde,,西南某高校学森一枚,喜欢有意思的数据挖掘分析。希望给世界带来些清新空气~

Python 分析《三国演义》看司马懿三父子如何用计谋干掉了曹操后代相关推荐

  1. python 编程该看那些书籍_我用python5年后,我发现学python编程必看的三本书!

    非常喜欢python 我非常喜欢python,在前几年里,它一直是我热衷使用并不断研究的语言,迄今为止,python都非常友好并且易于学习! 它几乎可以做任何事,从简单的脚本创建.web,到数据可视化 ...

  2. python可视化案例书籍推荐_我用python5年后,我发现学python编程必看的三本书!...

    非常喜欢python 我非常喜欢python,在前几年里,它一直是我热衷使用并不断研究的语言,迄今为止,python都非常友好并且易于学习! 它几乎可以做任何事,从简单的脚本创建.web,到数据可视化 ...

  3. 斯坦福大学教授,推荐Python入门必看的三本书,非常适合零基础

    前言 Python 是一个高层次的结合了解释性.编译性.互动性和面向对象的脚本语言. Python 的设计具有很强的可读性,相比其他语言经常使用英文关键字,其他语言的一些标点符号,它具有比其他语言更有 ...

  4. python分析pdf年报 货币现金_如何用Python从大量pdf 中提取表格中的数据进行分析?...

    根据一楼答案@森林的建议 说说我的处理经验 我也是借助开源项目tabula,不得不说tabula的功能确实很强大. 我是用Python来处理数据,但是没有用tabula-py,因为表格跨列跨行等情况比 ...

  5. python官方推荐的三本书-【数据分析】入门数据分析,你一定要看的三本书

    原标题:[数据分析]入门数据分析,你一定要看的三本书 最近经常被问到怎么入门数据分析,可能很多同学对怎么开始学习还是比较困惑的.我回想自己学习数据分析的经历,总结了一些建议,希望能给到大家帮助. 打好 ...

  6. 多渠道归因分析:python实现马尔可夫链归因(三)

    本篇主要是python实现马尔科夫链归因,关联的文章: 多渠道归因分析(Attribution):传统归因(一) 多渠道归因分析:互联网的归因江湖(二) 多渠道归因分析:python实现马尔可夫链归因 ...

  7. 0基础学python看什么书-编程语言学python必看这三本书,少走一半弯路

    非常喜欢python 我非常喜欢python,在前面5年里,它一直是我热衷使用并不断研究的语言,迄今为止,python都非常友好并且易于学习! 它几乎可以做任何事,从简单的脚本创建.web,到数据可视 ...

  8. python福利彩随机_看大神如何用Python分析福利彩票的秘密,百万大奖不是梦!

    相信福利彩票大家都买过吧,既然学爬虫为何不动手写个小程序抓一下呢,说不定还能发现福彩的秘密,本篇主要面向于对Python爬虫感兴趣的零基础的同学,实例为中彩网的福利3D彩票,比较有趣,小伙伴们一起动手 ...

  9. 三国演义python分析系统_用python分析四大名著之三国演义

    点击上方"程序人生",选择"置顶公众号" 第一时间关注程序猿(媛)身边的故事 项目起因及意义 起初在浏览知乎时看见一篇文章觉得很有意思(用 Python 分析& ...

  10. 用Python分析了5万条相亲网站数据,看相亲男女画像

    这短短的一生,我们最终都会失去.你不妨大胆一些,爱一个人,攀一座山,追一个梦. 一.前言 数据来源:https://www.zhenai.com/zhenghun/ 本文利用 Python 分析了按城 ...

最新文章

  1. 跨模态学习在三维语义分割领域适应中的应用
  2. ***清除工具的进化
  3. python 识别图形验证码_Python验证码识别
  4. 达瓦里氏,星星之火已经可以燎原
  5. 程序员 35 岁危机很慌?看 CSDN 创始人蒋涛 12 分钟道破真相!
  6. Oracle 函数大全(字符串函数,数学函数,日期函数,逻辑运算函数,其他函数)...
  7. 树莓派指定python2编译_在树莓派上编译安装ROS2
  8. 如何通过U盘等外部设备启动带有T2芯片的Mac?
  9. 虚拟机安装教程win7_VMware15虚拟机软件安装教程
  10. python代码控制机械臂_Dobot 机械臂
  11. 案例二——网页倒计时(秒杀)
  12. java md5加密64位_MD5加密的Java实现
  13. java规则计算_亲属计算规则算法--java实现(关键算法摘要)
  14. c# API串口通信
  15. h0131. 钞票 (15 分)
  16. Android 10 手机端关闭蓝牙再打开,蓝牙不会自动回连设备蓝牙
  17. bat执行cmd命令
  18. linux bash环境下面给expect脚本传递参数
  19. 一文玩转 Java 日志数据脱敏
  20. pip 清华园下载 Opencv

热门文章

  1. Java之批量分卷压缩与解压缩实现
  2. 人工智能 4.不确定性推理方法
  3. Flexsim Rack设置最底层Level不放货物
  4. Python学习笔记(5),Battleship 游戏
  5. 常用的正则字母大小写转换
  6. 什么是透明数据加密(TDE)?
  7. 计算机网络的组成及其逻辑结构
  8. Android花样Text设置神器之SpanableString
  9. 计算机绘画小房子教案,中班绘画《房子》教案
  10. 支付宝接口http请求及sign加密