用networkx和python编程可视化分析共现词关系图

2022-8-23 15:59| 发布者: Fuller| 查看: 6121| 评论: 0

摘要: 本notebook将演练使用networkx画社会网络分析图。然后在接下来的notebook中演练对图的化简,分别根据边权重化简和利用MST算法化简。

1  介绍

上一篇Jupyter notebook《共词分析中的共词关系是怎么得到的?》我们讲解了怎样将GooSeeker分词和情感分析软件生成的选词矩阵和共词矩阵Excel表读入NumPy array数据结构中,并且展示了以词为研究对象的时候,选词矩阵和共词矩阵之间的关系。

GooSeeker分词和情感分析软件具有展示共现词关系图的能力,但是当前软件版本有以下几个局限需要使用Jupyter Notebook进行弥补:

  • 在GooSeeker分词软件上只能使用共现词矩阵表示的共现关系画图
  • 如果被分析的文档都比较长,也叫长文本(相反的是短文本,比如用网络爬虫采集到的微博内容是比较短的),那么,长文本的共现矩阵是一个比较密集的矩阵,体现在图上的极端情况就是共现词关系图几乎是一个全连接图,一些关键特征很容易被满眼的连接线所掩盖,暂时还没有化简功能。
  • 除了共现关系以外,还可以利用选词矩阵采用多种度量方法计算词与词之间的距离,都可以画成网络图进行观察,用共现关系画图后观察的目的可能是想发现文档表达的主题之间的关系,那么,用其他距离关系有可能可以更有效地进行观察。

这些问题会在接下来的多个notebook中逐一探索,首先,本notebook将演练使用networkx画社会网络分析图。然后在接下来的notebook中演练对图的化简,分别根据边权重化简和利用MST算法化简。

2  使用方法

操作顺序是:

  1. 在GooSeeker分词和文本分析软件上创建文本分析任务并导入包含待分析内容的excel,分析完成后导出共词矩阵表
  2. 将导出的excel表放在本notebook的data/raw文件夹中
  3. 从头到尾执行本notebook的单元

注意:GooSeeker发布的每个notebook项目目录都预先规划好了,具体参看Jupyter Notebook项目目录规划参考。如果要新做一个分析项目,把整个模板目录拷贝一份给新项目,然后编写notebook目录下的ipynb文件。

3  修改历史

2022-08-18:第一版发布

4  版权说明

本notebook是GooSeeker大数据分析团队开发的,所分析的源数据是GooSeeker分词和文本分析软件生成的,本notebook中的代码可自由共享使用,包括转发、复制、修改、用于其他项目中。

5  准备运行环境

5.1  引入需要用到的库

# -*- coding: utf-8 -*-

import os

import numpy as np

import pandas as pd

import networkx as nx

import matplotlib.pyplot as plt

import pylab

%xmode Verbose

import warnings

# 软件包之间配套时可能会使用过时的接口,把这类告警忽略掉可以让输出信息简练一些

warnings.filterwarnings("ignore", category=DeprecationWarning)

# 把RuntimeWarning忽略掉,不然画图的时候有太多告警了

warnings.filterwarnings("ignore", category=RuntimeWarning)


5.2  设置中文字体

因为含有中文,pyplot画图有可能会显示下面的错误信息:

C:\ProgramData\Anaconda3\lib\site-packages\matplotlib\backends\backend_agg.py:238: RuntimeWarning: Glyph 32993 missing from current font. font.set_text(s, 0.0, flags=flags)

这是因为找不到中文字体,所以在图上的中文显示不出来,为了解决pyplot显示找不到字体的问题,参看glyph-23130-missing-from-current-font,先做如下设置。

#plt.rcParams['font.sans-serif']=['SimHei']

# 上面一行在macOS上没有效果,所以,使用下面的字体

plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']

plt.rcParams['axes.unicode_minus'] = False


5.3  常量和配置

在我们发布的一系列Jupyter Notebook中,凡是处理GooSeeker分词软件导出的结果文件的,都给各种导出文件起了固定的名字。为了方便大家使用,只要把导出文件放在data/raw文件夹,notebook就会找到导出文件,赋值给对应的文件名变量。下面罗列了可能用到的文件名变量:

  • file_all_word:词频表
  • file_chosen_word: 选词结果表
  • file_seg_effect: 分词效果表
  • file_word_occurrence_matrix: 选词矩阵表(是否出现)
  • file_word_frequency_matrix: 文档词频对应矩阵
  • file_word_document_match: 选词匹配表
  • file_co_word_matrix: 共词矩阵表


pd.set_option('display.width', 1000)  # 设置字符显示宽度

pd.set_option('display.max_rows', None)  # 设置显示最大

# np.set_printoptions(threshold=np.inf) # threshold 指定超过多少使用省略号,np.inf代表无限大

# 存原始数据的目录

raw_data_dir = os.path.join(os.getcwd(), '../../data/raw')

# 存处理后的数据的目录

processed_data_dir = os.path.join(os.getcwd(), '../../data/processed')

filename_temp = pd.Series(['词频','分词效果','选词矩阵','选词匹配','选词结果','共词矩阵'])

