基于共现网络原理将剧本《人民的名义》人物关系社交网络可视化

本文借助于python,以《人民的名义》剧本为分析材料,基于共现网络原理,将剧本人物之间的共现关系重新整理发布,并实现人物社交网络可视化。该剧本非常适合文本处理,语言简洁,大致每一段对应一个关键情节。由于《人民的名义》人物少、关系简单,所以我们可以通过词典指定人物名称的方式做实体识别。也可以不建立字典并尝试使用某种分词算法或包装好的分词库(如使用jieba),但离开特定词典的针对特定文本的分词效果可能会有很大程度削弱。因此对简单网络而言,建立字典是效率较高的做法。

人物字典:

侯亮平 nr
李达康 nr
达康 nr
沙瑞金 nr
沙书记 nr
高育良 nr
育良 nr
老师 nr
祁同伟 nr
陆亦可 nr
钟小艾 nr
高小琴 nr
吴惠芬 nr
欧阳菁 nr
赵瑞龙 nr
郑西坡 nr
赵东来 nr
梁璐 nr
季昌明 nr
程度 nr
丁义珍 nr
陈海 nr
赵德汉 nr
林华华 nr
周正 nr
陈岩石 nr
陈老 nr
刘新建 nr
蔡成功 nr
王馥真 nr
易学习 nr
田国福 nr
陈清泉 nr
刘姗 nr
陈群芳 nr
田杏枝 nr
张宝宝 nr
郑胜利 nr
孙连城 nr
肖钢玉 nr
吴心怡 nr
王大路 nr
秦局长 nr
周桂春 nr
王文革 nr
白处长 nr
孙海平 nr
毛娅 nr
张树立 nr
吴春林 nr
马文明 nr
金秘书 nr
侯浩然 nr
常成虎 nr
小皮球 nr
汤成兰 nr
李梁 nr
毕敬业 nr
何阿三 nr
张天峰 nr
尤瑞星 nr
陈文强 nr
杜伯仲 nr
美女老总 nr
老程 nr

# -*- coding: utf-8 -*-
import jieba  # jiaba库
import codecs
import pandas as pd
import numpy as np
from PIL import Image
import jieba.posseg as pseg
import matplotlib.pyplot as plt
from wordcloud import WordCloud, ImageColorGenerator

# names        :  姓名字典,保存人物,键为人物名称,值为该人物在全文中出现的次数
# relationship :  关系字典,保存人物关系的有向边,键为有向边的起点,值为一个字典 edge ,edge 的键为有向边的终点,
#                 值是有向边的权值,代表两个人物之间联系的紧密程度
# lineNames    :  缓存变量,保存对每一段分词得到当前段中出现的人物名称关系
# node         :  存放处理后的人物
# stopwords    :  存放停用词
# replace_words:  替代词字典,保存要相互替代的词,键为被替代词,值为替代词
names = {}
relationships = {}
lineNames = []

一些词汇如“林城”、“白云”、“吕州”等,它们的属性被归到了“nr”,而且出现次数较多,和一些人物共现次数较多,如果不做处理的话,它们将会出现在绘制的关系图中。在这里可以借用列表类型stopwords将这些词作为停用词梳理。


stopwords = ['吕州', '林城', '银行卡', '明白', '白云', '嗡嗡嘤嘤',
             '阴云密布', '雷声', '陈大', '谢谢您', '安置费', '任重道远',
             '孤鹰岭', '阿庆嫂', '岳飞', '师生', '养老院', '段子', '老总']
replace_words = {'师母': '吴慧芬', '陈老': '陈岩石', '老赵': '赵德汉', '达康': '李达康', '高总': '高小琴',
                 '猴子': '侯亮平', '老郑': '郑西坡', '小艾': '钟小艾', '老师': '高育良', '同伟': '祁同伟',
                 '赵公子': '赵瑞龙', '郑乾': '郑胜利', '孙书记': '孙连城', '赵总': '赵瑞龙', '昌明': '季昌明',
                 '沙书记': '沙瑞金', '郑董': '郑胜利', '宝宝': '张宝宝', '小高': '高小凤', '老高': '高育良',
                 '伯仲': '杜伯仲', '老杜': '杜伯仲', '老肖': '肖钢玉', '刘总': '刘新建', '美女老总': '高小琴'}

读入《人民的名义》剧本的每一行,对其做分词(判断该词的词性是不是“人名”[词性编码:nr],如果该词的词性不为nr,则认为该词不是人名),提取该行(段)中出现的人物集,之后判断该人物集中的词是否是名词停用词、昵称,或者长度小于2,如果不是,就满足条件,存入lineName中。之后对出现的人物,更新他们在names中的出现次数

