几个月前,数学家 Andrew Beveridge和Jie Shan在数学杂志上发表《权力的网络》,主要分析畅销小说《冰与火之歌》第三部《冰雨的风暴》中人物关系,其已经拍成电视剧《权力的游戏》系列。他们在论文中介绍了如何通过文本分析和实体提取构建人物关系的网络。紧接着,使用社交网络分析算法对人物关系网络分析找出最重要的角色;应用社区发现算法来找到人物聚类。

#! pip install py2neo
from py2neo import Graph
graph = Graph()

安装py2neo

!pip install py2neo --upgrade
Collecting py2neo
Downloading py2neo-3.1.0-py2.py3-none-any.whl (140kB)
[K 100% |████████████████████████████████| 143kB 3.0MB/s
[?25hInstalling collected packages: py2neo
Found existing installation: py2neo 2.0.8
Uninstalling py2neo-2.0.8:
Successfully uninstalled py2neo-2.0.8
Successfully installed py2neo-3.1.0

Import into Neo4j

首先创建节点c,并做唯一限制性约束,c.name唯一,保证schema的完整性:

带有标签Character的节点代表小说中的角色,用单向关系类型INTERACTS代表小说中的角色有过接触。节点属性会存储角色的名字name,两角色间接触的次数作为关系的属性:权重(weight)。

一旦约束创建即相应的创建索引,这将有助于通过角色的名字查询的性能。作者使用Neo4j的Cypher(Cypher是一种声明式图查询语言,能表达高效查询和更新图数据库)LOAD CSV语句导入数据:

# 创建节点,并唯一性约束
graph.run("CREATE CONSTRAINT ON (c:Character) ASSERT c.name IS UNIQUE;") # 导入节点,关系和关系的属性
for record in graph.run('''
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)
SET r.weight = toInt(row.Weight)
RETURN count(*) AS paths_written
'''):
print(record)
('paths_written': 352)
# match
for r in graph.run('''
MATCH p=(:Character)-[:INTERACTS]-(:Character)
RETURN p limit 10
'''):
print(r)
<Record p=(Aemon)-[:INTERACTS {weight: 31}]->(Samwell)>
<Record p=(Aemon)<-[:INTERACTS {weight: 4}]-(Stannis)>
<Record p=(Aemon)-[:INTERACTS {weight: 5}]->(Grenn)>
<Record p=(Aemon)<-[:INTERACTS {weight: 4}]-(Robert)>
<Record p=(Aemon)<-[:INTERACTS {weight: 30}]-(Jon)>
<Record p=(Aerys)-[:INTERACTS {weight: 8}]->(Tywin)>
<Record p=(Aerys)-[:INTERACTS {weight: 5}]->(Tyrion)>
<Record p=(Aerys)-[:INTERACTS {weight: 18}]->(Jaime)>
<Record p=(Aerys)-[:INTERACTS {weight: 6}]->(Robert)>
<Record p=(Alliser)<-[:INTERACTS {weight: 15}]-(Jon)>

Analyzing the network 分析网络

Number of characters 人物数量

万事以简单开始。先看看上图上由有多少人物:

# count
for record in graph.run("MATCH (c:Character) RETURN count(c) AS num"):
print(record)
<Record num=107>

Summary statistics 概要统计

统计每个角色接触的其它角色的数目:

# with min max avg stdev
for record in graph.run('''
MATCH (c:Character)-[:INTERACTS]->()
WITH c, count(*) AS num
RETURN min(num) AS min, max(num) AS max, avg(num) AS avg_characters, stdev(num) AS stdev
'''):
print(record)
<Record min=1 max=24 avg_characters=4.957746478873241 stdev=6.2276723918750845>

Diameter of the network 网络直径

网络的直径或者测底线或者最长最短路径

for r in graph.run('''
// Find maximum diameter of network
// maximum shortest path between two nodes
MATCH (a:Character), (b:Character) WHERE id(a) > id(b)
MATCH p=shortestPath((a)-[:INTERACTS*]-(b))
RETURN length(p) AS len, extract(x IN nodes(p) | x.name) AS path
ORDER BY len DESC LIMIT 4
'''):
print(r)
('len': 6, 'path': ['Illyrio', 'Belwas', 'Daenerys', 'Robert', 'Tywin', 'Oberyn', 'Amory'])
('len': 6, 'path': ['Illyrio', 'Belwas', 'Daenerys', 'Robert', 'Sansa', 'Bran', 'Jojen'])
('len': 6, 'path': ['Illyrio', 'Belwas', 'Daenerys', 'Robert', 'Stannis', 'Davos', 'Shireen'])
('len': 6, 'path': ['Illyrio', 'Belwas', 'Daenerys', 'Robert', 'Sansa', 'Bran', 'Luwin'])

我们能看到网络中有许多长度为6的路径。

Shortest path 最短路径

使用Cypher 的shortestPath函数找到图中任意两个角色之间的最短路径。让我们找出凯特琳·史塔克(Catelyn Stark )和卓戈·卡奥(Kahl Drogo)之间的最短路径:

for r in graph.run('''
// Shortest path from Catelyn Stark to Khal Drogo
MATCH (catelyn:Character {name: "Catelyn"}), (drogo:Character {name: "Drogo"})
MATCH p=shortestPath((catelyn)-[INTERACTS*]-(drogo))
RETURN p
'''):
print(r)
<Record p=(Catelyn)-[:INTERACTS {weight: 8}]->(Sansa)-[:INTERACTS {weight: 5}]->(Robert)<-[:INTERACTS {weight: 5}]-(Daenerys)-[:INTERACTS {weight: 18}]->(Drogo)>

All shortest paths 所有的最短路径

联结凯特琳·史塔克(Catelyn Stark )和卓戈·卡奥(Kahl Drogo)之间的最短路径可能还有其它路径,我们可以使用Cypher的allShortestPaths函数来查找:

for r in graph.run('''
// All shortest paths from Catelyn Stark to Khal Drogo
MATCH (catelyn:Character {name: "Catelyn"}), (drogo:Character {name: "Drogo"})
MATCH p=allShortestPaths((catelyn)-[INTERACTS*]-(drogo))
RETURN p
'''):
print(r)
<Record p=(Catelyn)-[:INTERACTS {weight: 8}]->(Sansa)-[:INTERACTS {weight: 5}]->(Robert)<-[:INTERACTS {weight: 5}]-(Daenerys)-[:INTERACTS {weight: 18}]->(Drogo)>
<Record p=(Catelyn)-[:INTERACTS {weight: 19}]->(Jaime)-[:INTERACTS {weight: 4}]->(Barristan)<-[:INTERACTS {weight: 11}]-(Jorah)-[:INTERACTS {weight: 6}]->(Drogo)>
<Record p=(Catelyn)-[:INTERACTS {weight: 19}]->(Jaime)-[:INTERACTS {weight: 4}]->(Barristan)<-[:INTERACTS {weight: 20}]-(Daenerys)-[:INTERACTS {weight: 18}]->(Drogo)>
<Record p=(Catelyn)-[:INTERACTS {weight: 19}]->(Jaime)-[:INTERACTS {weight: 17}]->(Robert)<-[:INTERACTS {weight: 5}]-(Daenerys)-[:INTERACTS {weight: 18}]->(Drogo)>
<Record p=(Catelyn)-[:INTERACTS {weight: 4}]->(Cersei)-[:INTERACTS {weight: 16}]->(Robert)<-[:INTERACTS {weight: 5}]-(Daenerys)-[:INTERACTS {weight: 18}]->(Drogo)>
<Record p=(Catelyn)-[:INTERACTS {weight: 4}]->(Stannis)<-[:INTERACTS {weight: 5}]-(Robert)<-[:INTERACTS {weight: 5}]-(Daenerys)-[:INTERACTS {weight: 18}]->(Drogo)>
<Record p=(Catelyn)-[:INTERACTS {weight: 5}]->(Tyrion)<-[:INTERACTS {weight: 4}]-(Viserys)<-[:INTERACTS {weight: 8}]-(Daenerys)-[:INTERACTS {weight: 18}]->(Drogo)>
<Record p=(Catelyn)-[:INTERACTS {weight: 5}]->(Tyrion)-[:INTERACTS {weight: 9}]->(Robert)<-[:INTERACTS {weight: 5}]-(Daenerys)-[:INTERACTS {weight: 18}]->(Drogo)>
<Record p=(Catelyn)<-[:INTERACTS {weight: 5}]-(Eddard)-[:INTERACTS {weight: 10}]->(Robert)<-[:INTERACTS {weight: 5}]-(Daenerys)-[:INTERACTS {weight: 18}]->(Drogo)>

Pivotal nodes 关键节点

在网络中,如果一个节点位于其它两个节点所有的最短路径上,即称为关键节点。下面我们找出网络中所有的关键节点:

for r in graph.run('''
// Find all pivotal nodes in network
MATCH (a:Character), (b:Character)
MATCH p=allShortestPaths((a)-[:INTERACTS*]-(b))
WITH collect(p) AS paths, a, b
MATCH (c:Character) WHERE all(x IN paths WHERE c IN nodes(x)) AND NOT c IN [a,b]
RETURN a.name, b.name, c.name AS PivotalNode SKIP 490 LIMIT 10
'''):
print(r)
<Record a.name='Balon' b.name='Lothar' PivotalNode='Robb'>
<Record a.name='Balon' b.name='Luwin' PivotalNode='Bran'>
<Record a.name='Balon' b.name='Luwin' PivotalNode='Robb'>
<Record a.name='Balon' b.name='Melisandre' PivotalNode='Stannis'>
<Record a.name='Balon' b.name='Missandei' PivotalNode='Daenerys'>
<Record a.name='Balon' b.name='Myrcella' PivotalNode='Tyrion'>
<Record a.name='Balon' b.name='Rattleshirt' PivotalNode='Jon'>
<Record a.name='Balon' b.name='Rickard' PivotalNode='Robb'>
<Record a.name='Balon' b.name='Rickon' PivotalNode='Robb'>
<Record a.name='Balon' b.name='Roose' PivotalNode='Robb'>

从结果表格中我们可以看出有趣的结果:罗柏·史塔克(Robb)是卓戈·卡奥(Drogo)和拉姆塞·波顿(Ramsay)的关键节点。这意味着,所有联结卓戈·卡奥(Drogo)和拉姆塞·波顿(Ramsay)的最短路径都要经过罗柏·史塔克(Robb)。我们可以通过可视化卓戈·卡奥(Drogo)和拉姆塞·波顿(Ramsay)之间的所有最短路径来验证:

for r in graph.run('''
MATCH (a:Character {name: "Drogo"}), (b:Character {name: "Ramsay"})
MATCH p=allShortestPaths((a)-[:INTERACTS*]-(b))
RETURN p
'''):
print(r)
<Record p=(Drogo)<-[:INTERACTS {weight: 18}]-(Daenerys)-[:INTERACTS {weight: 5}]->(Robert)<-[:INTERACTS {weight: 5}]-(Sansa)<-[:INTERACTS {weight: 15}]-(Robb)-[:INTERACTS {weight: 4}]->(Ramsay)>
<Record p=(Drogo)<-[:INTERACTS {weight: 6}]-(Jorah)-[:INTERACTS {weight: 11}]->(Barristan)<-[:INTERACTS {weight: 4}]-(Jaime)<-[:INTERACTS {weight: 15}]-(Robb)-[:INTERACTS {weight: 4}]->(Ramsay)>
<Record p=(Drogo)<-[:INTERACTS {weight: 18}]-(Daenerys)-[:INTERACTS {weight: 20}]->(Barristan)<-[:INTERACTS {weight: 4}]-(Jaime)<-[:INTERACTS {weight: 15}]-(Robb)-[:INTERACTS {weight: 4}]->(Ramsay)>
<Record p=(Drogo)<-[:INTERACTS {weight: 18}]-(Daenerys)-[:INTERACTS {weight: 5}]->(Robert)<-[:INTERACTS {weight: 17}]-(Jaime)<-[:INTERACTS {weight: 15}]-(Robb)-[:INTERACTS {weight: 4}]->(Ramsay)>
<Record p=(Drogo)<-[:INTERACTS {weight: 18}]-(Daenerys)-[:INTERACTS {weight: 5}]->(Robert)-[:INTERACTS {weight: 5}]->(Stannis)<-[:INTERACTS {weight: 4}]-(Robb)-[:INTERACTS {weight: 4}]->(Ramsay)>
<Record p=(Drogo)<-[:INTERACTS {weight: 18}]-(Daenerys)-[:INTERACTS {weight: 8}]->(Viserys)-[:INTERACTS {weight: 4}]->(Tyrion)<-[:INTERACTS {weight: 12}]-(Robb)-[:INTERACTS {weight: 4}]->(Ramsay)>
<Record p=(Drogo)<-[:INTERACTS {weight: 18}]-(Daenerys)-[:INTERACTS {weight: 5}]->(Robert)<-[:INTERACTS {weight: 9}]-(Tyrion)<-[:INTERACTS {weight: 12}]-(Robb)-[:INTERACTS {weight: 4}]->(Ramsay)>
<Record p=(Drogo)<-[:INTERACTS {weight: 18}]-(Daenerys)-[:INTERACTS {weight: 5}]->(Robert)<-[:INTERACTS {weight: 5}]-(Jon)<-[:INTERACTS {weight: 14}]-(Robb)-[:INTERACTS {weight: 4}]->(Ramsay)>
<Record p=(Drogo)<-[:INTERACTS {weight: 18}]-(Daenerys)-[:INTERACTS {weight: 5}]->(Robert)<-[:INTERACTS {weight: 11}]-(Tywin)<-[:INTERACTS {weight: 12}]-(Robb)-[:INTERACTS {weight: 4}]->(Ramsay)>
<Record p=(Drogo)<-[:INTERACTS {weight: 18}]-(Daenerys)-[:INTERACTS {weight: 5}]->(Robert)<-[:INTERACTS {weight: 10}]-(Eddard)-[:INTERACTS {weight: 13}]->(Robb)-[:INTERACTS {weight: 4}]->(Ramsay)>
<Record p=(Drogo)<-[:INTERACTS {weight: 18}]-(Daenerys)-[:INTERACTS {weight: 5}]->(Robert)<-[:INTERACTS {weight: 4}]-(Arya)<-[:INTERACTS {weight: 15}]-(Robb)-[:INTERACTS {weight: 4}]->(Ramsay)>

Centrality measures 中心度度量

给出网络中节点的重要性的相对度量。有许多不同的方式来度量中心度,每种方式都代表不同类型的“重要性”。

Degree centrality 度中心性

度中心性是最简单度量,即为某个节点在网络中的联结数。在《权力的游戏》的图中,某个角色的度中心性是指该角色接触的其他角色数。作者使用Cypher计算度中心性:

for r in graph.run('''
MATCH (c:Character)-[:INTERACTS]-()
RETURN c.name AS character, count(*) AS degree ORDER BY degree DESC LIMIT 10
'''):
print(r)
<Record character='Tyrion' degree=36>
<Record character='Sansa' degree=26>
<Record character='Jon' degree=26>
<Record character='Robb' degree=25>
<Record character='Jaime' degree=24>
<Record character='Tywin' degree=22>
<Record character='Cersei' degree=20>
<Record character='Arya' degree=19>
<Record character='Catelyn' degree=18>
<Record character='Joffrey' degree=18>

从上面可以发现,在《权力的游戏》网络中提利昂·兰尼斯特(Tyrion)和最多的角色有接触。鉴于他的心计,我们觉得这是有道理的。

Weighted degree centrality 加权度中心性

作者存储一对角色接触的次数作为INTERACTS关系的weight属性。对该角色的INTERACTS关系的所有weight相加得到加权度中心性。作者使用Cypher计算所有角色的这个度量:

for r in graph.run('''
MATCH (c:Character)-[r:INTERACTS]-()
RETURN c.name AS character, sum(r.weight) AS weightedDegree ORDER BY weightedDegree DESC LIMIT 10
'''):
print(r)
<Record character='Tyrion' weightedDegree=551>
<Record character='Jon' weightedDegree=442>
<Record character='Sansa' weightedDegree=383>
<Record character='Jaime' weightedDegree=372>
<Record character='Bran' weightedDegree=344>
<Record character='Robb' weightedDegree=342>
<Record character='Samwell' weightedDegree=282>
<Record character='Arya' weightedDegree=269>
<Record character='Joffrey' weightedDegree=255>
<Record character='Daenerys' weightedDegree=232>

Betweenness centrality 介数中心性

介数中心性:在网络中,一个节点的介数中心性是指其它两个节点的所有最短路径都经过这个节点,则这些所有最短路径数即为此节点的介数中心性。介数中心性是一种重要的度量,因为它可以鉴别出网络中的“信息中间人”或者网络聚类后的联结点。

for r in graph.run('''
CALL algo.betweenness.stream('Character')
YIELD nodeId, centrality
MATCH (c:Character) WHERE id(c) = nodeId
RETURN c.name AS c,centrality
ORDER BY centrality DESC limit 10;
'''):
print(r)
<Record c='Tyrion' centrality=332.97460317460315>
<Record c='Samwell' centrality=244.6357142857143>
<Record c='Stannis' centrality=226.20476190476188>
<Record c='Robert' centrality=208.62301587301593>
<Record c='Mance' centrality=138.66666666666669>
<Record c='Jaime' centrality=119.99563492063493>
<Record c='Sandor' centrality=114.33333333333333>
<Record c='Jon' centrality=111.26666666666667>
<Record c='Janos' centrality=90.65>
<Record c='Aemon' centrality=64.59761904761905>

Closeness centrality 紧度中心性

紧度中心性是指到网络中所有其他角色的平均距离的倒数。在图中,具有高紧度中心性的节点在聚类社区之间被高度联结,但在社区之外不一定是高度联结的。

cql = '''CALL algo.closeness.stream('Character', 'INTERACTS')
YIELD nodeId, centrality
MATCH (c:Character) WHERE id(c) = nodeId
RETURN c.name AS c,centrality
ORDER BY centrality DESC limit 10;'''
for r in graph.run(cql):
print(r)
<Record c='Tyrion' centrality=0.5120772946859904>
<Record c='Sansa' centrality=0.5096153846153846>
<Record c='Robert' centrality=0.5>
<Record c='Robb' centrality=0.48847926267281105>
<Record c='Arya' centrality=0.48623853211009177>
<Record c='Jon' centrality=0.4796380090497738>
<Record c='Jaime' centrality=0.4796380090497738>
<Record c='Stannis' centrality=0.4796380090497738>
<Record c='Tywin' centrality=0.4690265486725664>
<Record c='Eddard' centrality=0.4608695652173913>

Using python-igraph 使用python-igraph

Neo4j与其它工具(比如,R和Python数据科学工具)完美结合。我们继续使用apoc运行 PageRank和社区发现(community detection)算法。这里接着使用python-igraph计算分析。Python-igraph移植自R的igraph图形分析库。 使用pip install python-igraph安装它。

Building an igraph instance from Neo4j 构建一个igraph实例

为了在《权力的游戏》的数据的图分析中使用igraph,首先需要从Neo4j拉取数据,用Python建立igraph实例。作者使用 Neo4j 的Python驱动库py2neo。我们能直接传入Py2neo查询结果对象到igraph的TupleList构造器,创建igraph实例:

#! pip install python-igraph
from igraph import Graph as IGraph 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) ig
<igraph.Graph at 0x2089631d68>

现在有了igraph对象,可以运行igraph实现的各种图算法了。

PageRank

PageRank算法源自Google的网页排名。它是一种特征向量中心性(eigenvector centrality)算法。

在igraph实例中运行PageRank算法,然后把结果写回Neo4j,在角色节点创建一个pagerank属性存储igraph计算的值:

PageRank

# Calculates the Google PageRank values of a graph.
pg = ig.pagerank() pgvs = []
# ig.vs:图的顶点序列
for p in zip(ig.vs, pg):
pgvs.append({"name": p[0]["name"], "pg": p[1]})
print(pgvs[:5]) 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)
[{'name': 'Stannis', 'pg': 0.018020131765195593}, {'name': 'Aemon', 'pg': 0.007328980991947571}, {'name': 'Robert', 'pg': 0.022292016521362857}, {'name': 'Jon', 'pg': 0.035828696691635555}, {'name': 'Alliser', 'pg': 0.005162125869510499}]