file_all_word = ''

file_seg_effect = ''

file_word_occurrence_matrix = ''

file_word_frequency_matrix = ''

file_word_document_match = ''

file_chosen_word = ''

file_co_word_matrix = ''


5.4  检测data\raw目录下是否有GooSeeker分词结果表

在本notebook只使用共词矩阵表,下面的代码将检查data/raw中有没有这个表,如果没有会报错,后面的程序就没法执行了。

# 0:'词频', 1:'分词效果', 2:'选词矩阵', 3:'选词匹配', 4:'选词结果', 5:'共词矩阵'

print(raw_data_dir + '\r\n')

for item_filename in os.listdir(raw_data_dir):

    if filename_temp[5] in item_filename:

        file_co_word_matrix = item_filename

        continue

if file_co_word_matrix:

    print("共词矩阵表:", "data/raw/", file_co_word_matrix)

else:

    print("共词矩阵表:不存在")

输出结果像这样:

C:\Users\work\workspace_219\notebook\发布-二舅\用networkx和python编程可视化分析共词关系图\notebook\eda\../../data/raw

共词矩阵表: data/raw/ 共词矩阵-知乎-二舅.xlsx

6  读取共词矩阵表并存入矩阵

读入过程不展开讲解,具体参看《共词分析中的共词关系是怎么得到的?

6.1  用pandas dataframe读入共词矩阵

df_co_word_matrix = pd.read_excel(os.path.join(raw_data_dir, file_co_word_matrix))

df_co_word_matrix.head(2)

6.2  提取字段名

将用于给graph的node命名

coword_names = df_co_word_matrix.columns.values[1:]

print("There are ", len(coword_names), " words")

coword_names

输出结果:

There are  133  words

array(['世界', '二舅', '现实', '时候', '故事', '人生', '事情', '苦难', '精神', '底层', '内耗',

       '时代', '人民', '视频', '社会', '人们', '问题', '母亲', '普通人', '国家', '农村', '作者',

       '东西', '中国', '回村', '作品', '时间', '残疾', '原因', '孩子', '命运', '个人', '力量',

       '年轻人', '价值', '意义', '一生', '经历', '感觉', '方式', '大学', '房子', '年代', '条件',

       '观众', '地方', '评论', '媒体', '城市', '态度', '村里', '能力', '本质', '青年', '文化',

       '能量', '医生', '老师', '办法', '大众', '电影', '鸡汤', '机会', '压力', '父母', '穷人',

       '小镇', '角度', '悲剧', '收入', '关系', '内容', '视角', '老人', '内心', '环境', '流量',

       '情况', '情绪', '文案', '目的', '观点', '人类', '农民', '资本', '个体', '励志', '代表',

       '平台', '文艺创作', '分钟', '经济', '想法', '朋友', '心理', '群众', '人物', '日子', '资源',

       '思想', '历史', '残疾人', '文艺', '编剧', '木匠', '过程', '生命', '身体', '状态', '艺术',

       '政府', '物质', '人人', '医疗', '村子', '文学', '热度', '心态', '网友', '周劼', '机制',

       '宁宁', '外甥', '兴趣', '主流', '公子', '父亲', '官方', '文艺作品', '好人', '源泉', '公寓',

       '彭叔'], dtype=object)

6.3  生成矩阵数据结构

# 使用astype函数对数据类型进行转换,否则,下面画图的时候可能会报错

array_co_word_matrix = df_co_word_matrix.values[:, 1:].astype(float)

array_co_word_matrix

输出结果:

array([[101.,  74.,  24., ...,   1.,   1.,   1.],

       [ 74., 403.,  59., ...,   5.,   1.,   1.],

       [ 24.,  59.,  76., ...,   1.,   1.,   1.],

       ...,

       [  1.,   5.,   1., ...,   7.,   0.,   0.],

       [  1.,   1.,   1., ...,   0.,   1.,   0.],

       [  1.,   1.,   1., ...,   0.,   0.,   1.]])

统计一下词语数量看看:

word_num = len(array_co_word_matrix)

word_num

输出结果:133

7  生成图并进行探索

7.1  从NumPy数组生成networkx图

参看networkx文档,有专门的函数从其他数据结构直接生成graph

#graph_co_word_df = nx.from_pandas_adjacency(df_co_word_matrix)

graph_co_word_matrix = nx.from_numpy_array(array_co_word_matrix)

print(nx.info(graph_co_word_matrix))

#graph_co_word_matrix.edges(data=True)

输出结果:

Name: 

Type: Graph

Number of nodes: 133

Number of edges: 7843

Average degree: 117.9398

7.2  给node加上label

如果不加label,画出来的图上的每个节点只是一个编号,加上label可以看到节点对应的词。

根据get_node_attributes,查看现在的note labels

coword_labels = nx.get_node_attributes(graph_co_word_matrix,'labels')

coword_labels

输出结果:

{}

根据How-do-I-label-a-node-using-networkx-in-python,重新命名labels

for idx, node in enumerate(graph_co_word_matrix.nodes()): 

    print("idx=", idx, "; node=", node)

    coword_labels[node] = coword_names[idx]

