精选词后求共现词关系的协方差矩阵进行社会网络分析

2022-9-9 18:08| 发布者: Fuller| 查看: 4230| 评论: 0

摘要: 这一批Jupyter Notebook围绕着点度中心性分析,介绍了两种简化社会网络图的方法:MST和根据阈值剪边,展示了在notebook中用python调用numpy和networkx的方法。下一批notebook将实验其他中心性的计算方法。在完成这一 ...

1  介绍

这一批Jupyter Notebook围绕着点度中心性分析,介绍了两种简化社会网络图的方法:MST和根据阈值剪边,展示了在notebook中用python调用numpy和networkx的方法。下一批notebook将实验其他中心性的计算方法。在完成这一批notebook之前,我将用两个notebook,采用精选的特征词再计算一遍,以便与没有精选词的计算结果进行对比。本篇对应《对共词关系求协方差矩阵后是否有更好的社会网络分析结果?》,计算过程一样,但是所用的数据集不一样,把特别高频的词和文档频率低的词删除了。

在《共词分析中的共词关系是怎么得到的?》一篇我们讲解了共词关系描述了什么,同时也提到,如果为了衡量词与词之间在文档中的分布规律是否相似,还有其他一些度量方法。前面,通过《用networkx和python编程可视化分析共词关系图》这篇notebook,我们学会了怎样用社会网络图分析和观察共词关系,通过《用MST(minimum or maximum spanning tree)算法简化共词关系图》和《设置边权重阈值裁剪共词关系图》学会了两种简化图的方法,这两种方法提供了两个不同的视图去观察共词关系。

这些分析同样的可以用在co-word之外的co-auther, co-cited, co-reference等等社会网络分析中。用co-word进行演练有个好处:借助于GooSeeker分词和情感分析软件,一系列数据集唾手可得,而且可以很有意思地紧跟热点,想研究二舅就研究二舅,想研究糖水爷爷就研究糖水爷爷。

本notebook准备使用协方差矩阵来描述共词关系,同共词矩阵相比,这可以看作是更加细腻的考察,因为通过去中心化计算,词在文档中的分布规律不再是非负数描述的,而是有正有负,可以看作是有涨有跌,这样,如果两个词有相同的涨跌,那么他们的协方差就会比较大,同时跌虽然都是负值,两个负数相乘变成正数,为协方差的最终结果给予正向的贡献。

看起来求协方差是挺美好的,但是,同时可以观察到一些低文档频率的词带来的冲击,问题的根源也在前面的notebook也做了分析和解释。本notebook处于对比的目的,使用GooSeeker分词和情感分析提供的排序功能,精选词语再进行协方差计算,对比观察分析结果有什么变化。

2  使用方法

为完成本notebook的分析任务,操作顺序是:

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

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

3  修改历史

2022-08-20:第一版发布

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  设置中文字体

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

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)

为了防止plt显示找不到字体的问题,先做如下设置。参看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[2] in item_filename:

        file_word_frequency_matrix = item_filename

        continue

if file_word_frequency_matrix:

    print("选词矩阵表:", "data/raw/", file_word_frequency_matrix)

else:

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

输出结果像这样:

C:\Users\work\workspace\notebook\GooSeeker分词软件导出的选词矩阵和共词矩阵的关系-二舅-对比\notebook\eda\../../data/raw

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

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

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

6.1  用pandas dataframe读入选词矩阵

df_word_frequency_matrix = pd.read_excel(os.path.join(raw_data_dir, file_word_frequency_matrix))

df_word_frequency_matrix.head(2)

6.2  提取字段名

将用于给graph的node命名

coword_names = df_word_frequency_matrix.columns.values[2:]

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

coword_names

输出结果如下:

There are  100  words

