知识图谱-入门项目

前面简单介绍了知识图谱:知识图谱入门-CSDN博客

如何在Windows中安装Neo4j:Windows 安装 Neo4j(2025最新·极简)-CSDN博客

基于前面的基础,现在一起来学习一个简单的小案例,走通从构建结构化数据 → 将数据构建成节点表,关系表 → 导入到Neo4j中进行图形绘制和编写Cypher语句回答问题的整个流程。

目录

[1. 项目介绍](#1. 项目介绍)

[2. 下载项目](#2. 下载项目)

[3. 知识图谱构建](#3. 知识图谱构建)

[3.1 知识抽取](#3.1 知识抽取)

知识图谱要解决什么问题?

任务1:从网页中抽取董事会的信息

任务2:获取股票行业和概念的信息

任务3:设计知识图谱

[3.2 知识融合](#3.2 知识融合)

[3.3 知识存储](#3.3 知识存储)

[任务1:准备 CSV 文件](#任务1:准备 CSV 文件)

任务2:停止Neo4j服务

任务3:执行导入命令

[任务4:重启 neo4j 服务](#任务4:重启 neo4j 服务)

任务5:查询

4.问题


1. 项目介绍

stock-knowledge-graph项目是开源的中文 A 股投资研究知识图谱,目的是找出值得买、卖的"投资标的",并给出合理的价位或者时机。适合零基础快速体验知识图谱构建全流程。

2. 下载项目

项目来源: https://github.com/lemonhu/stock-knowledge-graph

进入链接,点击 Code -> Download ZIP,解压后用 Pycharm 打开项目,配置环境并安装依赖(这里不做过多讲解,不会的同学可以找一个学习链接)。该项目的依赖在 requirements.txt 中,包括

  • HTML/XML处理库--lxml

  • 数据分析库--pandas

  • 超文本解析库--beautifulsoup4

  • 金融数据接口库--tushare

在 PyCharm 底部打开 Terminal,执行下面命令:

bash 复制代码
pip install -r requirements.txt

个人喜欢这种解压缩包的方式,大家也可以直接用git通过链接在 Pycharm 中拉取项目。

目录工程结构:

1、所有数据都存放在 data 文件夹:

stockpage.zip:同花顺董事页面 html 文件。

**结构化数据:**分别从 stockpage.zip 中爬取的董事人员信息表executive_prep.csv、股票行业信息表 stock_industry_prep.csv、股票信息表 stock_concept_prep.csv。

import 文件夹:分别存放从上面三个信息表中提取出来的

  • 4个实体表("人"实体表executive.csv、概念实体表 concept.csv、行业实体表 industry.csv、公司实体表 stock.csv)

  • 3个实体之间的关系表(人和公司关系表 executive_srock.csv 、公司和概念关系表 stock_concept.csv、公司和行业关系表 stock_industry.csv)

2、项目中的代码文件:

**extract.py:**把同花顺董事页面批量解析成结构化的csv。

**stock.py :**调取 Tushare 的证监会行业分类接口和同花顺概念分类接口。

**build_csv.py :**构建 4 个实体表 和 3 个关系表。

3. 知识图谱构建

3.1 知识抽取

前面说知识抽取就是从非结构化或半结构化的文本中抽取实体、关系和属性。那么我们得分析一下要抓取什么信息才能得到我们需要的实体,从而去分析他的关系和属性。

知识图谱要解决什么问题?

可以查询出"这家公司董事长是谁,炒什么题材,同行是谁?"

提问:

  1. 某公司的董事长是谁?

  2. 张三同时在哪些公式任独立董事?

  3. 同行业董事长平均年龄?

  4. 某公司属于哪些概念(标签)?

  5. 某概念下有多少家公司?

  6. 所有严重亏损(ST)的公司名单?

  7. 同行公司列表?

  8. "张三"任职的 ST 公司属于哪些概念?

**总结:**任何人,公司,行业,概念,ST,只要1条查询就能拉出来关联网络。

注:

  • **"概念"**是一群股票被贴上同一个故事标签,例如,所有做电池材料,电芯,设备的公司都属于"锂电池概念"。

  • **"ST"**标记公司为严重亏损状态。

  • **"行业"**以核心业务为准,例如,新能源汽车行业比亚迪。

任务1:从网页中抽取董事会的信息

项目的 /data/stockpage/stockpage.zip中存放的是 同花顺董事页面 html。

现在抽取每一个公司/股票的董事会成员信息,包括董事会成员的["姓名","职务","性别","年龄"]共四个字段。

代码文件为 extract.py,主要是一个网页解析脚本,把"同花顺个股董事页面" html 批量读进来,提取每位高管的姓名、性别、年龄、职位,并写入csv,为后续建知识图谱提供"人-公司-任职"原始数据。

python 复制代码
# -*- coding: utf-8 -*-
import os                 # 遍历文件夹
import csv                # 写 CSV 文件
from lxml import etree    # 解析 HTML 网页


def extract(stockpage_dir, executive_csv):
    """把同花顺董事页面 HTML 批量解析成结构化 CSV
    Args:
        stockpage_dir: 存放 html 的目录
        executive_csv: 输出 csv 完整路径
    """
    # 1. 拿到目录下所有 html 文件全路径,并过滤非 html
    pages = map(lambda _: os.path.join(stockpage_dir, _), os.listdir(stockpage_dir))
    pages = filter(lambda _: _.endswith('html'), pages)

    # 2. 定义输出字段,必须与后续 Neo4j 导入一致
    headers = ['name', 'gender', 'age', 'code', 'jobs']

    # 3. 打开输出文件,用 DictWriter 按字段写行
    with open(executive_csv, 'w', encoding='utf-8') as file_directors:
        file_directors_csv = csv.DictWriter(file_directors, headers)
        file_directors_csv.writeheader()          # 写表头

        # 4. 逐个 html 文件处理
        for page in pages:
            print(page)                           # 调试:看当前处理哪个文件
            file_name = page.split(r'/')[-1]      # 取文件名 000001.html
            code = file_name.split('.')[0]        # 股票代码 = 000001(后续作节点 ID)

            executives = []                       # 当前文件所有董事列表

            # 5. 读 HTML 并解析
            with open(page, 'r', encoding='gbk') as file_page:
                content = file_page.read()
                html = etree.HTML(content)        # lxml 解析成 DOM 树

                # 6. XPath 定位每个董事卡片(同花顺固定结构)
                divs = html.xpath('//div[@id="ml_001"]//div[contains(@class, "person_table")]')

                for div in divs:
                    item = {}                     # 单条董事字典

                    # 7. 提取姓名(在 h3>a 标签内)
                    item['name'] = div.xpath('.//thead/tr/td/h3/a/text()')[0].replace(',', '-')

                    # 8. 提取职位(thead 第 1 行第 2 列)
                    item['jobs'] = div.xpath('.//thead/tr[1]/td[2]/text()')[0].replace(',', '/')

                    # 9. 性别、年龄、学历在同一行,先 split
                    gender_age_education = div.xpath('.//thead/tr[2]/td[1]/text()')[0].split()

                    # 10. 性别在第 1 段,且只能是男/女,否则记 null
                    try:
                        item['gender'] = gender_age_education[0]
                        if item['gender'] not in ('男', '女'):
                            item['gender'] = 'null'
                    except IndexError:
                        item['gender'] = 'null'

                    # 11. 年龄在第 2 段,去掉"岁"字并转 int;失败记 -1
                    try:
                        item['age'] = gender_age_education[1].strip('岁')
                        try:
                            item['age'] = int(item['age'])
                        except ValueError:
                            item['age'] = -1
                    except IndexError:
                        item['age'] = -1

                    # 12. 把当前股票代码写进这条记录
                    item['code'] = code

                    executives.append(item)       # 加入当前文件列表

            # 13. 当前 html 所有董事一次性写进 CSV
            file_directors_csv.writerows(executives)


# 14. 脚本入口,指定输入输出路径
if __name__ == '__main__':
    stockpage_dir = './data/stockpage'              # HTML 目录
    executive_csv = './data/executive_prep.csv'     # 输出宽表
    extract(stockpage_dir, executive_csv)

最后生成一个executive_prep.csv文件,格式如下:

name gender age code(股票代码) jobs
杜玉岱 58 601058 董事长/董事
延万华 45 601058 副董事长/董事
任务2:获取股票行业和概念的信息

股票行业和概念上面已做介绍,该项目利⽤工具Tushare获取这部分信息,官网为http://tushare.org/,使用pip命令进行安装即可。

python 复制代码
pip install tushare

下载完之后,在python里即可调用股票行业和概念信息。参考链接:http://tushare.org/classifying.html#id2,通过 stock.py 代码获得股票行业信息股票概念信息

python 复制代码
import tushare as ts

# 获得并保存股票行业信息
df_industry = ts.get_industry_classified()
df_industry.to_csv("./data/stock_industry_prep.csv", index=False, sep=',')

# 获得并保存股票概念信息
df_concept = ts.get_concept_classified()
df_concept.to_csv("./data/stock_concept_prep.csv", index=False, sep=',')

并把返回的信息分别存储在stock_industry_prep.csv文件和stock_concept_prep.csv文件里。格式如下:

股票行业信息 stock_industry_prep.csv

code name c_name
600051 宁波联合 综合行业
600209 罗顿发展 综合行业
600132 江泉实业 综合行业

股票概念信息stock_concept_prep.csv

code name c_name
600007 中国国贸 外资背景
600114 东睦股份 外资背景
600132 重庆啤酒 外资背景
任务3:设计知识图谱

前面说过,我们可以根据提问去设计知识图谱,下面是实体和关系的设计。

实体:

  • 创建"人"实体,这个人拥有姓名、性别、年龄

  • 创建"公司"实体,除了股票代码,还有股票名称

  • 创建"概念"实体,每个概念都有概念名

  • 创建"行业"实体,每个行业都有⾏业名

  • 给"公司"实体添加"ST"的标记,这个由LABEL来实现

关系:

  • 创建"人"和"公司"的关系,这个关系有董事长、执行董事等等

  • 创建"公司"和"概念"的关系

  • 创建"公司"和"行业"的关系

把设计图存储为design.png文件。

**注:**实体名字和关系名字需要易懂,对于上述的要求,并不一定存在唯一的设计,只要能够覆盖上面这些要求即可。"ST"标记是⽤用来刻画⼀个股票严重亏损的状态,这个可以从给定的股票名字前缀来判断,背景知识可参考百科ST股票,"ST"股票对应列表为['*ST', 'ST', 'S*ST', 'SST']。

build_csv.py 代码获取实体和关系表:

python 复制代码
# -*- coding: utf-8 -*-
import os                 # 遍历文件夹
import csv                # 读写 CSV
import hashlib            # 生成 MD5,用作 Neo4j 唯一 ID


def get_md5(string):
    """根据任意字符串生成 32 位 MD5,用来当 Neo4j 的 :ID"""
    byte_string = string.encode("utf-8")
    md5 = hashlib.md5()
    md5.update(byte_string)
    return md5.hexdigest()          # 返回 32 位小写十六进制


def build_executive(executive_prep, executive_import):
    """把 executive_prep.csv → Neo4j 节点文件 executive.csv
    格式要求:person_id:ID,name,gender,age:int,:LABEL
    LABEL 统一为 Person
    """
    print('Writing to {} file...'.format(executive_import.split('/')[-1]))
    # 同时打开源文件和目标文件
    with open(executive_prep, 'r', encoding='utf-8') as file_prep, \
         open(executive_import, 'w', encoding='utf-8') as file_import:

        file_prep_csv = csv.reader(file_prep, delimiter=',')
        file_import_csv = csv.writer(file_import, delimiter=',')

        # 写 Neo4j bulk-import 要求的表头
        headers = ['person_id:ID', 'name', 'gender', 'age:int', ':LABEL']
        file_import_csv.writerow(headers)

        for i, row in enumerate(file_prep_csv):
            if i == 0 or len(row) < 3:   # 跳过表头或残缺行
                continue
            # 取姓名、性别、年龄
            info = [row[0], row[1], row[2]]
            # 用"姓名,性别,年龄"拼字符串 → MD5 → 全局唯一 ID
            info_id = get_md5('{},{},{}'.format(row[0], row[1], row[2]))
            info.insert(0, info_id)      # 插到最前面
            info.append('Person')        # 加 Label
            file_import_csv.writerow(info)
    print('- done.')


def build_stock(stock_industry_prep, stock_concept_prep, stock_import):
    """把行业+概念两个 CSV → Neo4j 节点文件 stock.csv
    格式:stock_id:ID,name,code,:LABEL
    LABEL 默认 Company;若股票名以 ST/S*ST 等开头 → Company;ST
    """
    print('Writing to {} file...'.format(stock_import.split('/')[-1]))
    stock = set()   # 用 set 去重:'code,name'

    # 先读行业文件,加入 set
    with open(stock_industry_prep, 'r', encoding='utf-8') as file_prep:
        file_prep_csv = csv.reader(file_prep, delimiter=',')
        for i, row in enumerate(file_prep_csv):
            if i == 0: continue
            code_name = '{},{}'.format(row[0], row[1].replace(' ', ''))
            stock.add(code_name)

    # 再读概念文件,加入 set(已去重)
    with open(stock_concept_prep, 'r', encoding='utf-8') as file_prep:
        file_prep_csv = csv.reader(file_prep, delimiter=',')
        for i, row in enumerate(file_prep_csv):
            if i == 0: continue
            code_name = '{},{}'.format(row[0], row[1].replace(' ', ''))
            stock.add(code_name)

    # 写 Neo4j 节点文件
    with open(stock_import, 'w', encoding='utf-8') as file_import:
        file_import_csv = csv.writer(file_import, delimiter=',')
        headers = ['stock_id:ID', 'name', 'code', ':LABEL']
        file_import_csv.writerow(headers)
        for s in stock:
            split = s.split(',')
            ST = False
            states = ['*ST', 'ST', 'S*ST', 'SST']
            info = []
            # 只要股票名以 ST 前缀开头 → 加 Company;ST 双 Label
            for state in states:
                if split[1].startswith(state):
                    ST = True
                    split[1] = split[1].replace(state, '')   # 去掉前缀
                    info = [split[0], split[1], split[0], 'Company;ST']
                    break
            if not ST:
                info = [split[0], split[1], split[0], 'Company']
            file_import_csv.writerow(info)
    print('- done.')


def build_concept(stock_concept_prep, concept_import):
    """把概念 CSV → Neo4j 节点文件 concept.csv
    格式:concept_id:ID,name,:LABEL  LABEL=Concept
    """
    print('Writing to {} file...'.format(concept_import.split('/')[-1]))
    with open(stock_concept_prep, 'r', encoding='utf-8') as file_prep, \
         open(concept_import, 'w', encoding='utf-8') as file_import:

        file_prep_csv = csv.reader(file_prep, delimiter=',')
        file_import_csv = csv.writer(file_import, delimiter=',')
        headers = ['concept_id:ID', 'name', ':LABEL']
        file_import_csv.writerow(headers)

        concepts = set()   # 去重概念名
        for i, row in enumerate(file_prep_csv):
            if i == 0: continue
            concepts.add(row[2])        # 第 3 列是概念名
        for concept in concepts:
            concept_id = get_md5(concept)   # MD5 当 ID
            new_row = [concept_id, concept, 'Concept']
            file_import_csv.writerow(new_row)
    print('- done.')


def build_industry(stock_industry_prep, industry_import):
    """把行业 CSV → Neo4j 节点文件 industry.csv
    格式:industry_id:ID,name,:LABEL  LABEL=Industry
    """
    print('Write to {} file...'.format(industry_import.split('/')[-1]))
    with open(stock_industry_prep, 'r', encoding="utf-8") as file_prep, \
         open(industry_import, 'w', encoding='utf-8') as file_import:

        file_prep_csv = csv.reader(file_prep, delimiter=',')
        file_import_csv = csv.writer(file_import, delimiter=',')
        headers = ['industry_id:ID', 'name', ':LABEL']
        file_import_csv.writerow(headers)

        industries = set()
        for i, row in enumerate(file_prep_csv):
            if i == 0: continue
            industries.add(row[2])          # 第 3 列是行业名
        for industry in industries:
            industry_id = get_md5(industry)
            new_row = [industry_id, industry, 'Industry']
            file_import_csv.writerow(new_row)
    print('- done.')


def build_executive_stock(executive_prep, relation_import):
    """生成关系文件 executive_stock.csv
    格式::START_ID,jobs,:END_ID,:TYPE
    START_ID = 人(MD5),END_ID = 股票代码,TYPE = employ_of
    """
    with open(executive_prep, 'r', encoding='utf-8') as file_prep, \
         open(relation_import, 'w', encoding='utf-8') as file_import:

        file_prep_csv = csv.reader(file_prep, delimiter=',')
        file_import_csv = csv.writer(file_import, delimiter=',')
        headers = [':START_ID', 'jobs', ':END_ID', ':TYPE']
        file_import_csv.writerow(headers)

        for i, row in enumerate(file_prep_csv):
            if i == 0: continue
            # 人 ID = MD5(姓名,性别,年龄)
            start_id = get_md5('{},{},{}'.format(row[0], row[1], row[2]))
            end_id = row[3]              # 股票代码
            relation = [start_id, row[4], end_id, 'employ_of']
            file_import_csv.writerow(relation)


def build_stock_industry(stock_industry_prep, relation_import):
    """生成关系文件 stock_industry.csv
    格式::START_ID,:END_ID,:TYPE
    START_ID = 股票代码,END_ID = 行业 MD5,TYPE = industry_of
    """
    with open(stock_industry_prep, 'r', encoding='utf-8') as file_prep, \
         open(relation_import, 'w', encoding='utf-8') as file_import:

        file_prep_csv = csv.reader(file_prep, delimiter=',')
        file_import_csv = csv.writer(file_import, delimiter=',')
        headers = [':START_ID', ':END_ID', ':TYPE']
        file_import_csv.writerow(headers)

        for i, row in enumerate(file_prep_csv):
            if i == 0: continue
            industry = row[2]                    # 行业名
            start_id = row[0]                    # 股票代码
            end_id = get_md5(industry)           # 行业 MD5
            relation = [start_id, end_id, 'industry_of']
            file_import_csv.writerow(relation)


def build_stock_concept(stock_concept_prep, relation_import):
    """生成关系文件 stock_concept.csv
    格式::START_ID,:END_ID,:TYPE
    START_ID = 股票代码,END_ID = 概念 MD5,TYPE = concept_of
    """
    with open(stock_concept_prep, 'r', encoding='utf-8') as file_prep, \
         open(relation_import, 'w', encoding='utf-8') as file_import:

        file_prep_csv = csv.reader(file_prep, delimiter=',')
        file_import_csv = csv.writer(file_import, delimiter=',')
        headers = [':START_ID', ':END_ID', ':TYPE']
        file_import_csv.writerow(headers)

        for i, row in enumerate(file_prep_csv):
            if i == 0: continue
            concept = row[2]                     # 概念名
            start_id = row[0]                    # 股票代码
            end_id = get_md5(concept)
            relation = [start_id, end_id, 'concept_of']
            file_import_csv.writerow(relation)


# 14. 主函数:一次生成 7 个 Neo4j bulk-import 文件
if __name__ == '__main__':
    import_path = 'data/import'
    if not os.path.exists(import_path):
        os.makedirs(import_path)      # 自动建 output 目录

    # 依次生成节点文件
    build_executive('data/executive_prep.csv', 'data/import/executive.csv')
    build_stock('data/stock_industry_prep.csv', 'data/stock_concept_prep.csv',
                'data/import/stock.csv')
    build_concept('data/stock_concept_prep.csv', 'data/import/concept.csv')
    build_industry('data/stock_industry_prep.csv', 'data/import/industry.csv')

    # 生成关系文件
    build_executive_stock('data/executive_prep.csv', 'data/import/executive_stock.csv')
    build_stock_industry('data/stock_industry_prep.csv', 'data/import/stock_industry.csv')
    build_stock_concept('data/stock_concept_prep.csv', 'data/import/stock_concept.csv')

最后生成的节点文件和关系文件存放在 \data\import\ 文件夹下面。

3.2 知识融合

该项目非常轻量级别,属于 规则式融合 ,而不是(字符串相似度、别名对齐、外部知识库映射)等机器学习或者复杂算法融合。

该项目主要将同一数据集去重、ST前缀清洗、Md5生成ID,没有对齐、MERGE导入图级去重。

3.3 知识存储

使用图数据库(Neo4j)存储知识图谱。这里的操作可以参考Neo4j官方导入数据 -作手册

任务1:准备 CSV 文件

前面代码已经生成需要的 csv 文件,现将 7 个csv文件(节点表和关系表)放入Neo4j安装路径下面和bin同级的import目录。

节点和关系表格式规范:导入Neo4j 数据库的 节点表和关系表有固定要求的格式。

(1)节点表必须有一个 :ID 和一个标签 :LABEL,还可以添加 节点实体的 其他属性,例如 executive.csv 人实体表,格式包括(ID,姓名,性别,年龄,标签)

其中,ID用于在关系文件中查找和连接节点。为了确保跨文件ID的唯一性,可以指定ID所属的组,例如 :ID(Executive),添加字段名称方便理解该列的作用,例如,person_id:ID(Executive)。
(2)关系数据有三个必填字段:

  • :START_ID------ ID 指的是一个节点(起始节点)。

  • :END_ID------ ID 指的是一个节点(结束节点)。

  • :TYPE------关系类型。

executive_stock.csv文件建立的是 人实体(Executive)和公司实体(Stock)的关系表。

任务2:停止Neo4j服务

导入前,必须停止Neo4j服务。在 cmd 命令行中执行以下命令:

bash 复制代码
neo4j stop
任务3:执行导入命令

在cmd 命令行中进入Neo4j的bin目录,然后运行格式正确的 **import **命令。

import 命令为:

bash 复制代码
neo4j-admin database import full neo4j --verbose --overwrite-destination --nodes=import/concept.csv --nodes=import/executive.csv --nodes=import/industry.csv --nodes=import/stock.csv --relationships=import/executive_stock.csv --relationships=import/stock_concept.csv --relationships=import/stock_industry.csv

断行的写法如下,其中第一行最后的 neo4j 指的是你要导入的数据库,该数据库必须为空。

bash 复制代码
neo4j-admin database import full neo4j ^
--verbose ^
--overwrite-destination ^
--nodes=import/concept.csv ^
--nodes=import/executive.csv ^
--nodes=import/industry.csv ^
--nodes=import/stock.csv ^
--relationships=import/executive_stock.csv ^
--relationships=import/stock_concept.csv ^
--relationships=import/stock_industry.csv

导入成功后:

注意:import 命令只能将数据导入到一个全新的、空的目标数据库。如果指定的数据库已存在,导入会失败,物理删除数据库的方法比如我现在要导入的数据库名字为 neo4j,则可以依次执行:

bash 复制代码
# 从 bin 目录退到 neo4j 的安装目录
cd..

# 进入数据目录
cd data/databases/

# 删除数据库文件夹(请将 `neo4j` 替换为您的实际数据库名)
rmdir /s neo4j

# 同时建议删除对应的事务日志文件夹
rmdir /s ..\transactions\neo4j\

进入 neo4j 安装目录下的 data\databases\ 目录,删除 neo4j 数据库,同时进入\data\transactions\ 目录下删除 neo4j 事务日志文件。运行效果如下:

任务4:重启 neo4j 服务
bash 复制代码
neo4j start
任务5:查询

从链接Neo4j Browser进入neo4j 数据库界面,输入账户和密码进入 browser 。

(1)依次输入简单查询命令

sql 复制代码
# 查询node
MATCH (n:Concept) RETURN n LIMIT 25

# 查询relationship
MATCH p=()-[r:industry_of]->() RETURN p LIMIT 100

(2)编写Cypher 语句回答如下问题

1.有多少个公司目前是属于"ST"类型的?

sql 复制代码
match (n:ST) return count(distinct(n))

2."600519"公司的所有独立董事人员中,有多少人同时也担任别的公司的独立董事职位?

sql 复制代码
MATCH (m:Company{code:'600519'})<-[:employ_of{jobs:'独立董事'}]-(n:Person)-[:employ_of{jobs:'独立董事'}]->(q:Company)
RETURN count(distinct(n))

3.有多少公司既属于环保行业,又有外资背景?

sql 复制代码
MATCH (:Concept{name:'外资背景'})<-[:concept_of]-(m:Company)-[:industry_of]-(:Industry{name:'环保行业'})
RETURN count(distinct(m))

4.对于有锂电池概念的所有公司,独立董事中女性人员比例是多少?

sql 复制代码
MATCH (m:Concept{name:'锂电池'})<-[:concept_of]-(n:Company)<-[:employ_of{jobs:'独立董事'}]-(p:Person{gender:'女'})
MATCH (m:Concept{name:'锂电池'})<-[:concept_of]-(n:Company)<-[:employ_of{jobs:'独立董事'}]-(p2:Person)
RETURN count(distinct(p))*1.0/count(distinct(p2))

4.问题

构建人的实体时,重名问题具体怎么解决?

(1) 最好的方式是用身份证或者其他唯一能确定人的方式去关联。

(2) 在本例中,我用 姓名、年龄、性别3个字段做唯一的,将这3个字段做md5。

相关推荐
NOCSAH1 小时前
统好AI:助力企业智改数转的务实实践
大数据·人工智能·统好ai
乔代码嘚1 小时前
Agentic-KGR:多智能体强化学习驱动的知识图谱本体渐进式扩展技术
人工智能·学习·大模型·知识图谱·ai大模型·大模型学习·大模型教程
张较瘦_1 小时前
[论文阅读] AI + 软件工程 | 突破LLM代码生成瓶颈:编程知识图谱(PKG)让检索增强更精准
论文阅读·人工智能·软件工程
sheji1051 小时前
人形机器人行业市场分析报告
人工智能·机器人·智能硬件
Sinokap1 小时前
GPT-5.5 上线:OpenAI 把 AI 推向真实办公场景
大数据·人工智能
肖有米XTKF86461 小时前
河北奢源水光商城系统制度开发
人工智能·软件工程·团队开发·csdn开发云
sinovoip1 小时前
香蕉派开源社区联合进迭进空重磅打造: BPI‑SM10(K3-Com260) 和 K3 Pico‑ITX 计算机将于5月11日全球发货
人工智能·开源·risc-v
南湖渔歌1 小时前
AI 模型选择与学习指南
人工智能
科研前沿1 小时前
镜像视界浙江科技有限公司的关键技术突破有哪些?
大数据·人工智能·科技·算法·音视频·空间计算