Datawhale 知识图谱组队学习 之 Task 3 Neo4j图数据库导入数据

一、引言

在图计算中,基本的数据结构表达式是:G=(V,E),V=vertex(节点),E=edge(边)。图形结构的数据结构一般以节点和边来表现,也可以在节点上增加键值对属性。图数据库是 NoSQL(非关系型数据库)的一种,它应用图形数据结构的特点(节点、属性和边)存储数据实体和相互之间的关系信息。

Neo4j 是当前较为主流和先进的原生图数据库之一,提供原生的图数据存储、检索和处理。

Neo4j 查询的高性能表现、易于使用的特性及其设计的灵活性和开发的敏捷性,以及坚如磐石般的事务管理特性,都充分说明了使用Neo4j是一个不错的选择。有关它的所有优点,总结起来,主要表现在以下几个方面。

  1. 闪电般的读/写速度,无与伦比的高性能表现;
  2. 非结构化数据存储方式,在数据库设计上具有很大的灵活性;
  3. 能很好地适应需求变化,并适合使用敏捷开发方法;
  4. 很容易使用,可以用嵌入式、服务器模式、分布式模式等方式来使用数据库;
  5. 使用简单框图就可以设计数据模型,方便建模;
  6. 图数据的结构特点可以提供更多更优秀的算法设计;
  7. 完全支持ACID完整的事务管理特性;
  8. 提供分布式高可用模式,可以支持大规模的数据增长;
  9. 数据库安全可靠,可以实时备份数据,很方便恢复数据;
  10. 图的数据结构直观而形象地表现了现实世界的应用场景。

二、Neo4j导入数据的简单使用

python操作图数据库neo4j的两种方式

三、Neo4j 数据导入

3.1 数据集简介

  • 数据源:39健康网。包括15项信息,其中7类实体,约3.7万实体,21万实体关系。

  • 本次组队学习搭建的系统的知识图谱结构如下:

知识图谱结构

知识图谱实体类型

知识图谱实体关系类型

知识图谱疾病属性

基于特征词分类的方法来识别用户查询意图

3.2 数据导入

3.2.1 Neo4j 账号密码设置

要将 数据 导入 Neo4j 图数据库,首先需要 进入 build_graph.py 类中,在 类 MedicalGraph 中 的加入 本地 Neo4j 图数据库 的 账号和密码;

    class MedicalGraph:
        def __init__(self):
            ...
            self.graph = Graph("http://localhost:7474", username="neo4j", password="自己的")
            ...

3.2.2 导入 数据

运行 以下命令:

    python build_graph.py 

注:由于数据量比较大,所以该过程需要运行几个小时

3.2.3 使用 ./neo4j-admin import 导入数据 【3G 的数据 十几分钟搞定】

【注:需要转成CSV;必须停止neo4j;只能生成新的数据库,而不能在已存在的数据库中插入数据。】

  1. 关闭 Neo4j 图数据库
  2. 删除 Neo4j 目录下的 data/databases/ 中信息
  3. 将 node.csv 和 relation.csv 放到 Neo4j 目录下的 import/ 中
  4. 在 bin/ 目录下,调用导入命令:
  • 代码
        ./neo4j-admin.bat import --mode csv  --database ../databases/graph.db --id-type string --nodes:Entity ../import/node.csv --relationships:Rel ../import/relation.csv
  • 输出
    Neo4j version: 3.5.0
    
    ... 

    IMPORT DONE in 3m 37s 15ms.
    Imported:
      7486600 nodes
      20000000 relationships
      14973200 properties
    Peak memory usage: 1.10 GB

3.3 主体类 MedicalGraph 介绍

class MedicalGraph:
    def __init__(self):
        pass
    
    # 读取文件,获得实体,实体关系
    def read_file(self):
        psss
    # 创建节点
    def create_node(self, label, nodes):
        pass
    # 创建疾病节点的属性
    def create_diseases_nodes(self, disease_info):
        pass
    # 创建知识图谱实体
    def create_graphNodes(self):
        pass
    # 创建实体关系边
    def create_graphRels(self):
        pass
    # 创建实体关系边
    def create_relationship(self, start_node, end_node, edges, rel_type, rel_name):
        pass

