众所周知在《三国演义》中随着时间的变化出场了很多的人物,因此也可以利用一些文本挖掘方式,分析书中的人物关系等。下面将会利用文本分析方法,观察书中人物的出场情况和人物之间的关系。
1 人物重要性时序分析
先介绍一些关键人物随着章节的推进,他们的出场频次随着时间的变化情况。先读取“一些三国人物的名和字.csv”和“三国演义.txt”文件,该数据中主要包含书中不同阵营的关键人物的名称和字等信息。
## 输出高清图像
%config InlineBackend.figure_format = 'retina'
%matplotlib inline
## 图像显示中文的问题
import matplotlib
matplotlib.rcParams['axes.unicode_minus']=False
import seaborn as sns
sns.set(font= "Kaiti",style="ticks",font_scale=1.4)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import networkx as nx
import altair as alt
## 忽略提醒
import warnings
warnings.filterwarnings("ignore")
## 读取三国一些三国人物的名和字数据
TK_name = pd.read_csv("data/三国演义/一些三国人物的名和字.csv")
print(TK_name.head())
名 字 阵营
0 曹操 孟德 曹魏
1 曹丕 子桓 曹魏
2 司马懿 仲达 曹魏
3 荀彧 文若 曹魏
4 荀攸 公达 曹魏
## 读取三国演义数据
TK_df = pd.read_csv("data/三国演义/三国演义.txt",sep="\t")
TK_df.head(5)
Name content
0 宴桃园豪杰三结义,斩黄巾英雄首立功 滚滚长江东逝水,浪花淘尽英雄。是非成败转头空。青山依旧在,几度夕阳红。白发渔樵江渚上,惯看秋...
1 张翼德怒鞭督邮,何国舅谋诛宦竖 且说董卓字仲颖,陇西临洮人也,官拜河东太守,自来骄傲。当日怠慢了玄德,张飞性发,便欲杀之。玄...
2 议温明董卓叱丁原,馈金珠李肃说吕布 且说曹操当日对何进曰:“宦官之祸,古今皆有;但世主不当假之权宠,使至于此。若欲治罪,当除元恶...
3 废汉帝陈留践位,谋董贼孟德献刀 且说董卓欲杀袁绍,李儒止之曰:“事未可定,不可妄杀。”袁绍手提宝剑,辞别百官而出,悬节东门,...
4 发矫诏诸镇应曹公,破关兵三英战吕布 却说陈宫临欲下手杀曹操,忽转念曰:“我为国家跟他到此,杀之不义。不若弃而他往。”插剑上马,不...
想要分析每个人物的出场情况,就需要计算在每个章节中,感兴趣的人出场频次等信息,下面的程序中,则是计算每个人物在每章节出现的次数,计算时将出现的名字和字各作为一次出现,需要注意的是,计算时根据分词前的文本内容进行计算,最终的计算结果将会转化为数据表格。最终的输出数据表中,行表示对应的章节,列表示人物名称,对应的数值为人物在相应章节出现的次数。
## 计算一个人物名称在每个章节出现的次数
TK_name_time = []
for ii in np.arange(len(TK_name)):
times = []
name = TK_name.iloc[ii,0] # 获取要计算的字
zi = TK_name.iloc[ii,1] # 获取要计算的名
nametime = TK_df.content.apply(func = lambda x: x.count(name))
zitime = TK_df.content.apply(func = lambda x: x.count(zi) if pd.isnull(zi)== False else 0)
times = nametime.values + zitime.values
TK_name_time.append(times)
## 计算结果设计为数据表
TK_name_timedf = pd.DataFrame(data = np.array(TK_name_time).T,
columns = TK_name.iloc[:,0])
TK_name_timedf
针对获得的数据表TK_name_timedf,可以使用热力图对其进行可视化分析,分析所有人的出场整体趋势,运行下面的程序可获得如图1所示的热力图。
In[3]:## 使用热力图可视化每个人出现的次数
plt.figure(figsize=(20,12))
ax = sns.heatmap(TK_name_timedf.T,annot=False,cmap="YlGnBu",
yticklabels=True,xticklabels=True)
ax.set_yticklabels(TK_name_timedf.columns, fontsize = 10)
ax.set_xticklabels(TK_name_timedf.index+1, fontsize = 10)
plt.title("三国演义中每个人在各章节的出现次数")
plt.show()
图1 人物出场次数热力图
从热力图中可以发现:出场次数较多都是刘备和诸葛亮两人,其中刘备前期出场较多,诸葛亮后期出场较多。
针对上面的数据可以使用蒸汽图可视化一些关键人物的出场情况,下面的程序中则是使用可交互的蒸汽图,可视化出"曹操"、"曹丕"、"刘备"、"刘禅"、"孙策"、"孙权"6个人的出场情况,运行程序后可获得如图2所示的图像。
## 使用蒸汽图可视化一些重要人物的出场情况
plotname = ["曹操","曹丕","刘备","刘禅","孙策","孙权"]
plotdata = TK_name_timedf[plotname]
plotdata["chap"] = np.arange(1,121)
## 转化为长数据
plotdata = plotdata.melt(["chap"], var_name="name",value_name="value")
## 使用可交互蒸汽图可视化
selection = alt.selection_multi(fields=["name"], bind="legend")
alt.Chart(plotdata).mark_area().encode(
alt.X("chap:Q"), ## X轴
alt.Y("value:Q", stack="center",axis=None), ## Y轴
alt.Color("name:N",scale=alt.Scale(scheme="category20c")), ##设置颜色
opacity=alt.condition(selection, alt.value(1), alt.value(0.2)),
).properties(width=800,height=400).add_selection(selection)
图2 蒸汽图可视化
需要注意的是图2是可交互的图像,可以通过鼠标点击选择一个人物的数据进行可视化分析,例如:刘备的出场情况可视化图像如3所示。
图3 蒸汽图可视化(刘备)
2 人物关系可视化分析
分析人物之间的关系,可以以根据前面计算得到的每个人在书中的出场情况,获取人物之间的相关系数,下面的程序中则是将相关系数使用热力图进行可视化,运行程序后可获得如图4所示的图像。通过热力图可以很方便的分析不同人物之间的相关性。
## 根据人物的出场情况计算他们之间的相关系数
Tkcor = TK_name_timedf.corr(method="pearson")
## 相关系数热力图
plt.figure(figsize=(20,18))
ax = sns.heatmap(Tkcor,square=True,annot=False,
linewidths=.5,cmap="YlGnBu",
cbar_kws={"fraction":0.046, "pad":0.03},
yticklabels=True,xticklabels=True)
ax.set_title("人物相关性")
plt.show()
图4 人物相关性热力图
针对人物之间的关系,也可以使用社交网络图可视化分析人物之间的关系,下面的程序中是根据相关系数的大小,构建人物之间的关系,并且只保留相关系数绝大值大于0.3的人物关系,从输出结果中可以发现最终只有100条边被保留。
## 相关系数矩阵的下三角取值定义为NaN
Tkcor = Tkcor.where(np.triu(np.ones(Tkcor.shape),k = 1).astype(np.bool))
## 宽数据转化为长数据
Tkcor.columns.name = "start"
Tkcor.index.name = "end"
Tkcorlong = Tkcor.unstack().reset_index()
Tkcorlong.columns = ["start","end","weight"]
## 剔除NaN的数据
Tkcorlong = Tkcorlong[~Tkcorlong["weight"].isna()]
## 去除相关系数的绝对值小于0.3的数据
Tkcorlong = Tkcorlong[Tkcorlong["weight"] > 0.3]
Tkcorlong = Tkcorlong.reset_index(drop = True)
print("Tkcorlong.shape",Tkcorlong.shape)
Tkcorlong.shape (100, 3)
针对获得的Tkcorlong数据表,可以使用下面的程序获得人物之间的关系网络图,如图5所示。
## 使用有向图可视化人物之间的关系
plt.figure(figsize=(14,12))
## 生成社交网络图
G=nx.DiGraph()
## 添加边
for ii in Tkcorlong.index:
G.add_edge(Tkcorlong.start[ii], Tkcorlong.end[ii],
weight = Tkcorlong.weight[ii])
## 定义正相关和负相关两种边
big=[(u,v) for (u,v,d) in G.edges(data=True) if d["weight"] >0.5]
small=[(u,v) for (u,v,d) in G.edges(data=True) if d["weight"] <0.5]
## 图的布局
pos=nx.kamada_kawai_layout(G)
nx.draw_networkx_nodes(G,pos,alpha=0.6,node_size=500)
nx.draw_networkx_edges(G,pos,edgelist=big,
width=1.5,alpha=0.6,edge_color="r",arrowsize=20)
nx.draw_networkx_edges(G,pos,edgelist=small,
width=1,alpha=0.8,edge_color="b",arrowsize=20)
nx.draw_networkx_labels(G,pos,font_size=10,font_family = "Kaiti")
plt.axis("off")
plt.title("《三观演义》人物关系")
plt.show()
图5 关键人物之间的关系
在图像5中正相关性使用红色的箭头表示,负相关性使用蓝色的箭头表示。通过图可以更方便的分析两人之间的关系。
针对获得的关系网络图也可以使用节点的度表示每个人物在网络中的重要程度,其中度越大说明其重要性越高。运行下面的程序可获得如图6所示的节点度分布条形图。
## 计算每个节点的度,分析人物的重要成程度
Tk_degree = pd.DataFrame(list(G.degree))
Tk_degree.columns = ["name","degree"]
Tk_degree = Tk_degree.sort_values(by="degree",ascending=False)
## 可视化
Tk_degree.plot(kind = "bar",x="name",y = "degree",
figsize=(12,7),legend=False)
plt.xticks(size = 12)
plt.ylabel("度")
plt.xlabel("")
plt.title("有向图的节点度分布")
plt.show()
图6 网络图中节点度分布条形图
从图6中可以发现甘宁、曹操、张鲁等人的节点度较高,说明他们在网络图中的重要性较大。
发表评论