前不久看了一部令我为之震撼的电影《流浪地球》,这部电影让我看到了国产科幻影视的曙光,在满怀激动的心情下借着这部电影对其中人物关系实现提取和绘制关系图。
项目开始前,我们需要了解几个概念才能对我们需要实现的目标有一个大致实现流程上的思路。
很多同学对共现网络这个概念并不是十分了解,我们在网上搜索共现图。借用两张展示,大致如下这样:
现在大家应该对共现图有一个大致的了解了。其实这就是文献计量学中关键词的共词方法,通常会使用这种方法来确定某文献集中各个学科主题概念之间的关系。这里我们通过《流浪地球》的剧本,来对各个人物之间的关系进行分析,这就是共现关系。
共现网络
其实就是由一组节点(Node)
和一组边(Edge)
组成的,所以我们再绘制关系图之前需要先去获取这组节点和边。每个节点都会有权重,并每两个节点之间会通过一条边进行连接,这条连接线也会拥有权重。低权重的边缘通常都是冗余的,就好比电影中有主配角之分,配角本身可以看做是一个权重很低节点Node
,而配角和其他角色之间的关联关系由于配角的不重要性所以也会变得十分低但却存在的关联关系,所以我们需要通过适当的方法去减少这种冗余的情况。这里我们使用的是一种比较容易理解的方法:过滤,我们可以通过设定阈值去过滤那些重量低的边缘。另外一种就是细分网络,这种方法大家可以自己去翻阅资料了解。
实体间的共现
是一种基于统计的信息提取。我们往往在阅读一篇文章或者观看一个影片、电视时,当同一时间段内同时出现多个人物我们就会认为他们之间存在着某种关联,和我们所了解的共现关系理念相似,所以我们这里的思路就是基于共现关系去分析人物之间的关系。
这里我们会对《流浪地球》电影的剧本进行分析,由于我们需要提取段落中的人物,这里选择的是
jieba
库。至于为什么选择这个分词库,好用就完了。不过还是比较准确的,大家可以深入去了解他的优缺点和使用方法。
Gephi
这是一款使用Java开发的复杂网络分析软件,这里我们用于最后共现关系图的绘制,稍后会稍微详细一点进行讲解。
上面我们也分析了在阅读一篇文章或者观看一个影片、电视时,当同一时间段内同时出现多个人物我们就会认为他们之间存在着某种关联,这也是我们实现共现关系的一个核心思想。我们先将文章以段落分隔,每个段落通过分词将其中出现的人物进行一个记录和统计,并会通过简单的算法将每两个人物之间构建一个关联关系,其实也就是通过共现关系构造任意两点的边。再生成指定格式化的文本数据通过
Gephi
对其进行人物关系绘制。
这里我们首先下载准备好的《流浪地球》中文剧本以及我们通过百科整理好的所需的【流浪地球主要人物列表】。如果大家想练手需要剧本可以去中国编剧网上找自己需要的资料。
这里我们首先去初始化了几个变量。首先使用字典names
用于保存人物,key为人物名称,value为该人物出现的次数;字典relationships
用于保存人物之间的关联系,即人物关系有向边,key为有向边的起点,value则也是一个字典edge
。在edge
这个字典中key则是有向边的终点,value代表的是当前有向边的权值,可以理解为两个人物之间的紧密程度。lineNames
则是一个缓存变量,用于保存每一段分词中出现的人物信息,lineNames[i]
即表示第i
段出现的所有人物。
import codecs
import jieba
import os
from jieba import posseg
class Earth(object):
# 初始化
def __init__(self):
# 姓名字典
self.names = {}
# 关系字典
self.relationships = {}
# 每段内人物关系
self.lineNames = []
这里我们会去读取《流浪地球》剧本中的每一行,然后通过
jieba
对其进行分词。我们之前在person.txt
中会预先配置好所需的人物名称
、权重
以及自定义词性
,通过分词可以确定当前分词的词性是否是我们所需的人物,并会标记为nr
词性。这里我们会先通过词性nr
以及单词长度是否小于2去过滤我们分析中不需要词集。然后我们会将有效人物词集放入到lineNames
缓存变量中, 并且会更新names
中该人物出现的次数。
# 分词方法
def analyze_word(self):
# 加载字典
jieba.load_userdict(os.path.abspath(os.curdir) + "/resources/person.txt")
with codecs.open(os.path.abspath(os.curdir) + "/resources/The Wandering Earth.txt", "r", "utf-8") as f:
for line in f.readlines():
# 分词返回词性
poss = posseg.cut(line)
# 为新读取的一段添加人物关系
self.lineNames.append([])
for w in poss:
# print("%s:%s" % (w.word, w.flag))
if w.flag != "nr" or len(w.word) < 2:
# 分词长度小于2 或词性不为nr时则与影片所需分析人物无关
continue
self.lineNames[-1].append(w.word)
if self.names.get(w.word) is None:
self.names[w.word] = 0
self.relationships[w.word] = {}
# 人物出现次数+1
self.names[w.word] += 1
上面我们将每段中出现的人物信息存放在了
lineNames
缓存变量中,这里我们对该变量中信息进行分析,将每行中所出现的人物进行两两关联。若两个人物之间尚不存在关联边,则将新建关联边权值置为1,;若已存在,则在原有权值基础上加1。
# 分析人物关系
def analyze_relationship(self):
for line in self.lineNames:
for name1 in line:
for name2 in line:
if name1 == name2:
continue
if self.relationships[name1].get(name2) is None:
# 两个人物第一次共同出现 初始化次数
self.relationships[name1][name2] = 1
else:
# 两个人物共同出现 关系+1
self.relationships[name1][name2] += 1
这里我们通过已经分析提取好的
names
以及relationships
中的数据,按照一定的格式化生成两个我们所需的节点表格文件earth_node.csv"
以及边表格文件earth_edge.csv
。并且在生成文件时,会通过边权重过滤冗余边缘数据。至于为什么按照这种格式分析和生成,和Gephi
的标准化导入有关,这个后面会稍作介绍。
# 写csv文件 用于网络图使用
def generate_gephi(self):
# 人物权重(节点)
with codecs.open(os.path.abspath(os.curdir) + "/resources/earth_node.csv", "w", "gbk") as f:
f.write("Id Label Weight\r\n")
for name, times in self.names.items():
f.write(name + " " + name + " " + str(times) + "\r\n")
# 人物关系边(边)
with codecs.open(os.path.abspath(os.curdir) + "/resources/earth_edge.csv", "w", "gbk") as f:
f.write("Source Target Weight\r\n")
for name, edge in self.relationships.items():
for v, w in edge.items():
if w > 3:
f.write(name + " " + v + " " + str(w) + "\r\n")
首先介绍一下
Gephi
,这是一款开源免费跨平台基于JVM的复杂网络分析软件,,主要用于各种网络和复杂系统,动态和分层图的交互可视化与探测开源工具。我们这里就是通过这款软件对人物关系进行绘制。更详细深入的了解使用大家有需要可以自行了解。
这里我们可以通过【Gephi官网】或者【Gephi Github】下载所需对应系统和版本的
Gephi
,这里需要注意的是由于该软件是使用Java
开发的,所以我们需要事先安装配置好JDK。Linux
环境直接解压即可,Windows
环境直接安装也十分方便。
我们打开
Gephi
,整个界面大致就是下面这个样子了。
此时我们点击左上角文件
->导入电子表格
导入我们准备好的node
节点数据。这里主要注意的就是需要选择节点表格
以及字符集选择GB2312
,之后一直点完成即可。
这里我们继续导入我们准备好的edge
边表格数据。同样这里需要设置为边表格
以及GB2312
。这里另外一个需要注意的就是下面第二张图上的,需要选择追加而不是新建。
此时
Gephi
会将我们导入的数据展示在我们面前,这里面包含了我们所有的节点并且用边相连起来。这里若没有出现图这个模块的可以自己通过工具栏上的窗口
选项将图
显示出来。
这个时候可以看到这只是一个雏形,并没有任何的布局,我们也无法从中观察到我们想要得到的结果。这里就需要我们来调整整个预览效果了。
首先我们看到右侧有一个上下文的模块,我们找到平均度
以及模块化
点击运行。在模块化运行时,我们将解析度设置为0.5.
接下来我们看到左侧外观栏中点击颜色
->Partition
,将其设置为Modularity Class
,点击应用。
点击大小
->Ranking
选择连入度,最小值设置为10,最大值设置为40,点击应用。
此时可以看到节点的大小和颜色都发生了变化。
这里我们可以看到许多没有强密度关联的散点也会集中在这块区域,我们决定将这些关联密度低的节点分隔开。这里找到左下角的布局
选择Force Atlas
。然后将斥力强度调整为10000,吸力强度设置为1。
点击运行,渲染效果就变成下面这样了,一些散点因为斥力强度过大被排斥到一边去了。
此时大家可以看到还有一些不完美,我们点击上方的预览
,找到设置中节点标签
下的显示标签
以及字体
进行调整。
设置好之后点击下方的刷新,此时展现在我们眼前的人物关系图就已经大功告成了。这里如果大家觉得整张图关系太过拥挤,可以调节斥力强度
、吸力强度
使其关系显示更加清晰。还可以通过调节预览
等其他各种配置使最终生成的关系图满足自己的条件。
emsp;细心的同学可以发现在最上面还有数据资料一栏,我们可以通过这一栏去自由地修改我们的数据,例如可以手动清除某些我们仍然认为冗余的数据。
另外可以看到节点
以及边
最上面的一列标签Id Label Weight
、Source Target Weight
,大家是否觉得有些熟悉?没错,这就是为什么我们在开始生成CSV
文件时需要遵循一定标准的原因了。整个《流浪地球》人物关系图的绘制大致就是这样的一个流程,还有更多更丰富更细节的地方就要靠大家根据所需去进行深入研究了。另附上一篇关于【Gephi网络图极简教程】,由于我们这里只是讲解了对Gephi
这款软件的简单使用,没有过多介绍,大家有兴趣可以自己去了解。