思路
基于共现来挖掘人物之间的关系。
准备好三个存储器
- names 存放的是人物出现次数,用来后面刻画节点的大小。names类似’叶三姐’: 8, ‘马鞍’: 6,
- relationships 存放人物关系 {‘钱塘江’: {‘牛家村’: 1}, ‘牛家村’: {‘钱塘江’: 1, ‘武功’: 1, ‘王道乾’: 1, ‘郭啸天’: 1,}} 嵌套字典。
- lineNames 共现词 类似[[‘钱塘江’, ‘牛家村’], [‘柏树’, ‘叶子’], [], [‘萧索’, ‘松树’]]
思路不解释,自己看代码,这也是我学习之后写的。
看别人文字思路,不如看代码。毕竟文字思路多啦一道转换。
步骤1
import jieba.posseg as pseg
from tqdm import tqdm#设置一个进度条
# 姓名字典
names = {}
# 关系字典
relationships = {}
# 每段内人物关系
lineNames = []
#打开文件
with open('射雕英雄传.txt', 'r',encoding='utf-8') as fp:
for line in tqdm(fp):
line = line.strip('\n')#去除换行
poss = pseg.cut(line)# 分词返回词性
# 为新读取的一段添加人物关系
lineNames.append([])
for w in poss:#遍历每一个
# print("%s:%s" % (w.word, w.flag))
# 分词长度小于2 或词性不为nr时则与影片所需分析人物无关
if w.flag != "nr" or len(w.word) < 2:
continue
lineNames[-1].append(w.word)#当前段存放人物名
if names.get(w.word) is None:#如果姓名未出现过
names[w.word] = 0#当前姓名添加进names字典里
relationships[w.word] = {}#初始化该姓名关系图
# 人物出现次数+1
names[w.word] += 1
print('names\n',names)
print('relationships\n',relationships)
print('lineNames\n',lineNames)

步骤2
#分析人物关系
for line in lineNames:
for name1 in line:
for name2 in line:
if name1 == name2:
continue
if relationships[name1].get(name2) is None:
# 两个人物第一次共同出现 初始化次数
relationships[name1][name2] = 1
else:
# 两个人物共同出现 关系+1
relationships[name1][name2] += 1

步骤3
# 写csv文件 用于网络图使用
def generate_gephi():
# 人物权重(节点)
with open("earth_node.csv", "w", encoding='utf-8') as f:
f.write("Id Label Weight\r\n")
for name, times in names.items():
f.write(name + " " + name + " " + str(times) + "\r\n")
# 人物关系边(边)
with open("earth_edge.csv", "w", encoding='utf-8') as f:
f.write("Source Target Weight\r\n")
for name, edge in relationships.items():
for v, w in edge.items():
if w > 3:
f.write(name + " " + v + " " + str(w) + "\r\n")
generate_gephi()
得到的数据 是一列的


需要应用excel 技术将一列分成多列
如结果

画图
import pandas as pd
file1=pd.read_csv('earth_edge.csv',encoding='gbk')#人物关系
file1=file1.dropna()
import networkx as nx
from pylab import *
import matplotlib.pyplot as plt
mpl.rcParams['font.sans-serif'] = ['SimHei']
mpl.rcParams['axes.unicode_minus'] = False
def painting(): #绘制人物亲密度图
G = nx.Graph() # 绘制个人物之间的亲密关系
for index, row in file1.iterrows():
G.add_node(row['Source'])#添加节点
for index, row in file1.iterrows():
G.add_weighted_edges_from([(row['Source'],row['Target'],row['Weight'])])
pos = nx.shell_layout(G)
print('画出网络图像:')
nx.draw(G, pos, with_labels=True, node_color='white', edge_color='red', node_size=400, alpha=0.5)
plt.show()
painting()
人太多 ,图炸啦。
接下来我只绘制重要人物的图
import pandas as pd
import numpy as np
file=pd.read_csv('earth_node.csv',encoding='gbk')#人物权重文件
file=file.dropna()
file1=pd.read_csv('earth_edge.csv',encoding='gbk')#人物关系文件
file1=file1.dropna()#去空白
#主要人物
namelist = ['郭啸天','杨铁心','冷笑','武功','包惜弱','那道人','郭靖','杨康','李萍','段天德','完颜洪烈',
'柯镇恶','朱聪','韩宝驹','韩小莹','铁木真','梅超风','黄药师','尹志平','马钰','沙通天',
'黄蓉','穆念慈','洪七公','周伯通','欧阳锋','裘千仞']
#人物权重
node_size=[]#no人物权重
for name in namelist :
node_size.append(np.array(file.loc[file['Id'] == name, 'Weight'])[0])
import networkx as nx
from pylab import *
import matplotlib.pyplot as plt
mpl.rcParams['font.sans-serif'] = ['SimHei']
mpl.rcParams['axes.unicode_minus'] = False
def painting(): #绘制人物亲密度图
G = nx.Graph() # 绘制个人物之间的亲密关系
for index, row in file1.iterrows():
if row['Source'] in namelist:
G.add_node(row['Source']) # 添加节点# 将当前人物添加进节点
for index, row in file1.iterrows():
if (row['Source'] in namelist) & (row['Target'] in namelist):
#G.add_node(row['Source'], row['Target'])
G.add_weighted_edges_from([(row['Source'], row['Target'], 10*np.array(row['Weight']))])#添加权重
pos = nx.shell_layout(G)
print('画出网络图像:')
nx.draw(G, pos, with_labels=True, node_color=range(27), edge_color='red', node_size=node_size, alpha=0.5,width=[float(d['weight']*0.01) for (u,v,d) in G.edges(data=True)])
plt.show()
painting()

郭靖和黄蓉果然是真爱。
分析
本次 还有些缺陷。
如郭靖和黄蓉之间还有些昵称。如靖哥哥,蓉儿,未统计进来。(主要是我懒,懒得写多余代码,使他们都映射到同一个名字。如把靖哥哥、郭大哥、郭靖 全部映射到郭靖。)
作者:电气余登武