参考链接:
文本可视化[二]——《今生今世》人物关系可视化python实现
绘制《玩偶之家》中的人物关系网
代码实现分为三步
人物出场次数统计
对文本进行针对性分词,需要准备两份特殊词典
(1)
关心的人物词典:
海尔茂 10 nr
托伐 10 nr
娜拉 10 nr
爱伦 10 nr
安娜 10 nr
阮克 10 nr
林丹 10 nr
克立斯替纳 10 nr
柯洛克斯泰 10 nr
伊娃 10 nr
爱密 10 nr
巴布 10 nr
尼尔 10 nr
(2)
人物别称映射词典:
海尔茂 托伐
海尔茂先生 托伐
鸟儿 娜拉
小鸟儿 娜拉
小松鼠儿 娜拉
海尔茂太太 娜拉
阮克大夫 阮克
阮克医生 阮克
林丹 克立斯替纳
尼尔 柯洛克斯泰
人物关系统计
格式化输出。
实现代码:
# -*- coding: GBK -*-
from __future__ import print_function
"""
人物关系网
"""
import jieba
import codecs
from collections import defaultdict
#定义文件路径常量和初始化全局变量
TEXT_PATH = 'F:/geany_python_codes/analysis_py/textminingprojects/doll_house.txt' # 文本路径
DICT_PATH = 'F:/python35/Lib/site-packages/jieba/dict_doll_house_person.txt' # 人物词典路径,保存我们关心的人物
SYNONYMOUS_DICT_PATH = 'F:/python35/Lib/site-packages/jieba/synonymous_dict.txt' # 同义词词典路径,保存人物别称
SAVE_NODE_PATH = 'F:/geany_python_codes/analysis_py/textminingprojects/node.csv' #输出的节点数据
SAVE_EDGE_PATH = 'F:/geany_python_codes/analysis_py/textminingprojects/edge.csv' #输出的边数据
class RelationshipView:
def __init__(self, text_path, dict_path, synonymous_dict_path):
self._text_path = text_path
self._dict_path = dict_path
self._synonymous_dict_path = synonymous_dict_path
'''
person_counter是一个计数器,用来统计人物出现的次数。{'a':1,'b':2}
person_per_paragraph每段文字中出现的人物[['a','b'],[]]
relationships保存的是人物间的关系。key为人物A,value为字典,包含人物B和权值。
'''
self._person_counter = defaultdict(int)
self._person_per_paragraph = []
self._relationships = {}
self._synonymous_dict = {}
def generate(self):
self.count_person()
self.calc_relationship()
self.save_node_and_edge()
def synonymous_names(self):
'''
获取同义名字典
:return:
'''
with codecs.open(self._synonymous_dict_path, 'r', 'utf-8') as f:
lines = f.read().split('\r\n')
for l in lines:
self._synonymous_dict[l.split(' ')[0]] = l.split(' ')[1]
return self._synonymous_dict
def get_clean_paragraphs(self):
'''
以段为单位分割全文
:return:
'''
with codecs.open(self._text_path, 'r', 'utf-8') as f:
paragraphs = f.read().split('\r\n\r\n')
return paragraphs
def count_person(self):
'''
统计人物出场次数,添加每段的人物
:return:
'''
paragraphs = self.get_clean_paragraphs()
synonymous = self.synonymous_names()
print('start process node')
with codecs.open(self._dict_path, 'r', 'utf-8') as f:
name_list = f.read().split(' 10 nr\r\n') # 获取干净的name_list
for p in paragraphs:
jieba.load_userdict(self._dict_path)
# 分词,为每一段初始化新字典
poss = jieba.cut(p)
self._person_per_paragraph.append([])
for w in poss:
# 判断是否在姓名字典以及同义词区分
if w not in name_list:
continue
if synonymous.get(w):
w = synonymous[w]
# 往每段中添加人物
self._person_per_paragraph[-1].append(w)
# 初始化人物关系,计数
if self._person_counter.get(w) is None:
self._relationships[w] = {}
self._person_counter[w] += 1
return self._person_counter
def calc_relationship(self):
'''
统计人物关系权值
:return:
'''
print("start to process edge")
for p in self._person_per_paragraph:
for name1 in p:
for name2 in p:
if name1 == name2:
continue
if self._relationships[name1].get(name2) is None:
self._relationships[name1][name2] = 1
else:
self._relationships[name1][name2] += 1
return self._relationships
def save_node_and_edge(self):
'''
根据dephi格式保存为csv
:return:
'''
with codecs.open(SAVE_NODE_PATH, "a+", "utf-8") as f:
f.write("Id,Label,Weight\r\n")
for name, times in self._person_counter.items():
f.write(name + "," + name + "," + str(times) + "\r\n")
with codecs.open(SAVE_EDGE_PATH, "a+", "utf-8") as f:
f.write("Source,Target,Weight\r\n")
for name, edges in self._relationships.items():
for v, w in edges.items():
if w > 3:
f.write(name + "," + v + "," + str(w) + "\r\n")
print('save file successful!')
if __name__ == '__main__':
v = RelationshipView(TEXT_PATH, DICT_PATH, SYNONYMOUS_DICT_PATH)
v.generate()
随后可以将生成的csv文件导入到软件gephi中。
人物出场次数(节点数据)输出为格式为(Id,Label,Weight),人物关系(边数据)输出格式为(Source,Target,Weigh)。
gephi的使用
1.安装
2.使用
参考链接:
自然语言课程(五):使用gephi绘制人物关系图及案例分析
Gephi网络图极简教程
对上图的解释:
节点的大小代表了人物在小说中的重要程度(按照出场频率计算),节点越大该人物就越重要(出场频率越高)。
标签颜色的深浅代表了人物在小说中与其他人出现交集的频率高低,标签颜色越深,则该标签所代表的人物与他人交集越多。
节点颜色的深浅代表了人物在小说中与其他人出现交集的频率高低,节点颜色越深,则该节点所代表的人物与他人交集越多。
边的粗细与长短代表了人物之间关系的亲疏,当边越粗、越短,则表示该边所连接的节点所代表的人物之间的关系越紧密。