<py2neo.database.Cursor at 0x208962d7f0>

现在可以在Neo4j的图中查询最高PageRank值的节点:

for r in graph.run('''
MATCH (n:Character)
RETURN n.name AS name, n.pagerank AS pagerank ORDER BY pagerank DESC LIMIT 10
'''):
print(r)
('name': 'Tyrion', 'pagerank': 0.042884981999963316)
('name': 'Jon', 'pagerank': 0.03582869669163558)
('name': 'Robb', 'pagerank': 0.03017114665594764)
('name': 'Sansa', 'pagerank': 0.030009716660108578)
('name': 'Daenerys', 'pagerank': 0.02881425425830273)
('name': 'Jaime', 'pagerank': 0.028727587587471206)
('name': 'Tywin', 'pagerank': 0.02570016262642541)
('name': 'Robert', 'pagerank': 0.022292016521362864)
('name': 'Cersei', 'pagerank': 0.022287327589773507)
('name': 'Arya', 'pagerank': 0.022050209663844467)

Community detection

社区发现算法用来找出图中的社区聚类。作者使用igraph实现的随机游走算法( walktrap)来找到在社区中频繁有接触的角色社区,在社区之外角色不怎么接触。

在igraph中运行随机游走的社区发现算法,然后把社区发现的结果导入Neo4j,其中每个角色所属的社区用一个整数来表示:

clusters = IGraph.community_walktrap(ig, weights="weight").as_clustering()

nodes = [{"name": node["name"]} for node in ig.vs]
for node in nodes:
idx = ig.vs.find(name=node["name"]).index
node["community"] = clusters.membership[idx] print(nodes[:5]) write_clusters_query = '''
UNWIND {nodes} AS n
MATCH (c:Character) WHERE c.name = n.name
SET c.community = toInt(n.community)
''' graph.run(write_clusters_query, nodes=nodes)
[{'name': 'Stannis', 'community': 0}, {'name': 'Aemon', 'community': 1}, {'name': 'Robert', 'community': 2}, {'name': 'Jon', 'community': 1}, {'name': 'Alliser', 'community': 1}]

<py2neo.database.Cursor at 0x208962ae48>

我们能在Neo4j中查询有多少个社区以及每个社区的成员数:

for r in graph.run('''
MATCH (c:Character)
WITH c.community AS cluster, collect(c.name) AS members
RETURN cluster, members ORDER BY cluster ASC
'''):
print(r)
<Record cluster=0 members=['Davos', 'Melisandre', 'Shireen', 'Stannis', 'Cressen', 'Salladhor']>
<Record cluster=1 members=['Aemon', 'Alliser', 'Craster', 'Eddison', 'Gilly', 'Janos', 'Jon', 'Mance', 'Rattleshirt', 'Samwell', 'Val', 'Ygritte', 'Grenn', 'Karl', 'Bowen', 'Dalla', 'Orell', 'Qhorin', 'Styr']>
<Record cluster=2 members=['Aerys', 'Amory', 'Balon', 'Brienne', 'Bronn', 'Cersei', 'Gregor', 'Jaime', 'Joffrey', 'Jon Arryn', 'Kevan', 'Loras', 'Lysa', 'Meryn', 'Myrcella', 'Oberyn', 'Podrick', 'Renly', 'Robert', 'Robert Arryn', 'Sansa', 'Shae', 'Tommen', 'Tyrion', 'Tywin', 'Varys', 'Walton', 'Petyr', 'Elia', 'Ilyn', 'Pycelle', 'Qyburn', 'Margaery', 'Olenna', 'Marillion', 'Ellaria', 'Mace', 'Chataya', 'Doran']>
<Record cluster=3 members=['Arya', 'Beric', 'Eddard', 'Gendry', 'Sandor', 'Anguy', 'Thoros']>
<Record cluster=4 members=['Brynden', 'Catelyn', 'Edmure', 'Hoster', 'Lothar', 'Rickard', 'Robb', 'Roose', 'Walder', 'Jeyne', 'Roslin', 'Ramsay']>
<Record cluster=5 members=['Belwas', 'Daario', 'Daenerys', 'Irri', 'Jorah', 'Missandei', 'Rhaegar', 'Viserys', 'Barristan', 'Illyrio', 'Drogo', 'Aegon', 'Kraznys', 'Rakharo', 'Worm']>
<Record cluster=6 members=['Bran', 'Hodor', 'Jojen', 'Luwin', 'Meera', 'Rickon', 'Nan', 'Theon']>
<Record cluster=7 members=['Lancel']>

