共计 14675 个字符,预计需要花费 37 分钟才能阅读完成。
手把手教学小型金融常识图谱构建:量化剖析、图数据库 neo4j、图算法、关系预测、命名实体辨认、Cypher Cheetsheet 具体教学等
成果预览:
1. 常识图谱存储形式
常识图谱存储形式次要蕴含资源形容框架 (Resource Description Framework,RDF) 和图数据库(Graph Database)。
1.1 资源形容框架个性
- 存储为三元组(Triple)
- 规范的推理引擎
- W3C 规范
- 易于公布数据
- 少数为学术界场景
1.2 图数据库个性
- 节点和关系均能够蕴含属性
- 没有规范的推理引擎
- 图的遍历效率高
- 事务管理
- 少数为工业界场景
码源链接见文末跳转
文末链接跳转
2. 图数据库 neo4j
neo4j 是一款 NoSQL 图数据库,具备高性能的读写可扩展性,基于高效的图形查询语言Cypher
,更多介绍可拜访 neo4j 官网,官网还提供了 Online Sandbox 实现疾速上手体验。
2.1 软件下载
下载链接:https://neo4j.com/download-center/
2.2 启动登录
2.2.1 Windows
- 进入
neo4j
目录
cd neo4j/bin
./neo4j start
- 启动胜利,终端呈现如下提醒即为启动胜利
Starting Neo4j.Started neo4j (pid 30914). It is available at http://localhost:7474/ There may be a short delay until the server is ready.
(1)拜访页面:http://localhost:7474
(2)初始账户和明码均为 neo4j
(host
类型抉择bolt
)
(3)输出旧明码并输出新密码:启动前留神本地已装置jdk
(倡议装置jdk version 11
):https://www.oracle.com/java/technologies/javase-downloads.html
2.2.2 MacOS
执行 Add Local DBMS 后,再关上 Neo4j Browser 即可
2.3 储备常识
在 neo4j 上执行 CRUD 时须要应用 Cypher 查询语言。
- 官网文档
- 集体整顿的常见 Cypher 指令
2.4 Windows 装置时可能遇到问题及解决办法
- 问题:实现装置 JDK1.8.0_261 后,在启动
neo4j
过程中呈现了以下问题:
Unable to find any JVMs matching version "11"
- 解决方案:提醒装置
jdk 11 version
,于是下载了jdk-11.0.8
,Mac OS
可通过ls -la /Library/Java/JavaVirtualMachines/
查看已装置的jdk
及版本信息。
3. 常识图谱数据筹备
3.1 收费开源金融数据接口
Tushare 收费账号可能无奈拉取数据,可参考 issues 提供的股票数据获取办法:
3.1.1 Tushare
官网链接:http://www.tushare.org/
3.1.2 JointQuant
官网链接:https://www.joinquant.com/
3.1.3 导入模块
import tushare as ts # 参考 Tushare 官网提供的装置形式
import csv
import time
import pandas as pd
# 以下 pro_api token 可能已过期,可自行返回申请或者应用收费版本
pro = ts.pro_api('4340a981b3102106757287c11833fc14e310c4bacf8275f067c9b82d')
3.2 数据预处理
3.2.1 股票根本信息
stock_basic = pro.stock_basic(list_status='L', fields='ts_code, symbol, name, industry')
# 重命名行,便于前面导入 neo4j
basic_rename = {'ts_code': 'TS 代码', 'symbol': '股票代码', 'name': '股票名称', 'industry': '行业'}
stock_basic.rename(columns=basic_rename, inplace=True)
# 保留为 stock_basic.csv
stock_basic.to_csv('financial_data\\stock_basic.csv', encoding='gbk')
3.2.2 股票持有股东信息
holders = pd.DataFrame(columns=('ts_code', 'ann_date', 'end_date', 'holder_name', 'hold_amount', 'hold_ratio'))
# 获取一年内所有上市股票股东信息(能够获取一个报告期的)for i in range(3610):
code = stock_basic['TS 代码'].values[i]
holders = pro.top10_holders(ts_code=code, start_date='20180101', end_date='20181231')
holders = holders.append(holders)
if i % 600 == 0:
print(i)
time.sleep(0.4)# 数据接口限度
# 保留为 stock_holders.csv
holders.to_csv('financial_data\\stock_holders.csv', encoding='gbk')
holders = pro.holders(ts_code='000001.SZ', start_date='20180101', end_date='20181231')
3.2.3 股票概念信息
concept_details = pd.DataFrame(columns=('id', 'concept_name', 'ts_code', 'name'))
for i in range(358):
id = 'TS' + str(i)
concept_detail = pro.concept_detail(id=id)
concept_details = concept_details.append(concept_detail)
time.sleep(0.4)
# 保留为 concept_detail.csv
concept_details.to_csv('financial_data\\stock_concept.csv', encoding='gbk')
3.2.4 股票布告信息
for i in range(3610):
code = stock_basic['TS 代码'].values[i]
notices = pro.anns(ts_code=code, start_date='20180101', end_date='20181231', year='2018')
notices.to_csv("financial_data\\notices\\"+str(code)+".csv",encoding='utf_8_sig',index=False)
notices = pro.anns(ts_code='000001.SZ', start_date='20180101', end_date='20181231', year='2018')
3.2.5 财经新闻信息
news = pro.news(src='sina', start_date='20180101', end_date='20181231')
news.to_csv("financial_data\\news.csv",encoding='utf_8_sig')
3.2.6 概念信息
concept = pro.concept()
concept.to_csv('financial_data\\concept.csv', encoding='gbk')
3.2.7 沪股通和深股通成分信息
# 获取沪股通成分
sh = pro.hs_const(hs_type='SH')
sh.to_csv("financial_data\\sh.csv",index=False)
#获取深股通成分
sz = pro.hs_const(hs_type='SZ')
sz.to_csv("financial_data\\sz.csv",index=False)
3.2.8 股票价格信息
for i in range(3610):
code = stock_basic['TS 代码'].values[i]
price = pro.query('daily', ts_code=code, start_date='20180101', end_date='20181231')
price.to_csv("financial_data\\price\\"+str(code)+".csv",index=False)
3.2.9 应用收费接口获取股票数据
import tushare as ts
# 基本面信息
df = ts.get_stock_basics()
# 布告信息
ts.get_notices("000001")
# 新浪股吧
ts.guba_sina()
# 历史价格数据
ts.get_hist_data("000001")
# 历史价格数据(周粒度)ts.get_hist_data("000001",ktype="w")
# 历史价格数据(1 分钟粒度)ts.get_hist_data("000001",ktype="m")
# 历史价格数据(5 分钟粒度)ts.get_hist_data("000001",ktype="5")
# 指数数据(sh 上证指数;sz 深圳成指;hs300 沪深 300;sz50 上证 50;zxb 中小板指数;cyb 创业板指数)ts.get_hist_data("cyb")
# 宏观数据(居民消费指数)
ts.get_cpi()
# 获取分笔数据
ts.get_tick_data('000001', date='2018-10-08', src='tt')
3.3 数据预处理
3.3.1 统计股票的交易日量众数
import numpy as np
yaxis = list()
for i in listdir:
stock = pd.read_csv("financial_data\\price_logreturn\\"+i)
yaxis.append(len(stock['logreturn']))
counts = np.bincount(yaxis)
np.argmax(counts)
3.3.2 计算股票对数收益
股票对数收益及皮尔逊相关系数的计算公式:
import pandas as pd
import numpy as np
import os
import math
listdir = os.listdir("financial_data\\price")
for l in listdir:
stock = pd.read_csv('financial_data\\price\\'+l)
stock['index'] = [1]* len(stock['close'])
stock['next_close'] = stock.groupby('index')['close'].shift(-1)
stock = stock.drop(index=stock.index[-1])
logreturn = list()
for i in stock.index:
logreturn.append(math.log(stock['next_close'][i]/stock['close'][i]))
stock['logreturn'] = logreturn
stock.to_csv("financial_data\\price_logreturn\\"+l,index=False)
3.3.3 股票间对数收益率相关系数
from math import sqrt
def multipl(a,b):
sumofab=0.0
for i in range(len(a)):
temp=a[i]*b[i]
sumofab+=temp
return sumofab
def corrcoef(x,y):
n=len(x)
#求和
sum1=sum(x)
sum2=sum(y)
#求乘积之和
sumofxy=multipl(x,y)
#求平方和
sumofx2 = sum([pow(i,2) for i in x])
sumofy2 = sum([pow(j,2) for j in y])
num=sumofxy-(float(sum1)*float(sum2)/n)
#计算皮尔逊相关系数
den=sqrt((sumofx2-float(sum1**2)/n)*(sumofy2-float(sum2**2)/n))
return num/den
因为原始数据达百万条,为节俭计算量仅选取前 300 个股票进行关联性剖析
listdir = os.listdir("financial_data\\300stock_logreturn")
s1 = list()
s2 = list()
corr = list()
for i in listdir:
for j in listdir:
stocka = pd.read_csv("financial_data\\300stock_logreturn\\"+i)
stockb = pd.read_csv("financial_data\\300stock_logreturn\\"+j)
if len(stocka['logreturn']) == 242 and len(stockb['logreturn']) == 242:
s1.append(str(i)[:10])
s2.append(str(j)[:10])
corr.append(corrcoef(stocka['logreturn'],stockb['logreturn']))
print(str(i)[:10],str(j)[:10],corrcoef(stocka['logreturn'],stockb['logreturn']))
corrdf = pd.DataFrame()
corrdf['s1'] = s1
corrdf['s2'] = s2
corrdf['corr'] = corr
corrdf.to_csv("financial_data\\corr.csv")
4 搭建金融常识图谱
装置第三方库
pip install py2neo
4.1 基于 python 连贯
具体代码可参考 3.1 python 操作 neo4j- 连贯
from pandas import DataFrame
from py2neo import Graph,Node,Relationship,NodeMatcher
import pandas as pd
import numpy as np
import os
# 连贯 Neo4j 数据库
graph = Graph('http://localhost:7474/db/data/',username='neo4j',password='neo4j')
4.2 读取数据
stock = pd.read_csv('stock_basic.csv',encoding="gbk")
holder = pd.read_csv('holders.csv')
concept_num = pd.read_csv('concept.csv')
concept = pd.read_csv('stock_concept.csv')
sh = pd.read_csv('sh.csv')
sz = pd.read_csv('sz.csv')
corr = pd.read_csv('corr.csv')
4.3 填充和去重
stock['行业'] = stock['行业'].fillna('未知')
holder = holder.drop_duplicates(subset=None, keep='first', inplace=False)
4.4 创立实体
概念、股票、股东、股通
sz = Node('深股通', 名字 ='深股通')
graph.create(sz)
sh = Node('沪股通', 名字 ='沪股通')
graph.create(sh)
for i in concept_num.values:
a = Node('概念', 概念代码 =i[1], 概念名称 =i[2])
print('概念代码:'+str(i[1]),'概念名称:'+str(i[2]))
graph.create(a)
for i in stock.values:
a = Node('股票',TS 代码 =i[1], 股票名称 =i[3], 行业 =i[4])
print('TS 代码:'+str(i[1]),'股票名称:'+str(i[3]),'行业:'+str(i[4]))
graph.create(a)
for i in holder.values:
a = Node('股东',TS 代码 =i[0], 股东名称 =i[1], 持股数量 =i[2], 持股比例 =i[3])
print('TS 代码:'+str(i[0]),'股东名称:'+str(i[1]),'持股数量:'+str(i[2]))
graph.create(a)
4.5 创立关系
股票 - 股东、股票 - 概念、股票 - 布告、股票 - 股通
matcher = NodeMatcher(graph)
for i in holder.values:
a = matcher.match("股票",TS 代码 =i[0]).first()
b = matcher.match("股东",TS 代码 =i[0])
for j in b:
r = Relationship(j,'参股',a)
graph.create(r)
print('TS',str(i[0]))
for i in concept.values:
a = matcher.match("股票",TS 代码 =i[3]).first()
b = matcher.match("概念", 概念代码 =i[1]).first()
if a == None or b == None:
continue
r = Relationship(a,'概念属于',b)
graph.create(r)
noticesdir = os.listdir("notices\\")
for n in noticesdir:
notice = pd.read_csv("notices\\"+n,encoding="utf_8_sig")
notice['content'] = notice['content'].fillna('空白')
for i in notice.values:
a = matcher.match("股票",TS 代码 =i[0]).first()
b = Node('布告', 日期 =i[1], 题目 =i[2], 内容 =i[3])
graph.create(b)
r = Relationship(a,'发布公告',b)
graph.create(r)
print(str(i[0]))
for i in sz.values:
a = matcher.match("股票",TS 代码 =i[0]).first()
b = matcher.match("深股通").first()
r = Relationship(a,'成分股属于',b)
graph.create(r)
print('TS 代码:'+str(i[1]),'-- 深股通')
for i in sh.values:
a = matcher.match("股票",TS 代码 =i[0]).first()
b = matcher.match("沪股通").first()
r = Relationship(a,'成分股属于',b)
graph.create(r)
print('TS 代码:'+str(i[1]),'-- 沪股通')
# 构建股票间关联
corr = pd.read_csv("corr.csv")
for i in corr.values:
a = matcher.match("股票",TS 代码 =i[1][:-1]).first()
b = matcher.match("股票",TS 代码 =i[2][:-1]).first()
r = Relationship(a,str(i[3]),b)
graph.create(r)
print(i)
5 数据可视化查问
基于 Crypher 语言,以安全银行为例进行可视化查问。
5.1 查看所有关联实体
match p=(m)-[]->(n) where m. 股票名称 ="安全银行" or n. 股票名称 ="安全银行" return p;
5.2 限度显示数量
计算股票间对数收益率的相关系数后,查看与安全银行股票相关联的实体
match p=(m)-[]->(n) where m. 股票名称 ="安全银行" or n. 股票名称 ="安全银行" return p limit 300;
5.3 指定股票间对数收益率相关系数
match p=(m)-[]->(n) where m. 股票名称 ="安全银行" and n. 股票名称 ="万科 A" return p;
6 neo4j 图算法
6.1. 核心度算法(Centralities)
- PageRank(页面排名)
- ArticleRank(文章排名)
- Betweenness Centrality (中介核心度)
- Closeness Centrality (靠近核心度)
- Harmonic Centrality(谐波核心度)
6.2 社区检测算法(Community detection)
- Louvain (鲁汶算法)
- Label Propagation (标签流传)
- Connected Components (连通组件)
- Strongly Connected Components (强连通组件)
- Triangle Counting / Clustering Coefficient (三角计数 / 聚类系数)
6.3 门路搜索算法(Path finding)
- Minimum Weight Spanning Tree (最小权重生成树)
- Shortest Path (最短门路)
- Single Source Shortest Path (单源最短门路)
- All Pairs Shortest Path (全顶点对最短门路)
- A*(A 星)
- Yen’s K-shortest Paths(Yen- K 最短门路)
- Random Walk (随机游走)
6.4 相似性算法(Similarity)
- Jaccard Similarity (Jaccard 类似度)
- Cosine Similarity (余弦类似度)
- Pearson Similarity (Pearson 类似度)
- Euclidean Distance (欧氏间隔)
- Overlap Similarity (重叠类似度)
6.5 链接预测(Link Prediction)
- Adamic Adar(AA)
- Common Neighbors(独特近邻)
- Preferential Attachment(优先连贯)
- Resource Allocation(资源分配)
- Same Community(独特社区)
- Total Neighbors(近邻总数)
6.6 预处理算法(Preprocessing)
- One Hot Encoding(独热编码)
6.7 算法库装置及导入办法
以 Windows OS 为例,neo4j 的算法库并非在安装包中提供,而须要下载算法包:
(1)下载graph-algorithms-algo-3.5.4.0.jar
(2)将 graph-algorithms-algo-3.5.4.0.jar
挪动至 neo4j 数据库根目录下的 plugin
中
(3)批改 neo4j 数据库目录的 conf
中neo4j.conf
,增加以下配置
dbms.security.procedures.unrestricted=algo.*
(4)应用以下命令查看所有算法列表
CALL algo.list()
6.8 算法实际——链路预测
6.8.1 Aaamic Adar algorithm
次要基于判断相邻的两个节点之间的密切水平作为评判规范,2003 年由 Lada Adamic 和 Eytan Adar 在 Friends and neighbors on the Web 提出,其中节点亲密度的计算公式如下:
其中 N(u)
示意与节点 u 相邻的节点汇合,若 A(x,y)
示意节点 x 和节点 y 不相邻,而该值若越大则紧密度为高。
AAA 算法 cypher 代码参考:
MERGE (zhen:Person {name: "Zhen"})
MERGE (praveena:Person {name: "Praveena"})
MERGE (michael:Person {name: "Michael"})
MERGE (arya:Person {name: "Arya"})
MERGE (karin:Person {name: "Karin"})
MERGE (zhen)-[:FRIENDS]-(arya)
MERGE (zhen)-[:FRIENDS]-(praveena)
MERGE (praveena)-[:WORKS_WITH]-(karin)
MERGE (praveena)-[:FRIENDS]-(michael)
MERGE (michael)-[:WORKS_WITH]-(karin)
MERGE (arya)-[:FRIENDS]-(karin)
// 计算 Michael 和 Karin 之间的亲密度
MATCH (p1:Person {name: 'Michael'})
MATCH (p2:Person {name: 'Karin'})
RETURN algo.linkprediction.adamicAdar(p1, p2) AS score
// score: 0.910349
// 基于好友关系计算 Michael 和 Karin 之间的亲密度
MATCH (p1:Person {name: 'Michael'})
MATCH (p2:Person {name: 'Karin'})
RETURN algo.linkprediction.adamicAdar(p1, p2, {relationshipQuery: "FRIENDS"}) AS score
// score: 0.0
6.8.2 Common Neighbors
基于节点之间独特近邻数量计算,计算公式如下:
其中 N(x)示意与节点 x 相邻的节点汇合,独特近邻示意两个汇合的交加,若 CN(x,y)值越高,示意节点 x 和节点 y 的亲密度越高。
Common Neighbors 算法 cypher 代码参考:
MATCH (p1:Person {name: 'Michael'})
MATCH (p2:Person {name: 'Karin'})
RETURN algo.linkprediction.commonNeighbors(p1, p2) AS score
6.8.3 Resource Allocation
资源分配算法,计算公式如下:
其中 N(u)
是与节点 u
相邻的节点汇合,RA(x,y)越高表明节点 x 和节点 y 的亲密度越大。
Resource Allocation 算法 cypher 代码参考:
MATCH (p1:Person {name: 'Michael'})
MATCH (p2:Person {name: 'Karin'})
RETURN algo.linkprediction.resourceAllocation(p1, p2) AS score
6.8.4 Total Neighbors
指的是相邻节点之间的街坊总数,计算公式如下:
其中 N(u)
是与节点 u
相邻的节点汇合。
Total Neighbors 算法 cypher 代码参考:
MATCH (p1:Person {name: 'Michael'})
MATCH (p2:Person {name: 'Karin'})
RETURN algo.linkprediction.totalNeighbors(p1, p2) AS score
官网文档 > 链路算法 > 介绍:https://neo4j.com/docs/graph-algorithms/3.5/labs-algorithms/l…
7.Cypher Cheetsheet 根底语法
7.1 创立节点
类型为Person
(属性:姓名、年龄及性别)
create (:Person{name:"Tom",age:18,sex:"male"})
create (:Person{name:"Jimmy",age:20,sex:"male"})
7.2 创立关系
寻找 2 个 Person 类型节点别离姓名为 Tom 和 Jimmy,创立两节点之间的关系:类型为 Friend,关系值为 best
match(p1:Person),(p2:Person)
where p1.name="Tom" and p2.name = "Jimmy"
create(p1) -[:Friend{relation:"best"}] ->(p2);
7.3 创立索引
create index on :Person(name)
// 创立惟一索引(属性值惟一)create constraint on (n:Person) assert n.name is unique
7.4 删除节点
// 一般删除
match(p:Person_{name:"Jiimmy"}) delete p
match (a)-[r:knows]->(b) delete r,b
// 级联删除(即删除某个节点时会同时删除该节点的关系)match (n{name: "Mary"}) detach delete n
// 删除所有节点
match (m) delete m
7.5 删除关系
// 一般删除
match(p1:Person)-[r:Friend]-(p2:Person)
where p1.name="Jimmy" and p2.name="Tom"
delete r
// 删除所有关系
match p=()-[]-() delete p
7.6 merge 关键字
存在间接返回;不存在则新建并返回(通常理论用处于在对节点增加属性时防止报错)
// 创立 / 获取对象
merge (p:Person { name: "Jim1"}) return p;
// 创立 / 获取对象 + 设置属性值 + 返回属性值
merge (p:Person { name: "Koko"})
on create set p.time = timestamp()
return p.name, p.time
// 创立关系
match (a:Person {name: "Jim"}),(b:Person {name: "Tom"})
merge (a)-[r:friends]->(b)
7.7 更新节点
7.7.1 更新属性值
match (n {name:'Jim'})
set n.name='Tom'
set n.age=20
return n
7.7.2 新增属性和属性值
match (n {name:'Mary'}) set n += {age:20} return n
7.7.3 删除属性值
match(n{name:'Tom'}) remove n.age return n
7.7.4 更新节点类型(容许有多个标签)
①match (n{name:'Jim'}) set n:Person return n
②match (n{name:'Jim'}) set n:Person:Student return n
7.8 匹配
7.8.1 限度节点类型和属性匹配
match (n:Person{name:"Jim"}) return n
match (n) where n.name = "Jim" return n
match (n:Person)-[:Realation]->(m:Person) where n.name = 'Mary'
7.8.2 可选匹配(对于缺失局部应用 Null 代替)
optional match (n)-[r]->(m) return m
7.8.3 字符串结尾匹配
match (n) where n.name starts with 'J' return n
7.8.4 字符串结尾匹配
match (n) where n.name ends with 'J' return n
7.8.5 字符串蕴含匹配
match (n) where n.name contains with 'g' return n
7.8.6 字符串排除匹配
match (n) where not n.name starts with 'J' return n
7.8.7 正则匹配 =~(含糊匹配)
match (n) where n.name =~ '.*J.*' return n(等价)like '%J%'
7.8.8 正则匹配 =~(不辨别大小写)
match (n) where n.name =~ '(?i)b.*' return n(等价)like 'B/b%'
7.8.9 属性值蕴含(IN)
match (n { name: 'Jim'}),(m) where m.name in ['Tom', 'Koo'] and (n)<--(m) return m
7.8.10 “ 或 ” 匹配(|)
match p=(n)-[:knows|:likes]->(m) return p
7.8.11 任意节点和指定范畴深度关系
match p=(n)-[*1..3]->(m) return p
7.8.12 任意节点和指任意深度关系
match p=(n)-[*]->(m) return p
7.8.13 去重返回
match (n) where n.ptype='book' return distinct n
7.8.14 排序返回(desc 降序;asc 升序)
match (n) where n.ptype='book' return n order by n.price desc
7.8.15 重命名返回
match (n) where n.ptype='book' return n.pname as name
7.8.16 多重条件限度(with),即返回意识 10 人以上的张 %
match (a)-[:knows]-(b)
where a.name =~ '张.*'
with a, count(b) as friends
where friends > 10
return a
7.8.17 并集去重(union)
match (a)-[:knows]->(b) return b.name
union
match (a)-[:likes]->(b) eturn b.name
7.8.18 并集不去重(union all)
match (a)-[:knows]->(b) return b.name
union all
match (a)-[:likes]->(b) eturn b.name
7.8.19 查看节点属性 /ID
match (p) where p.name = 'Jim'
return keys(p)/properties(p)/id(p)
7.8.20 匹配分页返回
match (n) where n.name='John' return n skip 10 limit 10
7.9 读取文件
7.9.1 读取网络资源 csv 文件
load csv with header from 'url:[www.download.com/abc.csv](http://www.download.com/abc.csv)' as line
create (:Track{trackId:line.id,name:line.name,length:line.length})
7.9.2 分批读取网络资源
例如 csv 文件(default=1000)
using periodic commit (800)
load csv with header from 'url:[www.download.com/abc.csv](http://www.download.com/abc.csv)' as line
create (:Track{trackId:line.id,name:line.name,length:line.length})
7.9.3 读取本地文件
load csv with headers from 'file:///00000.csv' as line
create (:Data{date:line['date'],open:line['open']})
(fieldterminator ';') // 自定义分隔符
7.9.4 注意事项
※ 本地 csv 文件必须是 utf- 8 格局
※ 须要导入 neo4j 数据库目录的 import 目录下
※ 本地 csv 蕴含 column 必须增加 with headers
7.10 foreach 关键字
- 集体小结
1. 节点属性应用 ()
2. 关系属性应用[]
3.where 中应用"="
4.{}
中应用 ":"
5. 关系建设应用(m)-[:r]->(n)
6. 正则应用"=~"
7. 节点或者关系(/[变量名: 类型{属性名: 属性值}]/)
8. 匹配关系时须要基于 p =(m)-[r]->(n) 返回 p,而不是返回 r(显示空)
码源链接见文末跳转
文末链接跳转
更多优质内容请关注公号 & 知乎:汀丶人工智能;会提供一些相干的资源和优质文章,收费获取浏览。
本文参加了 SegmentFault 思否写作挑战「摸索编码世界之旅 – 记我的第一份编程工作」,欢送正在浏览的你也退出。