51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Neo4j 构建关系图谱

数据来源:http://www.openkg.cn/dataset/personrelationgraph,该数据集仅仅用来做学习之用。构建的过程主要包括两步:

  1. 将数据按照关系存储到 neo4j 数据库中;
  2. 输入名字可视化其关系人物。
import pandas as pd
from py2neo import Graph
from py2neo import Node
from py2neo import NodeMatcher
from py2neo import Relationship
from py2neo import RelationshipMatcher
from py2neo import Subgraph
from tqdm import tqdm
import networkx as nx
import matplotlib.pyplot as plt
import time


# 连接 Neo4j 数据库
graph = Graph('http://localhost:7474', auth=('neo4j', 'neo4j'))
  1. 存储人物关系 {#title-0} ====================

构建人物关系对象入库时,可以使用 Subgraph 批量插入数据,效率更高一些。所以,下面代码在实现的时,先将构建好的 Node 和 Relation 对象存储在 nodes 和 relations 容器中,最后使用 Subgraph 批量插入结点和关系对象。

def build_relation():

    # 清空图数据库
    graph.delete_all()
    # 读取关系数据
    person_data = pd.read_csv('personrelkg.data', usecols=['人物1', '大类关系', '人物2'])
    # 删除缺失值
    person_data = person_data.dropna()
    # 初始化进度条
    from colorama import Fore
    progress_bar = tqdm(range(len(person_data)-1), desc='构建对象')

    nodes = {}
    def create_node(name):

        # 去掉[]中的说明, 例如: 邹凯[中国体操运动员]
        name = re.sub(r'\[.*\]', '', name)
        if name not in nodes:
            nodes[name] = Node('Person', name=name)
        return nodes[name]

    relations = []
    for row_data, _ in zip(person_data.to_numpy(), progress_bar):
        name1, rname, name2 = row_data
        # 插入结点对象
        person1 = create_node(name1)
        person2 = create_node(name2)
        # 构建结点关系
        relation = Relationship(person1, rname, person2)
        relations.append(relation)

    progress_bar.close()
    # 结点关系 Neo4j 存储
    start = time.time()
    print('开始入库, 数量: %6s 条' % len(relations))
    subgraph = Subgraph(nodes.values(), relations)
    graph.create(subgraph)
    progress_bar.update()
    print('完成入库, 用时: %s 秒' % str(time.time() - start)[:6])


if __name__ == '__main__':
    build_relation()

程序执行结果:

构建对象: 100%|██████████| 97140/97140 [00:07<00:00, 12354.25it/s]
开始入库, 数量:  97140 条
完成入库, 用时: 8.0692 秒
  1. 查询人物关系 {#title-1} ====================

给定人物名字找到其直接关联的人物关系。下面是两种方法,直接 run cypher 的方式比第一种方式效率要好很多。最后绘图,绘图的样式取决于使用的 layout。

def query_relation1(name):

    start_name = name
    start_node = NodeMatcher(graph).match('Person', name=start_name).first()
    if start_node is None:
        return
    relations = RelationshipMatcher(graph).match([start_node]).all()

    return_result = {}
    for relation in relations:
        rtype = type(relation).__name__
        if rtype not in return_result:
            return_result[rtype] = []

        snode, enode = relation.start_node, relation.end_node
        enode_name = enode['name']
        return_result[rtype].append(enode_name)

    # 绘图
    draw_relation(start_name, return_result)


def query_relation2(name):

    start_name = name
    cql = 'match (:Person{name:"%s"})-[r]->(n) ' % start_name
    cql += 'return type(r) as relation, n.name as name'
    query_result = graph.run(cql).data()

    return_result = {}
    for result in query_result:
        pname, relation = result['name'], result['relation']
        if relation not in return_result:
            return_result[relation] = []
        return_result[relation].append(pname)

    # 绘图
    draw_relation(start_name, return_result)


def draw_relation(start_name, return_result):

    G = nx.Graph()
    for relation, end_names in return_result.items():
        for end_name in end_names:
            G.add_edge(start_name, end_name, relation=relation)

    plt.figure(figsize=(30, 20), dpi=100)
    plt.title(start_name + '关系图', fontsize=30, fontweight='bold')
    pos = nx.planar_layout(G)
    # pos = nx.spring_layout(G, pos=pos)
    nx.draw_networkx(G,
                     pos=pos,
                     arrows=True,
                     arrowstyle='->',
                     arrowsize=15,
                     node_shape='',
                     font_size=12,
                     edge_color='red')

    # 设置边标签
    edge_labels = nx.get_edge_attributes(G, 'relation')
    nx.draw_networkx_edge_labels(G,
                                 pos=pos,
                                 edge_labels=edge_labels,
                                 font_size=10)
    plt.show()


if __name__ == '__main__':
    query_relation1('刘德华')
    query_relation2('刘德华')

程序输出结果:

下面是查询两个结点之间的最短关系路径:

def query_shortest_relation(name1, name2):

    cql =  'MATCH (p1:Person{name:"%s"}), (p2:Person{name:"%s"}), '  % (name1, name2)
    cql += 'p=allshortestpaths((p1)-[*..10]->(p2)) '
    cql += 'RETURN p'

    query_result = graph.run(cql).data()
    if query_result == []:
        print(name1, name2, '不存在')
        return

    G = nx.DiGraph()
    for result in query_result:
        result = result['p']
        for node in result:
            sname = node.start_node['name']
            ename = node.end_node['name']
            relation = type(node).__name__
            G.add_edge(sname, ename, relation=relation)
            print(sname, relation, ename)
        print('-' * 50)


    pos = nx.spring_layout(G, seed=88)
    nx.draw_networkx(G,
                     pos=pos,
                     node_shape='',
                     arrows=True,
                     arrowstyle='->',
                     arrowsize=15,)

    edge_labels = nx.get_edge_attributes(G, 'relation')
    nx.draw_networkx_edge_labels(G,
                                 pos=pos,
                                 edge_labels=edge_labels,
                                 font_size=10)
    plt.show()


if __name__ == '__main__':
    query_shortest_relation('樊少皇', '宋祖英')

程序输出结果中箭头方向表示关系方向:

樊少皇 合作 周迅
周迅 欺骗 王林
王林 忽悠 李丹阳
李丹阳 同学 宋祖英
--------------------------------------------------
樊少皇 合作 李连杰
李连杰 欺骗 王林
王林 忽悠 李丹阳
李丹阳 同学 宋祖英
--------------------------------------------------
樊少皇 合作 周迅
周迅 欺骗 王林
王林 忽悠 戴玉强
戴玉强 同学 宋祖英
--------------------------------------------------
樊少皇 合作 李连杰
李连杰 欺骗 王林
王林 忽悠 戴玉强
戴玉强 同学 宋祖英
--------------------------------------------------
赞(4)
未经允许不得转载:工具盒子 » Neo4j 构建关系图谱