3.4 主体类 MedicalGraph 中关键代码讲解

  • 获取数据路径
    cur_dir = '/'.join(os.path.abspath(__file__).split('/')[:-1])
    self.data_path = os.path.join(cur_dir, 'DATA/disease.csv')
  • 链接 Neo4j 图数据库
    self.graph = Graph("http://localhost:7474", username="neo4j", password="自己设定的密码")
  • 读取文件,获得实体,实体关系

这部分代码的核心就是读取 数据文件,并 获取实体和实体关系信息。

  • 实体信息:
    • diseases 疾病
    • aliases 别名
    • symptoms 症状
    • parts 部位
    • departments 科室
    • complications 并发症
    • drugs 药品
  • 实体关系:
    • disease_to_symptom 疾病与症状关系
    • disease_to_alias 疾病与别名关系
    • diseases_to_part 疾病与部位关系
    • disease_to_department 疾病与科室关系
    • disease_to_complication 疾病与并发症关系
    • disease_to_drug 疾病与药品关系
  • disease 实体 属性信息:
    • name
    • age 年龄
    • infection 传染性
    • insurance 医保
    • checklist 检查项
    • treatment 治疗方法
    • period 治愈周期
    • rate 治愈率
    • money 费用
    def read_file(self):
        """
        读取文件,获得实体,实体关系
        :return:
        """
        # cols = ["name", "alias", "part", "age", "infection", "insurance", "department", "checklist", "symptom",
        #         "complication", "treatment", "drug", "period", "rate", "money"]
        # 实体
        diseases = []  # 疾病
        aliases = []  # 别名
        symptoms = []  # 症状
        parts = []  # 部位
        departments = []  # 科室
        complications = []  # 并发症
        drugs = []  # 药品

        # 疾病的属性:age, infection, insurance, checklist, treatment, period, rate, money
        diseases_infos = []
        # 关系
        disease_to_symptom = []  # 疾病与症状关系
        disease_to_alias = []  # 疾病与别名关系
        diseases_to_part = []  # 疾病与部位关系
        disease_to_department = []  # 疾病与科室关系
        disease_to_complication = []  # 疾病与并发症关系
        disease_to_drug = []  # 疾病与药品关系

        all_data = pd.read_csv(self.data_path, encoding='gb18030').loc[:, :].values
        for data in all_data:
            disease_dict = {}  # 疾病信息
            # 疾病
            disease = str(data[0]).replace("...", " ").strip()
            disease_dict["name"] = disease
            # 别名
            line = re.sub("[,、;,.;]", " ", str(data[1])) if str(data[1]) else "未知"
            for alias in line.strip().split():
                aliases.append(alias)
                disease_to_alias.append([disease, alias])
            # 部位
            part_list = str(data[2]).strip().split() if str(data[2]) else "未知"
            for part in part_list:
                parts.append(part)
                diseases_to_part.append([disease, part])
            # 年龄
            age = str(data[3]).strip()
            disease_dict["age"] = age
            # 传染性
            infect = str(data[4]).strip()
            disease_dict["infection"] = infect
            # 医保
            insurance = str(data[5]).strip()
            disease_dict["insurance"] = insurance
            # 科室
            department_list = str(data[6]).strip().split()
            for department in department_list:
                departments.append(department)
                disease_to_department.append([disease, department])
            # 检查项
            check = str(data[7]).strip()
            disease_dict["checklist"] = check
            # 症状
            symptom_list = str(data[8]).replace("...", " ").strip().split()[:-1]
            for symptom in symptom_list:
                symptoms.append(symptom)
                disease_to_symptom.append([disease, symptom])
            # 并发症
            complication_list = str(data[9]).strip().split()[:-1] if str(data[9]) else "未知"
            for complication in complication_list:
                complications.append(complication)
                disease_to_complication.append([disease, complication])
            # 治疗方法
            treat = str(data[10]).strip()[:-4]
            disease_dict["treatment"] = treat
            # 药品
            drug_string = str(data[11]).replace("...", " ").strip()
            for drug in drug_string.split()[:-1]:
                drugs.append(drug)
                disease_to_drug.append([disease, drug])
            # 治愈周期
            period = str(data[12]).strip()
            disease_dict["period"] = period
            # 治愈率
            rate = str(data[13]).strip()
            disease_dict["rate"] = rate
            # 费用
            money = str(data[14]).strip() if str(data[14]) else "未知"
            disease_dict["money"] = money

            diseases_infos.append(disease_dict)

        return set(diseases), set(symptoms), set(aliases), set(parts), set(departments), set(complications), \
                set(drugs), disease_to_alias, disease_to_symptom, diseases_to_part, disease_to_department, \
                disease_to_complication, disease_to_drug, diseases_infos
  • 创建节点