Visualization

See neovis.js

使用Neo4j分析《权力的游戏》的更多相关文章

  1. GraphX 在图数据库 Nebula Graph 的图计算实践

    不同来源的异构数据间存在着千丝万缕的关联,这种数据之间隐藏的关联关系和网络结构特性对于数据分析至关重要,图计算就是以图作为数据模型来表达问题并予以解决的过程. 一.背景 随着网络信息技术的飞速发展,数 ...

  2. 主流图数据库Neo4J、ArangoDB、OrientDB综合对比:架构分析

    主流图数据库Neo4J.ArangoDB.OrientDB综合对比:架构分析 YOTOY 关注 0.4 2017.06.15 15:11* 字数 3733 阅读 16430评论 2喜欢 18 1: 本 ...

  3. neo4j源码分析1-编译打包启动

    date: 2018-03-22 title: "neo4j源码分析1-编译打包启动" author: "邓子明" tags: - 源码 - neo4j - 大 ...

  4. Neo4j源代码分析

    1.下载neo4j源码 https://github.com/neo4j/neo4j/ 参考文章 原文地址:https://blog.csdn.net/e15273/article/details/7 ...

  5. 图形数据库、NOSQL和Neo4j

    简介 在众多不同的数据模型里,关系数据模型自80年代就处于统治地位,而且有不少实现,如Oracle.MySQL和MSSQL,它们也被称为关系数据库管理系统(RDBMS).然而,最近随着关系数据库使用案 ...

  6. Neo4j入门之中国电影票房排行浅析

    什么是Neo4j?   Neo4j是一个高性能的NoSQL图形数据库(Graph Database),它将结构化数据存储在网络上而不是表中.它是一个嵌入式的.基于磁盘的.具备完全的事务特性的Java持 ...

  7. (二)图数据neo4j基本认识

    1.neo4j介绍 Neo4j是由Java和Scala实现的开源NoSQL图数据库.自2003年开始研发,直到2007年正式发布第一版.Neo4j的源代码托管在GitHub上,技术支持托管在Stack ...

  8. 抓包分析、多线程爬虫及xpath学习

    1.抓包分析 1.1 Fiddler安装及基本操作 由于很多网站采用的是HTTPS协议,而fiddler默认不支持HTTPS,先通过设置使fiddler能抓取HTTPS网站,过程可参考(https:/ ...

  9. 域渗透分析神器BloodHound

    一.安装过程: 1.首先安装JDK,要求是JDK8 JDK8下载地址 windows下按照提示自动安装好久可以了 2.安装neo4j neo4j图数据库下载地址 下载好后,进入对应的目录在命令行运行如 ...

随机推荐

  1. 【转载】Linux 软件安装到 /usr,/usr/local/ 还是 /opt 目录?

    Linux 的软件安装目录是也是有讲究的,理解这一点,在对系统管理是有益的 /usr:系统级的目录,可以理解为C:/Windows/ /usr/lib:理解为C:/Windows/System32. ...

  2. csdr Makefile for openwrt(纯粹笔记,暂未成功)

    1.自已学着写的Makefile给csdr在openwrt平台上使用 参照:https://blog.csdn.net/lvshaorong/article/details/54668220 incl ...

  3. python自定义ORM并操作数据库

    看这个代码之前先去看上篇文章,理解type的用法及元类的含义: ORM可以代替pymysql,实现将python语义装换为sql语句,简单化 import pymysql ''' metaclass, ...

  4. 动态规划——python

    1.爬楼梯问题一个人爬楼梯,每次只能爬1个或两个台阶,假设有n个台阶,那么这个人有多少种不同的爬楼梯方法 动态规划的状态转移:第 i 个状态的方案数和第 i-1, i-2时候的状态有关,即:dp[i] ...

  5. Sketch 61 UI设计必备软件下载

    UI设计必备软件Sketch 61破解版下载已经全新上线啦!Sketch 61是一个创新的矢量绘图软件,拥有简约的设计,调色板,面板,菜单,窗口和控件和功能强大的矢量绘图和文字工具,包含完美的布尔运算 ...

  6. springboot整合freemarker模板引擎后在页面获取basePath绝对路径

    在项目中引用静态资源文件或者进行ajax请求时我们有时候会使用 ${basePath} ,其实这就是一种获取绝对路径的方式: 那么在springboot项目中要怎么配置才能使用 basePaht呢? ...

  7. Dymola — 多学科系统仿真平台

            Dymola 是法国Dassault Systems公司的多学科系统仿真平台,广泛应用于国内外汽车.工业.交通.能源等行业的系统总体架构设计.指标分解以及系统功能验证及优化等.Dymo ...

  8. GIS 基础知识简介

    前言 前一段时间,在公司进行了分析 GIS 基础信息的介绍.之所以会有这个介绍以及为什么是我?这个个中缘由说下. 公司不是一个GIS方面的公司,但是由于业务的需要,经常需要用到地图(要和地图打交道), ...

  9. C# String 字符拼接测试(“+”、string.Format、StringBuilder 比较)

    对于字符串的拼接自己一直有疑问,在何时该用什么方法来拼接?哪种方法更好.更适合. 几种方法 1.“+” 拼接字符串 现在在 C# 中,字符串进行拼接,可以直接用 “+” 而且可以直接用于数字类型的而不 ...

  10. Pycharm----【Mac】设置默认模板

    使用场景:新建的文件中,有某些字段或者代码段是每次都需要写入的,因此为了编写的方便,我们会创建对应的模板,每次新建选择模板即可. 操作步骤如下: pycharm--->preference--- ...