一、概要
数学家 Andrew Beveridge 和Jie Shan在数学杂志上发表过一篇名叫《权力的网络》的论文,主要分析畅销小说《冰与火之歌》第三部《冰雨的风暴》中人物关系,其已经拍成电视剧《权力的游戏》系列。他们在论文中介绍了如何通过文本分析和实体提取构建人物关系的网络。紧接着,使用社交网络分析算法对人物关系网络分析找出最重要的角色;应用社区发现算法来找到人物聚类。
其中的分析和可视化是用Gephi做的。本文参考了一些大牛的帖子,主要使用neo4j进行分析及可视化。
Notes:如何从文本提取到人物角色的关系网路(图)数据?
首先需要编制一份详尽的字符及其昵称列表,用于对文本的检索。每次人物的名字(或昵称)在段落出现时,若在间距为15个单词的空间内出现另一个人物名字(或昵称),则认为他们之前有联系。两个人物名称出现的情况有很多种,但在各类情形下,共现就已经暗示着两者有某种联系。
更多细节见论文作者的网站(别问我是怎么知道的~)
二、工具准备
-
本地安装并配置好neo4j数据库附参考安装教
-
安装python包py2neo、igraph
三、具体步骤
1.导入基础数据
先启动neo4j ,有时候neo4j start 不管用,neo4j.bat console能行。
可以选择已经准备好的网络数据,导入节点关系数据。
数据格式如:
LOAD CSV WITH HEADERS FROM "https://www.macalester.edu/~abeverid/data/stormofswords.csv" AS row
MERGE (src:Character {
name: row.Source})
MERGE (tgt:Character {
name: row.Target})
MERGE (src)-[r:INTERACTS]->(tgt) ON CREATE SET r.weight = toInteger(row.weight)
数据库中的图由“Character”节点和“INTERACTS”关系构成,我们可以运行以下命令在neo4j中得到简单的可视化结果
MATCH p=(:Character)-[:INTERACTS]->(:Character)
RETURN p
但是这个图直观展示的信息较少,我们想让可视化满足以下要求:
- 节点的大小与其PageRank分数成比例,使得我们可以直观判断网络中的重要节点
- 节点的颜色由其社区属性(comunity)决定,我们一眼就能判断社区的分布以及节点的社区所在
- 节点之间联系厚度(thickness)与关系权重(weight)成比例
接下来我们主要使最终的可视化结果满足以上要求。
2.导入/计算 PR、community数据
导入网络上整理好的PageRank数据和社区搜寻结果数据
LOAD CSV WITH HEADERS FROM "https://raw.githubusercontent.com/johnymontana/neovis.js/master/examples/data/got-centralities.csv" AS row
MATCH (c:Character {
name: row.name})
SET c.community = toInteger(row.community),
c.pagerank = toFloat(row.pagerank)
或者我们可以利用python的图分析库将PR和community直接计算出来。
使用python-igraph
从Neo4j构建一个igraph实例
为了在《权力的游戏》的数据的图分析中使用igraph,首先需要从Neo4j拉取数据,用Python建立igraph实例。作者使用 Neo4j 的Python驱动库py2neo。我们能直接传入Py2neo查询结果对象到igraph的TupleList构造器,创建igraph实例:
from py2neo import Graph
from igraph import Graph as IGraph
graph = Graph()
query = '''
MATCH (c1:Character)-[r:INTERACTS]->(c2:Character)
RETURN c1.name, c2.name, r.weight AS weight
'''
ig = IGraph.TupleList(graph.run(query), weights=True)
现在有了igraph对象,可以运行igraph实现的各种图算法。
PageRank
使用igraph运行的第一个算法是PageRank。PageRank算法源自Google的网页排名。它是一种特征向量中心性(eigenvector centrality)算法。
在igraph实例中运行PageRank算法,然后把结果写回Neo4j,在角色节点创建一个pagerank属性存储igraph计算的值:
pg = ig.pagerank()
pgvs = []
for p in zip(ig.vs, pg):
print(p)
pgvs.append({
"name": p[0]["name"], "pg": p[1]})
pgvs
write_clusters_query = '''
UNWIND {nodes} AS n
MATCH (c:Character) WHERE c.name = n.name
SET c.pagerank = n.pg
'''
graph.run(write_clusters_query, nodes=pgvs