jieba.load_userdict("data/ccin/dict.txt")  # 加载字典
with codecs.open("data/ccin/renmin.txt", "r", "utf8") as f:
    for line in f.readlines():
        poss = pseg.cut(line)  # 分词并返回该词词性
        lineNames.append([])  # 为新读入的一段添加人物名称列表
        for w in poss:
            if w.word in stopwords:  # 去掉某些停用词,防止干扰人物关系
                continue
            if w.flag != "nr" or len(w.word) < 2:  # 判断当前词的词性和长度
                if w.word not in replace_words:
                    continue
            if w.word in replace_words:  # 将文中某些人物的昵称替换成正式的名字
                w.word = replace_words[w.word]
            lineNames[-1].append(w.word)  # 为当前段的环境增加一个人物
            if names.get(w.word) is None:  # 如果这个名字从来没有出现过,初始化这个名字
                names[w.word] = 0
                relationships[w.word] = {}
            names[w.word] += 1  # 该人物出现次数加1

对于 lineNames 中每一行,我们为该行中出现的所有人物两两相连。如果两个人物之间尚未有边建立,则将新建的边权值设为 1,否则将已存在的边的权值加 1。这种方法将产生很多的冗余边,这些冗余边将在最后处理。

    # explore relationships
    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:
                    relationships[name1][name2] += 1  # 两人共同出现次数加 1

将已经建好的 names 和 relationships 输出到文本,以方便 gephi 可视化处理。输出边的过程中可以过滤可能是冗余的边,这里假设共同出现次数少于 3 次的是冗余边,则在输出时跳过这样的边。输出的节点集合保存为 busan_node.txt和node.csv文件 ,边集合保存为 busan_edge.txt和edge.csv文件

# output_txt
# with codecs.open("data/ccout/renmin_node.txt", "w", "gbk") as f:
#     f.write("Id Label Weight\r\n")
#     for name, times in names.items():
#         f.write(name + " " + name + " " + str(times) + "\r\n")
#
# with codecs.open("data/ccout/renmin_edge.txt", "w", "gbk") as f:
#     f.write("Source Target Weight\r\n")
#     for name, edges in relationships.items():
#         for v, w in edges.items():
#             if w > 3:
#                 f.write(name + " " + v + " " + str(w) + " \r\n ")

# output_csv
list_node = []

for name, times in names.items():
    # print('name1: ',name,'name2: ',name,'频数: ',times)
    list_node.append([name, name, times])
df1 = pd.DataFrame(list_node, columns=['Id', 'Label', 'weight'])
df1.to_csv('data/ccout/node.csv', index=False)
list_edge = []
for name, edges in relationships.items():
    for v, w in edges.items():
        if w > 3:
            list_edge.append([name, v, str(w)])
df2 = pd.DataFrame(list_edge, columns=['Source', 'Target', 'weight'])
df2.to_csv('data/ccout/edge.csv', index=False)
# 至此,代码就生成了目标文件: node.csv,edge.csv文件

保存完数据后,根据names里存放的人物出现的数据,调用Python下的词云生成工具wordcloud以词云的形式将人物出现的频率形象的展示出来。

# def draw_cloud()
names2 = names.copy()
ci = list(names2.keys())  # 将字典的键值转换为列表
for seg in ci:
    if names2[seg] < 5 or '一' in seg:
        names2.pop(seg)
# 图片遮罩层
mask_img = np.array(Image.open("data/ccin/yunBackground.png"))
font = r'font/simfang.ttf'
wc = WordCloud(
    background_color="white",  # 设置背景颜色,与图片的背景色相关
    mask=mask_img,  # 设置背景图片
    collocations=False,
    font_path=font,  # 显示中文,可以更换字体
    max_font_size=2000,  # 设置字体最大值
    random_state=1,  # 设置有多少种随机生成状态,即有多少种配色方案
    width=1600,
    margin=0).generate_from_frequencies(names2)
plt.imshow(wc)
image_colors = ImageColorGenerator(mask_img)
plt.imshow(wc.recolor(color_func=image_colors))
plt.axis('off')  # 隐藏图像坐标轴
plt.show()  # 展示图片
wc.to_file('data/ccout/ciyun.png')  # 保存生成的词云图

基于共现网络原理将剧本《人民的名义》人物关系社交网络可视化_第1张图片
生成的词云图↑↑↑↑
基于共现网络原理将剧本《人民的名义》人物关系社交网络可视化_第2张图片
Gephi可视化人物网络关系图1↑↑↑↑
基于共现网络原理将剧本《人民的名义》人物关系社交网络可视化_第3张图片
Gephi可视化人物网络关系图2↑↑↑↑

你可能感兴趣的:(随笔记_心得)