array(['苦难', '精神', '内耗', '故事', '问题', '社会', '时代', '人生', '时候', '世界', '作者',

       '残疾', '中国', '作品', '农村', '城市', '现实', '电影', '命运', '人民', '东西', '底层',

       '媒体', '文化', '年轻人', '人们', '事情', '感觉', '观众', '普通人', '孩子', '经历', '一生',

       '价值', '内容', '编剧', '鸡汤', '能力', '年代', '时间', '原因', '能量', '意义', '老人',

       '评论', '励志', '文案', '村里', '资本', '医生', '流量', '文艺创作', '国家', '艺术', '个人',

       '情绪', '内心', '大众', '朋友', '农民', '母亲', '悲剧', '观点', '大学', '机会', '思想',

       '残疾人', '文艺', '压力', '力量', '角度', '心理', '父母', '分钟', '方式', '人物', '老师',

       '环境', '态度', '物质', '关系', '个体', '条件', '历史', '情况', '群众', '穷人', '房子',

       '回村', '本质', '办法', '官方', '平台', '想法', '视角', '生命', '热度', '地方', '医疗',

       '身体'], dtype=object)

6.3  生成矩阵数据结构

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

array_word_frequence_matrix = df_word_frequency_matrix.values[:, 2:].astype(float)

array_word_frequence_matrix

输出结果如下:

array([[0., 1., 1., ..., 0., 0., 0.],

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

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

       ...,

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

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

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

7  求协方差矩阵

在《共词分析中的共词关系是怎么得到的?》我们说过,如果选词矩阵称为R,那么,RTR(因为网页上排版能力有限,这个表示R矩阵的转置乘以R)就是共词矩阵(这里假定R中只有0和1两个值表示是否出现这个词,我们下面的计算所用的不只是1,而是>=1的数值表示词频,原理一样)。如果先把R去中心化得到矩阵B(转换成mean deviation form),那么,BTB(因为网页上排版能力有限,这个表示B矩阵的转置乘以B)就是协方差矩阵。

covariance = np.cov(array_word_frequence_matrix, rowvar = False)

covariance

输出结果如下:

array([[5.96986392e+00, 7.96245049e-01, 3.68695203e-01, ...,

        6.50198030e-02, 7.63472801e-02, 1.19833452e-02],

       [7.96245049e-01, 3.22441480e+00, 1.82168215e+00, ...,

        4.68459091e-02, 1.86753157e-02, 7.34741546e-02],

       [3.68695203e-01, 1.82168215e+00, 1.94116440e+00, ...,

        3.00472225e-02, 5.83955350e-02, 3.87089130e-02],

       ...,

       [6.50198030e-02, 4.68459091e-02, 3.00472225e-02, ...,

        4.51491148e-02, 2.99160489e-03, 1.59100911e-03],

       [7.63472801e-02, 1.86753157e-02, 5.83955350e-02, ...,

        2.99160489e-03, 4.96238279e-02, 4.55299414e-03],

       [1.19833452e-02, 7.34741546e-02, 3.87089130e-02, ...,

        1.59100911e-03, 4.55299414e-03, 4.53606851e-02]])

协方差矩阵是一个对称矩阵,对角线上的值是每个词的方差(variance),为了画图的时候不画环回的边,我们把对角线的所有值赋0

np.fill_diagonal(covariance, 0)

covariance

输出结果如下:

array([[0.00000000e+00, 7.96245049e-01, 3.68695203e-01, ...,

        6.50198030e-02, 7.63472801e-02, 1.19833452e-02],

       [7.96245049e-01, 0.00000000e+00, 1.82168215e+00, ...,

        4.68459091e-02, 1.86753157e-02, 7.34741546e-02],

       [3.68695203e-01, 1.82168215e+00, 0.00000000e+00, ...,

        3.00472225e-02, 5.83955350e-02, 3.87089130e-02],

       ...,

       [6.50198030e-02, 4.68459091e-02, 3.00472225e-02, ...,

        0.00000000e+00, 2.99160489e-03, 1.59100911e-03],

       [7.63472801e-02, 1.86753157e-02, 5.83955350e-02, ...,

        2.99160489e-03, 0.00000000e+00, 4.55299414e-03],

       [1.19833452e-02, 7.34741546e-02, 3.87089130e-02, ...,

        1.59100911e-03, 4.55299414e-03, 0.00000000e+00]])

8  生成图并进行探索

8.1  从NumPy数组生成networkx图

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

graph_covariance = nx.from_numpy_array(covariance)

print(nx.info(graph_covariance))

#graph_covariance.edges(data=True)

输出结果如下:

Name: 

Type: Graph

Number of nodes: 100

Number of edges: 4950

Average degree:  99.0000

8.2  给node加上label

对程序代码的解释参看《用networkx和python编程可视化分析共词关系图》,不再赘述。

coword_labels = {}

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

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

    coword_labels[node] = coword_names[idx]

graph_covariance = nx.relabel_nodes(graph_covariance, coword_labels)

sorted(graph_covariance)

8.3  画图

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

由于是一个全连接图,就没有画的必要了,下面的代码都注释掉了。

#pos = nx.spring_layout(graph_covariance)

#plt.figure(1,figsize=(120,120)) 

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

#plt.show()

9  用MST(maximum spanning tree)删减边

9.1  MST计算

graph_covariance_mst = nx.maximum_spanning_tree(graph_covariance)

print(nx.info(graph_covariance_mst))

# graph_covariance_mst.edges(data=True)

输出结果如下:

Name: 

Type: Graph

Number of nodes: 100

Number of edges: 99

Average degree:   1.9800

9.2  画MST后的图

# 方案2:

#pos = nx.circular_layout(graph_covariance_mst)

pos = nx.spring_layout(graph_covariance_mst)

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

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

plt.show()

10  设定阈值删减边

10.1  选择阈值

删掉多少边比较好呢?我们先看看这些位置上的边权重是多少:

  • 中位数
  • 10%位
  • 2%位

coword_median = np.median(covariance)

coword_median

输出结果:0.008842583189465455

coword_max = np.max(covariance)

coword_max

输出结果:2.4578551843200986

coword_min = np.min(covariance)

coword_min

输出结果:-0.060885718154429454

coword_per10 = np.percentile(covariance, 90)

coword_per10

输出结果:0.0691976828814191

coword_per2 = np.percentile(covariance, 98)

coword_per2

输出结果:0.22641354388815757

10.2  删除权重小于2%分位的边

我们不挨个尝试删减度了,直接实验重度删减后的效果

graph_covariance_per2 = graph_covariance.copy()

graph_covariance_per2.remove_edges_from([(n1, n2) for n1, n2, w in graph_covariance_per2.edges(data="weight") if w < coword_per2])

pos = nx.spring_layout(graph_covariance_per2)

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

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

plt.show()

10.3  删除基于2%分位裁剪的图的孤立点

graph_covariance_per2.remove_nodes_from(list(nx.isolates(graph_covariance_per2)))

#pos = nx.circular_layout(graph_covariance_per2)

pos = nx.spring_layout(graph_covariance_per2)

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

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

plt.show()

11  点度中心性分析

11.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()

11.2  针对MST裁剪的图的处理

11.2.1  对点度中心性排序

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

输出结果如下:

[('精神', 21),

 ('城市', 18),

 ('苦难', 16),

 ('故事', 10),

 ('时候', 7),

 ('农村', 6),

 ('社会', 5),

 ('文化', 5),

 ('中国', 4),

 ('内耗', 3),

 ('问题', 3),

 ('时代', 3),

 ('人生', 3),

 ('作者', 2),

 ('作品', 2),

 ('电影', 2),

 ('命运', 2),

 ('媒体', 2),

 ('价值', 2),

 ('思想', 2),

 ('世界', 1),

 ('残疾', 1),

 ('现实', 1),

 ('人民', 1),

 ('东西', 1),

 ('底层', 1),

 ('年轻人', 1),

 ('人们', 1),

 ('事情', 1),

 ('感觉', 1),

 ('观众', 1),

 ('普通人', 1),

 ('孩子', 1),

 ('经历', 1),

 ('一生', 1),

 ('内容', 1),

 ('编剧', 1),

 ('鸡汤', 1),

 ('能力', 1),

 ('年代', 1),

 ('时间', 1),

 ('原因', 1),

 ('能量', 1),

 ('意义', 1),

 ('老人', 1),

 ('评论', 1),

 ('励志', 1),

 ('文案', 1),

 ('村里', 1),

 ('资本', 1),

 ('医生', 1),

 ('流量', 1),

 ('文艺创作', 1),

 ('国家', 1),

 ('艺术', 1),

 ('个人', 1),

 ('情绪', 1),

 ('内心', 1),

 ('大众', 1),

 ('朋友', 1),

 ('农民', 1),

 ('母亲', 1),

 ('悲剧', 1),

 ('观点', 1),

 ('大学', 1),

 ('机会', 1),

 ('残疾人', 1),

 ('文艺', 1),

 ('压力', 1),

 ('力量', 1),

 ('角度', 1),

 ('心理', 1),

 ('父母', 1),

 ('分钟', 1),

 ('方式', 1),

 ('人物', 1),

 ('老师', 1),

 ('环境', 1),

 ('态度', 1),

 ('物质', 1),

 ('关系', 1),

 ('个体', 1),

 ('条件', 1),

 ('历史', 1),

 ('情况', 1),

 ('群众', 1),

 ('穷人', 1),

 ('房子', 1),

 ('回村', 1),

 ('本质', 1),

 ('办法', 1),

 ('官方', 1),

 ('平台', 1),

 ('想法', 1),

 ('视角', 1),

 ('生命', 1),

 ('热度', 1),

 ('地方', 1),

 ('医疗', 1),

 ('身体', 1)]

11.2.2  综合展示点度中心性

diplay_graph_degree(graph_covariance_mst)

11.3  针对按2%分位数裁剪的图的处理

11.3.1  对点度中心性排序

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

输出结果如下:

[('城市', 18),

 ('精神', 15),

 ('社会', 15),

 ('苦难', 14),

 ('文化', 14),

 ('时代', 11),

 ('内耗', 10),

 ('问题', 9),

 ('作者', 8),

 ('故事', 7),

 ('中国', 7),

 ('农村', 7),

 ('时候', 4),

 ('世界', 4),

 ('残疾', 4),

 ('人们', 4),

 ('人民', 3),

 ('价值', 3),

 ('资本', 3),

 ('国家', 3),

 ('大众', 3),

 ('机会', 3),

 ('底层', 2),

 ('媒体', 2),

 ('事情', 2),

 ('悲剧', 2),

 ('大学', 2),

 ('心理', 2),

 ('方式', 2),

 ('条件', 2),

 ('人生', 1),

 ('作品', 1),

 ('现实', 1),

 ('电影', 1),

 ('命运', 1),

 ('年轻人', 1),

 ('孩子', 1),

 ('能力', 1),

 ('能量', 1),

 ('老人', 1),

 ('励志', 1),

 ('医生', 1),

 ('朋友', 1),

 ('个体', 1),

 ('平台', 1)]

11.3.2  综合展示点度中心性

diplay_graph_degree(graph_covariance_per2)

12  总结

在前面的notebook中,我们没有精选词,当时比较困扰的一个问题是:“二舅”和“视频”这两个高度集中词形成了绝对的核心,经过MST处理以后,几乎是一个星状结构。计算了协方差以后,有一些改善,但是,又出现了新问题:“彭叔”,“公寓”的文档频率很低,只出现了一次,却被大幅度提升了。

本notebook,是在GooSeeker分词和情感分析软件上,根据文档频率排序,将普遍词和稀有词都删除,重新导出选词矩阵做实验,就会发现社会网络图的整个拓扑关系都变了,分析结果将极大幅度改善。

还有一个很重要的发现:经过MST计算和利用阈值删减边的结果很相似,一致性较高,可信度较好:

MST的核心词:('精神', 21),('城市', 18),('苦难', 16),('故事', 10),('时候', 7),('农村', 6),('社会', 5),('文化', 5),('中国', 4),('内耗', 3)

利用阈值删边:('城市', 18),('精神', 15),('社会', 15),('苦难', 14),('文化', 14),('时代', 11),('内耗', 10),('问题', 9),('作者', 8),('故事', 7)

在MST图上,可以很明显地看出来,以“精神”-“苦难”-“故事”为核心表述精神生活状态,以“城市”为中心表述发展平台。

13 下载Notebook

下载本篇Jupyter Notebook请进:精选词后求共现词关系的协方差矩阵观察社会网络分析的效果


鲜花

握手

雷人

路过

鸡蛋

最新评论

GMT+8, 2024-10-11 20:05