这部分代码主要是为了创建不包含属性的 节点

    def create_node(self, label, nodes):
        """
        创建节点
        :param label: 标签
        :param nodes: 节点
        :return:
        """
        count = 0
        for node_name in nodes:
            node = Node(label, name=node_name)
            self.graph.create(node)
            count += 1
            print(count, len(nodes))
        return
  • 创建带有属性节点
    def create_diseases_nodes(self, disease_info):
        """
        创建疾病节点的属性
        :param disease_info: list(Dict)
        :return:
        """
        count = 0
        for disease_dict in disease_info:
            node = Node("Disease", name=disease_dict['name'], age=disease_dict['age'],
                        infection=disease_dict['infection'], insurance=disease_dict['insurance'],
                        treatment=disease_dict['treatment'], checklist=disease_dict['checklist'],
                        period=disease_dict['period'], rate=disease_dict['rate'],
                        money=disease_dict['money'])
            self.graph.create(node)
            count += 1
            print(count)
        return
  • 创建知识图谱实体
    def create_graphNodes(self):
        """
        创建知识图谱实体
        :return:
        """
        disease, symptom, alias, part, department, complication, drug, rel_alias, rel_symptom, rel_part, \
        rel_department, rel_complication, rel_drug, rel_infos = self.read_file()
        self.create_diseases_nodes(rel_infos)
        self.create_node("Symptom", symptom)
        self.create_node("Alias", alias)
        self.create_node("Part", part)
        self.create_node("Department", department)
        self.create_node("Complication", complication)
        self.create_node("Drug", drug)

        return
  • 创建知识图谱关系
    def create_graphRels(self):
        disease, symptom, alias, part, department, complication, drug, rel_alias, rel_symptom, rel_part, \
        rel_department, rel_complication, rel_drug, rel_infos = self.read_file()

        self.create_relationship("Disease", "Alias", rel_alias, "ALIAS_IS", "别名")
        self.create_relationship("Disease", "Symptom", rel_symptom, "HAS_SYMPTOM", "症状")
        self.create_relationship("Disease", "Part", rel_part, "PART_IS", "发病部位")
        self.create_relationship("Disease", "Department", rel_department, "DEPARTMENT_IS", "所属科室")
        self.create_relationship("Disease", "Complication", rel_complication, "HAS_COMPLICATION", "并发症")
        self.create_relationship("Disease", "Drug", rel_drug, "HAS_DRUG", "药品")
  • 创建实体关系边
    def create_relationship(self, start_node, end_node, edges, rel_type, rel_name):
        """
        创建实体关系边
        :param start_node:
        :param end_node:
        :param edges:
        :param rel_type:
        :param rel_name:
        :return:
        """
        count = 0
        # 去重处理
        set_edges = []
        for edge in edges:
            set_edges.append('###'.join(edge))
        all = len(set(set_edges))
        for edge in set(set_edges):
            edge = edge.split('###')
            p = edge[0]
            q = edge[1]
            query = "match(p:%s),(q:%s) where p.name='%s'and q.name='%s' create (p)-[rel:%s{name:'%s'}]->(q)" % (
                start_node, end_node, p, q, rel_type, rel_name)
            try:
                self.graph.run(query)
                count += 1
                print(rel_type, count, all)
            except Exception as e:
                print(e)
        return

四、总结

Neo4j是一个高性能的,NOSQL图形数据库,它将结构化数据存储在网络上而不是表中。Neo4j也可以被看作是一个高性能的图引擎,该引擎具有成熟数据库的所有特性。Neo4j是一个高度可扩展的本机图形数据库,旨在专门利用数据和数据关系。使用Neo4j,开发人员可以构建智能应用程序,以实时遍历当今大型的,相互关联的数据集。大家工作在一个面向对象的、灵活的网络结构下而不是严格、静态的表中,但是可以享受到具备完全的事务特性、企业级的数据库的所有好处。

Neo4j因其嵌入式、高性能、轻量级等优势,越来越受到关注。

posted @ 2021-01-13 20:49  Ethan_CW  阅读(238)  评论(0编辑  收藏  举报