graph_co_word_matrix = nx.relabel_nodes(graph_co_word_matrix, coword_labels)

sorted(graph_co_word_matrix)

for idx, node in enumerate(graph_co_word_matrix.nodes()): 

    print("idx=", idx, "; node=", node)

7.3  画图

figure函数的使用方法参看pyplot官网 。其他参考资料:

# 方案1:用pylab画图

#pos=nx.shell_layout(graph_co_word_matrix)

#nx.draw(graph_co_word_matrix,pos,with_labels=True, node_color='white', edge_color='grey', node_size=1200, alpha=1 )

#pylab.title('co-word matrix',fontsize=25)

#pylab.show()

# 方案2

#pos = nx.circular_layout(maximum_tree)

pos = nx.spring_layout(graph_co_word_matrix)

plt.figure(1,figsize=(30,30)) 

nx.draw(graph_co_word_matrix, pos, node_size=10, with_labels=True, font_size=22, font_color="red")

#nx.draw(graph_co_word_matrix, pos, with_labels=True)

#nx.draw_networkx_labels(graph_co_word_matrix, pos, labels)

plt.show()

8  点度中心性分析

上面画的图糊成了一片,改进方向可以有这些

  1. 可能是上面的代码没有把pyplot的能力发挥出来,应该查查相关手册,设置更多参数,让图更加直观。
  2. 采用Gephi画图,Gephi是一个专用的可视化社会网络图分析工具,有更多展示手段
  3. 从多个侧面观察一下图数据。

本notebook尝试第三点,而Gephi的使用方法将在另一个系列的文章中讲解。

8.1  定义一个公共画图函数

下面的代码来自NetworkX的中心性分析案例:plot_degree.html。将用来从多个角度观察点度中心性。

def diplay_graph_degree(G):

    seq_degree = sorted((d for n, d in G.degree()), reverse=True)

    dmax = max(seq_degree)

    

    fig = plt.figure("Degree of the count graph", figsize=(8, 8))

    # Create a gridspec for adding subplots of different sizes

    axgrid = fig.add_gridspec(5, 4)

    

    ax0 = fig.add_subplot(axgrid[0:3, :])

    Gcc = G.subgraph(sorted(nx.connected_components(G), key=len, reverse=True)[0])

    pos = nx.spring_layout(Gcc, seed=10396953)

    nx.draw_networkx_nodes(Gcc, pos, ax=ax0, node_size=20)

    nx.draw_networkx_edges(Gcc, pos, ax=ax0, alpha=0.4)

    ax0.set_title("Connected components of G")

    ax0.set_axis_off()

    

    ax1 = fig.add_subplot(axgrid[3:, :2])

    ax1.plot(seq_degree, "b-", marker="o")

    ax1.set_title("Degree Rank Plot")

    ax1.set_ylabel("Degree")

    ax1.set_xlabel("Rank")

    

    ax2 = fig.add_subplot(axgrid[3:, 2:])

    ax2.bar(*np.unique(seq_degree, return_counts=True))

    ax2.set_title("Degree histogram")

    ax2.set_xlabel("Degree")

    ax2.set_ylabel("# of Nodes")

    

    fig.tight_layout()

    plt.show()

8.2  对点度中心性排序

观察哪些词是中心词。可以看到,由于数据集中的每个文档都比较长,共现的机会很高,所以,点度中心性很近似,前面的这些基本上都是全连接。

sorted(graph_co_word_matrix.degree(), key=lambda x: x[1], reverse=True)

8.3  综合展示点度中心性

因为图的密度很高,用这个新定义的画图函数依然显示一片黑,但是还有两个图值得注意,实际上这两个图展示了相同内容,只是展示的角度不同,从这两个图可以看到具有某个点度的节点数量。

diplay_graph_degree(graph_co_word_matrix)

9  总结

下面是GooSeeker分词和情感分析软件自带的社会网络图展示,可以看到,软件自带的展示效果要远比上面的好。那么,我们需要怎样的处理才能从社会网络图上发现更多有价值的信息呢?在接下来的notebook中,我们将讲解两种简化图的方法:

  • MST:mininum spanning tree,或者maximum spanning tree。展开树(spanning tree)是一个极端的简化手段,把绝大多数边都删除了,每个节点只留下一条最相似/最近的边跟另一个节点相连。处理以后,如果还有局部的星状结构,那就是十分核心的词。
  • 基于权重阈值剪掉低权重的边:MST处理以后,graph变成了tree,不再有环路了,顶多围绕个别核心词形成星状结构,但是,依据阈值剪裁,可以保留紧密度高的局部的图。

除了简化社会网络图,我们在《共词分析中的共词关系是怎么得到的?》提到,度量词与词之间关系的方法不只是共词关系,或者说共词关系不只是用共现次数表示,还可以有更细致的方法,比如,度量同步变化规律和幅度的协方差分析,经过标准化后的皮尔森相关系数分析等等,后面的notebook讲逐步讲解。

10  下载源代码

下载Jupyter Notebook源代码请进入:使用python编程实现共现词关系图的可视化



鲜花

握手

雷人

路过

鸡蛋

最新评论

GMT+8, 2024-11-24 07:10