图算法
Neo4j Graph Data Science(GDS)库是一套高性能的图算法集合,提供了超过 50 种经过优化的图算法,用于解决复杂的数据分析问题。这些算法可以帮助你发现隐藏的模式、识别关键节点、预测关系、检测异常等。本章将介绍 GDS 库的核心概念和常用算法的实际应用。
什么是图算法
图算法是对图结构数据进行数学分析的方法,它们利用节点之间的关系来揭示数据的深层特征。与传统的统计分析不同,图算法关注的是实体之间的连接和拓扑结构,这使得它们在处理关联数据时具有独特的优势。
图算法的应用场景
图算法在许多领域都有广泛的应用:
社交网络分析:识别影响力用户、发现社区结构、分析信息传播路径。例如,Twitter 使用 PageRank 算法来识别具有高影响力的用户,Facebook 使用社区发现算法来优化好友推荐。
金融风控:检测欺诈团伙、分析交易网络、识别洗钱模式。图算法可以发现传统方法难以察觉的复杂欺诈网络,例如循环转账、多层转移资金等模式。
推荐系统:基于用户行为模式推荐商品、发现相似用户、计算物品相似度。许多电商平台的"猜你喜欢"功能背后都有图算法的支持。
知识图谱:实体链接、关系推理、知识补全。图算法可以帮助构建更完整的知识网络,支持智能问答和语义搜索。
网络安全:识别关键基础设施节点、检测异常流量、分析攻击路径。在网络安全运维中,图算法可以快速定位潜在的安全风险。
GDS 库概述
安装 GDS 库
GDS 库需要单独安装。Neo4j Desktop 用户可以通过插件市场一键安装,服务器版用户需要下载并配置插件。
Neo4j Desktop 安装:
- 打开 Neo4j Desktop
- 选择你的数据库项目
- 点击 "Add" → "Plugins"
- 找到 "Graph Data Science Library" 并点击 "Install"
- 重启数据库
服务器版安装:
- 从 Neo4j 官网下载 GDS 库 JAR 文件
- 将 JAR 文件放入
$NEO4J_HOME/plugins目录 - 编辑
conf/neo4j.conf,添加:dbms.security.procedures.unrestricted=gds.*
dbms.security.procedures.allowlist=gds.* - 重启 Neo4j 服务
验证安装:
-- 检查 GDS 库是否安装成功
RETURN gds.version() AS version
GDS 算法分类
GDS 库将算法分为以下几个主要类别:
| 类别 | 用途 | 代表算法 |
|---|---|---|
| 中心性算法 | 识别图中最重要的节点 | PageRank、Betweenness、Degree |
| 社区发现算法 | 发现节点群体和社区结构 | Louvain、Label Propagation、SCC |
| 路径查找算法 | 计算节点间的最优路径 | Dijkstra、A*、BFS |
| 相似度算法 | 计算节点间的相似程度 | Jaccard、Cosine、Node Similarity |
| 链接预测算法 | 预测未来可能的关系 | Adamic Adar、Common Neighbors |
| 节点嵌入算法 | 生成节点的向量表示 | FastRP、GraphSAGE |
| 拓扑预测算法 | 分析图的拓扑结构 | Triangle Count、K-Core |
执行模式
GDS 算法支持多种执行模式,适用于不同的使用场景。理解这些模式的区别对于高效使用 GDS 至关重要。
五种执行模式对比
| 模式 | 输出 | 写入数据库 | 内存使用 | 适用场景 |
|---|---|---|---|---|
| stream | 返回结果行 | 否 | 低 | 探索性分析、可视化、调试 |
| write | 统计信息 | 是 | 中 | 持久化结果、后续查询使用 |
| mutate | 统计信息 | 否(写入内存图) | 中 | 算法流水线、多阶段处理 |
| stats | 统计信息 | 否 | 低 | 快速了解数据分布 |
| estimate | 资源估算 | 否 | 极低 | 执行前预估资源消耗 |
stream 模式:流式返回结果
stream 模式直接返回算法计算的结果,不修改数据库。这是最常用的模式,适合:
- 数据探索和可视化
- 调试算法参数
- 不需要持久化的临时分析
-- stream 模式:直接返回 PageRank 分数
CALL gds.pageRank.stream('myGraph')
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, score
ORDER BY score DESC
LIMIT 10
-- 带过滤的 stream
CALL gds.pageRank.stream('myGraph')
YIELD nodeId, score
WITH gds.util.asNode(nodeId) AS node, score
WHERE node.category = 'Technology'
RETURN node.name AS name, score
ORDER BY score DESC
性能特点:
- 结果按需返回,内存占用低
- 适合大规模数据的结果预览
- 返回的结果可以在后续 Cypher 中继续处理
write 模式:持久化结果
write 模式将算法结果写回 Neo4j 数据库作为节点属性,适合:
- 需要持久化分析结果
- 后续 Cypher 查询需要使用算法结果
- 构建基于算法结果的应用功能
-- write 模式:将 PageRank 分数写入节点属性
CALL gds.pageRank.write('myGraph', {
writeProperty: 'pagerank',
maxIterations: 20,
dampingFactor: 0.85
})
YIELD nodePropertiesWritten, ranIterations
-- 写入后可以常规查询使用
MATCH (p:Person)
WHERE p.pagerank > 0.5
RETURN p.name, p.pagerank
ORDER BY p.pagerank DESC
-- 写入多个属性
CALL gds.louvain.write('myGraph', {
writeProperty: 'community',
includeIntermediateCommunities: true
})
YIELD nodePropertiesWritten, communityCount
-- 查询社区成员
MATCH (p:Person)
RETURN p.community AS communityId, count(*) AS memberCount, collect(p.name) AS members
ORDER BY memberCount DESC
LIMIT 5
注意事项:
- 写入操作会修改数据库,确保有备份
- 大规模写入可能影响数据库性能
- 建议在低峰期执行大规模写入操作
mutate 模式:内存图修改
mutate 模式将结果写入内存图(而不是数据库),供后续算法使用。这是实现算法流水线的关键模式,适合:
- 多个算法串联执行
- 中间结果不需要持久化
- 避免频繁的数据库写入
-- 第一步:计算 PageRank 并存入内存图
CALL gds.pageRank.mutate('myGraph', {
mutateProperty: 'pagerank'
})
YIELD nodePropertiesWritten
-- 第二步:基于 PageRank 结果计算社区
CALL gds.louvain.mutate('myGraph', {
nodeWeightProperty: 'pagerank', -- 使用第一步的结果作为权重
mutateProperty: 'community'
})
YIELD communityCount
-- 第三步:导出最终结果
CALL gds.louvain.write('myGraph', {
writeProperty: 'community'
})
YIELD nodePropertiesWritten
-- 清理内存图
CALL gds.graph.drop('myGraph')
算法流水线示例:
-- 完整的用户影响力分析流水线
// 1. 创建图投影
CALL gds.graph.project('userInfluence',
['User', 'Post'],
['POSTED', 'FRIEND', 'LIKED', 'COMMENTED']
)
// 2. 计算度中心性(存入内存)
CALL gds.degree.mutate('userInfluence', {
mutateProperty: 'degree'
})
// 3. 计算 PageRank(使用度中心性作为初始权重)
CALL gds.pageRank.mutate('userInfluence', {
mutateProperty: 'pagerank',
sourceNodes: ['User']
})
// 4. 发现社区(使用 PageRank 作为节点权重)
CALL gds.louvain.mutate('userInfluence', {
nodeWeightProperty: 'pagerank',
mutateProperty: 'community'
})
// 5. 计算每个用户的最终影响力得分
CALL gds.alpha.scaleProperties.mutate('userInfluence', {
nodeLabels: ['User'],
mutateProperty: 'influenceScore',
scaler: 'Mean'
})
// 6. 写入所有结果
CALL gds.graph.writeNodeProperties('userInfluence',
['degree', 'pagerank', 'community', 'influenceScore'],
['User']
)
YIELD propertiesWritten
// 7. 分析结果
MATCH (u:User)
RETURN u.name, u.degree, u.pagerank, u.community, u.influenceScore
ORDER BY u.influenceScore DESC
LIMIT 20
stats 模式:统计分析
stats 模式只返回统计信息,不返回具体结果,适合:
- 快速了解数据分布
- 验证参数配置
- 数据探索的初步分析
-- stats 模式:获取 PageRank 分布统计
CALL gds.pageRank.stats('myGraph')
YIELD centralityDistribution
RETURN centralityDistribution
-- 返回示例
{
"min": 0.15,
"max": 5.234,
"mean": 0.876,
"stdDev": 0.543,
"p50": 0.65,
"p75": 1.2,
"p90": 2.1,
"p95": 3.0,
"p99": 4.5
}
-- 使用统计信息调整参数
CALL gds.pageRank.stats('myGraph', {
maxIterations: 30
})
YIELD ranIterations, didConverge
RETURN ranIterations, didConverge
estimate 模式:资源预估
estimate 模式在执行算法前预估资源消耗,这是大规模数据处理的重要步骤:
-- 预估 PageRank 内存消耗
CALL gds.pageRank.write.estimate('myGraph', {
writeProperty: 'pagerank'
})
YIELD requiredMemory, treeView, bytesMin, bytesMax
RETURN requiredMemory, bytesMin, bytesMax
-- 预估社区发现
CALL gds.louvain.stream.estimate('myGraph')
YIELD requiredMemory
RETURN requiredMemory
-- 预估并条件执行
CALL gds.pageRank.write.estimate('myGraph', {
writeProperty: 'pagerank'
})
YIELD requiredMemory
WITH requiredMemory
WHERE requiredMemory CONTAINS 'GB' -- 简单判断
CALL gds.pageRank.write('myGraph', {
writeProperty: 'pagerank'
})
YIELD nodePropertiesWritten
RETURN nodePropertiesWritten
执行模式选择指南
开始分析
│
▼
┌─────────────────┐
│ 需要持久化结果? │
└────────┬────────┘
│
┌───────────┴───────────┐
│ │
▼ ▼
是的 不需要
│ │
▼ ▼
┌──────────────┐ ┌─────────────────┐
│ write 模式 │ │ 需要中间结果? │
└──────────────┘ └────────┬────────┘
│
┌───────────┴───────────┐
│ │
▼ ▼
是的 不需要
│ │
▼ ▼
┌──────────────┐ ┌─────────────────┐
│ mutate 模式 │ │ 只需要统计? │
└──────────────┘ └────────┬────────┘
│
┌───────────┴───────────┐
│ │
▼ ▼
是的 不需要
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ stats 模式 │ │ stream 模式 │
└──────────────┘ └──────────────┘
图投影
在使用 GDS 算法之前,需要先创建图投影。图投影是将 Neo4j 数据库中的数据加载到内存中,供算法高效执行。
为什么需要图投影
直接在数据库上运行算法需要频繁访问磁盘,性能较差。图投影将相关数据一次性加载到内存中,使得算法执行速度提升数十倍甚至数百倍。
创建图投影
命名图(Named Graph):创建一个命名的内存图,可以被多个算法复用。
-- 创建一个简单的图投影
CALL gds.graph.project(
'myGraph', -- 图名称
'Person', -- 节点标签
'FRIEND' -- 关系类型
)
-- 创建带有属性过滤的图投影
CALL gds.graph.project(
'activeUsers',
['Person', 'User'], -- 多个节点标签
['FRIEND', 'FOLLOWS'], -- 多个关系类型
{
nodeProperties: ['age', 'name'], -- 加载节点属性
relationshipProperties: ['weight'] -- 加载关系属性
}
)
-- 使用 Cypher 查询创建图投影(更灵活)
CALL gds.graph.project.cypher(
'myGraph',
'MATCH (p:Person) WHERE p.active = true RETURN id(p) AS id',
'MATCH (a:Person)-[r:FRIEND]->(b:Person) RETURN id(a) AS source, id(b) AS target, r.weight AS weight'
)
管理图投影
-- 列出所有图投影
CALL gds.graph.list()
YIELD graphName, nodeCount, relationshipCount
-- 查看特定图投影的详细信息
CALL gds.graph.list('myGraph')
YIELD graphName, schema
-- 删除图投影
CALL gds.graph.drop('myGraph')
-- 检查图投影是否存在
CALL gds.graph.exists('myGraph')
YIELD exists
中心性算法
中心性算法用于识别图中最重要的节点。不同的中心性度量从不同角度定义"重要性"。
PageRank
PageRank 是 Google 搜索引擎的核心算法,它基于"被重要节点指向的节点更重要"的递归思想来计算节点的重要性。
应用场景:
- 社交网络中的影响力用户识别
- 网页重要性排序
- 学术论文影响力评估
-- 创建图投影
CALL gds.graph.project('socialGraph', 'Person', 'FRIEND')
-- 计算 PageRank 并返回前 10 个最重要的节点
CALL gds.pageRank.stream('socialGraph')
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, score
ORDER BY score DESC
LIMIT 10
-- 写入数据库
CALL gds.pageRank.write('socialGraph', {
writeProperty: 'pagerank',
maxIterations: 20,
dampingFactor: 0.85
})
YIELD nodePropertiesWritten
-- 带权重的 PageRank
CALL gds.pageRank.stream('socialGraph', {
relationshipWeightProperty: 'weight'
})
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, score
ORDER BY score DESC
参数说明:
maxIterations:最大迭代次数,默认 20dampingFactor:阻尼系数,表示用户继续点击链接的概率,默认 0.85relationshipWeightProperty:关系权重属性名
度中心性(Degree Centrality)
度中心性是最简单的中心性度量,它计算节点直接连接的邻居数量。分为入度(incoming)、出度(outgoing)和总度数。
应用场景:
- 识别社交网络中的活跃用户
- 检测网络中的超级节点
- 快速筛选重要节点
-- 计算度中心性
CALL gds.degree.stream('socialGraph')
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, score AS degree
ORDER BY degree DESC
LIMIT 10
-- 计算入度(被关注数)
CALL gds.degree.stream('socialGraph', {
orientation: 'REVERSE'
})
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, score AS inDegree
-- 计算出度(关注数)
CALL gds.degree.stream('socialGraph', {
orientation: 'NATURAL'
})
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, score AS outDegree
中介中心性(Betweenness Centrality)
中介中心性衡量节点在图中作为"桥梁"的重要性。一个节点的中介中心性高,意味着很多最短路径经过它。
应用场景:
- 识别网络中的关键桥梁节点
- 分析信息传播的关键节点
- 网络鲁棒性分析
-- 计算中介中心性
CALL gds.betweenness.stream('socialGraph')
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, score
ORDER BY score DESC
LIMIT 10
-- 使用采样加速大规模图计算
CALL gds.betweenness.stream('socialGraph', {
samplingSize: 1000
})
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, score
ORDER BY score DESC
LIMIT 10
接近中心性(Closeness Centrality)
接近中心性衡量节点到图中所有其他节点的平均距离。接近中心性高的节点可以快速到达图中的其他节点。
应用场景:
- 物流网络中的最优仓库位置选择
- 社交网络中的信息传播效率分析
- 应急设施选址
-- 计算接近中心性
CALL gds.closeness.stream('socialGraph')
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, score
ORDER BY score DESC
LIMIT 10
-- 使用 Wasserman-Faust 改进版本(适合不连通图)
CALL gds.closeness.stream('socialGraph', {
variant: 'wasserman-faust'
})
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS name, score
ORDER BY score DESC
社区发现算法
社区发现算法用于识别图中紧密连接的节点群体,这些群体内部的连接比群体间的连接更加密集。
Louvain 算法
Louvain 是最常用的社区发现算法,它通过最大化模块度(modularity)来划分社区。算法速度快,适合处理大规模图。
应用场景:
- 社交网络中的社区发现
- 生物网络中的功能模块识别
- 用户群体划分
-- 创建图投影
CALL gds.graph.project('communityGraph', 'Person', {
FRIEND: {
orientation: 'UNDIRECTED'
}
})
-- 运行 Louvain 算法
CALL gds.louvain.stream('communityGraph')
YIELD nodeId, communityId
RETURN gds.util.asNode(nodeId).name AS name, communityId
ORDER BY communityId
-- 写入社区 ID 到数据库
CALL gds.louvain.write('communityGraph', {
writeProperty: 'community'
})
YIELD nodePropertiesWritten, communityCount
-- 获取每个社区的成员数量
CALL gds.louvain.stream('communityGraph')
YIELD nodeId, communityId
WITH communityId, count(*) AS memberCount
RETURN communityId, memberCount
ORDER BY memberCount DESC
参数说明:
maxIterations:最大迭代次数tolerance:收敛阈值includeIntermediateCommunities:是否返回中间社区划分
标签传播算法(Label Propagation)
标签传播是一种基于标签扩散的社区发现算法,速度快但结果可能不稳定。
应用场景:
- 大规模网络的快速社区发现
- 半监督学习的标签传播
- 内容分类
-- 运行标签传播算法
CALL gds.labelPropagation.stream('communityGraph')
YIELD nodeId, communityId
RETURN gds.util.asNode(nodeId).name AS name, communityId
ORDER BY communityId
-- 写入数据库
CALL gds.labelPropagation.write('communityGraph', {
writeProperty: 'lpaCommunity'
})
YIELD nodePropertiesWritten
强连通分量(Strongly Connected Components)
强连通分量是指图中任意两个节点都可以互相到达的最大节点集合。有向图中的强连通分量代表紧密互动的群体。
应用场景:
- 识别循环依赖
- 分析有向图的结构
- 发现高度内聚的群体
-- 计算强连通分量
CALL gds.scc.stream('communityGraph')
YIELD nodeId, componentId
RETURN gds.util.asNode(nodeId).name AS name, componentId
ORDER BY componentId
-- 统计各分量大小
CALL gds.scc.stream('communityGraph')
YIELD nodeId, componentId
WITH componentId, count(*) AS size
RETURN componentId, size
ORDER BY size DESC
LIMIT 10
弱连通分量(Weakly Connected Components)
弱连通分量忽略边的方向,将可以互相到达的节点划分为同一分量。常用于检测孤立的子图。
应用场景:
- 检测图中的孤立部分
- 数据质量检查(发现不连通的数据)
- 网络连接性分析
-- 计算弱连通分量
CALL gds.wcc.stream('communityGraph')
YIELD nodeId, componentId
RETURN gds.util.asNode(nodeId).name AS name, componentId
ORDER BY componentId
-- 找出最大的连通分量
CALL gds.wcc.stream('communityGraph')
YIELD nodeId, componentId
WITH componentId, count(*) AS size
ORDER BY size DESC
LIMIT 1
RETURN componentId, size
路径查找算法
路径查找算法用于计算图中节点之间的路径,包括最短路径、所有路径等。
Dijkstra 最短路径
Dijkstra 算法计算两个节点之间的最短路径,适用于带权图。
应用场景:
- 路线规划
- 网络路由
- 资源分配优化
-- 创建带权重的图投影
CALL gds.graph.project(
'weightedGraph',
'Location',
{
ROAD: {
properties: ['distance']
}
}
)
-- 查找两点间的最短路径
MATCH (start:Location {name: 'A'}), (end:Location {name: 'Z'})
CALL gds.shortestPath.dijkstra.stream('weightedGraph', {
sourceNode: start,
targetNode: end,
relationshipWeightProperty: 'distance'
})
YIELD index, sourceNode, targetNode, totalCost, nodeIds, costs
RETURN
index,
gds.util.asNode(sourceNode).name AS source,
gds.util.asNode(targetNode).name AS target,
totalCost,
[nodeId IN nodeIds | gds.util.asNode(nodeId).name] AS path,
costs
-- 计算从某个节点到所有其他节点的最短路径
MATCH (start:Location {name: 'A'})
CALL gds.allShortestPaths.dijkstra.stream('weightedGraph', {
sourceNode: start,
relationshipWeightProperty: 'distance'
})
YIELD nodeId, totalCost
RETURN gds.util.asNode(nodeId).name AS destination, totalCost AS distance
ORDER BY totalCost
LIMIT 10
A* 算法
A* 算法是 Dijkstra 的改进版本,使用启发式函数加速搜索,特别适合地理空间数据。
应用场景:
- 地图导航
- 游戏寻路
- 物流路径规划
-- A* 算法需要地理坐标属性
MATCH (start:Location {name: 'A'}), (end:Location {name: 'Z'})
CALL gds.shortestPath.astar.stream('weightedGraph', {
sourceNode: start,
targetNode: end,
relationshipWeightProperty: 'distance',
latitudeProperty: 'lat',
longitudeProperty: 'lon'
})
YIELD nodeIds, totalCost
RETURN
[nodeId IN nodeIds | gds.util.asNode(nodeId).name] AS path,
totalCost
广度优先搜索(BFS)
BFS 按层次遍历图,适合查找无权图中的最短路径。
-- 从某个节点开始 BFS 遍历
MATCH (start:Location {name: 'A'})
CALL gds.bfs.stream('weightedGraph', {
sourceNode: start
})
YIELD nodeId
RETURN gds.util.asNode(nodeId).name AS name
-- 限制搜索深度
MATCH (start:Location {name: 'A'})
CALL gds.bfs.stream('weightedGraph', {
sourceNode: start,
maxDepth: 3
})
YIELD nodeId
RETURN gds.util.asNode(nodeId).name AS name
深度优先搜索(DFS)
DFS 沿一条路径尽可能深入,然后回溯。
-- 从某个节点开始 DFS 遍历
MATCH (start:Location {name: 'A'})
CALL gds.dfs.stream('weightedGraph', {
sourceNode: start
})
YIELD nodeId
RETURN gds.util.asNode(nodeId).name AS name
相似度算法
相似度算法用于计算节点之间的相似程度,是推荐系统的核心技术。
节点相似度(Node Similarity)
节点相似度基于节点的邻居集合计算 Jaccard 或重叠相似度。
应用场景:
- 协同过滤推荐
- 相似用户发现
- 去重和实体解析
-- 创建图投影
CALL gds.graph.project('similarityGraph', 'User', 'RATED')
-- 计算节点相似度
CALL gds.nodeSimilarity.stream('similarityGraph')
YIELD node1, node2, similarity
WHERE similarity > 0.5
RETURN
gds.util.asNode(node1).name AS user1,
gds.util.asNode(node2).name AS user2,
similarity
ORDER BY similarity DESC
LIMIT 10
-- 创建相似度关系
CALL gds.nodeSimilarity.write('similarityGraph', {
writeRelationshipType: 'SIMILAR_TO',
writeProperty: 'score'
})
YIELD relationshipsWritten
Jaccard 相似度
Jaccard 相似度计算两个集合的交集与并集之比。
-- 直接计算两个节点的 Jaccard 相似度
MATCH (a:User {name: '张三'}), (b:User {name: '李四'})
WITH a, b,
[(a)-[:FRIEND]->(f) | id(f)] AS friendsA,
[(b)-[:FRIEND]->(f) | id(f)] AS friendsB
WITH a, b, friendsA, friendsB,
size([f IN friendsA WHERE f IN friendsB]) AS intersection,
size(friendsA + friendsB) AS unionSize
RETURN a.name, b.name, toFloat(intersection) / unionSize AS jaccard
余弦相似度
余弦相似度常用于计算向量之间的相似程度,如用户评分向量。
-- 使用 GDS 函数计算余弦相似度
WITH [1, 2, 3, 4] AS vector1, [2, 3, 4, 5] AS vector2
RETURN gds.similarity.cosine(vector1, vector2) AS cosineSimilarity
-- 使用 GDS 函数计算 Jaccard 相似度
WITH [1, 2, 3] AS set1, [2, 3, 4] AS set2
RETURN gds.similarity.jaccard(set1, set2) AS jaccardSimilarity
链接预测算法
链接预测算法预测图中未来可能出现的关系。
常用链接预测指标
共同邻居(Common Neighbors):两个节点的共同邻居数量越多,越可能建立关系。
-- 计算共同邻居数
MATCH (a:Person {name: '张三'}), (b:Person {name: '李四'})
WHERE NOT (a)-[:FRIEND]-(b)
WITH a, b,
[(a)-[:FRIEND]-(common)-[:FRIEND]-(b) | common] AS commonNeighbors
RETURN a.name, b.name, size(commonNeighbors) AS commonNeighborCount
Adamic-Adar:考虑共同邻居的度数,度数低的共同邻居贡献更大。
-- 计算 Adamic-Adar 指数
MATCH (a:Person {name: '张三'}), (b:Person {name: '李四'})
WHERE NOT (a)-[:FRIEND]-(b)
WITH a, b
MATCH (a)-[:FRIEND]-(common)-[:FRIEND]-(b)
WITH a, b, common, log(size((common)-[:FRIEND]-())) AS contribution
RETURN a.name, b.name, sum(1.0 / contribution) AS adamicAdar
优先连接(Preferential Attachment):度数高的节点更可能建立新关系。
-- 计算优先连接分数
MATCH (a:Person {name: '张三'}), (b:Person {name: '李四'})
WHERE NOT (a)-[:FRIEND]-(b)
WITH a, b,
size((a)-[:FRIEND]-()) AS degreeA,
size((b)-[:FRIEND]-()) AS degreeB
RETURN a.name, b.name, degreeA * degreeB AS preferentialAttachment
好友推荐示例
-- 综合多种指标推荐好友
MATCH (u:Person {name: '张三'})
MATCH (potential:Person)
WHERE NOT (u)-[:FRIEND]-(potential) AND u <> potential
WITH u, potential
MATCH (u)-[:FRIEND]-(common)-[:FRIEND]-(potential)
WITH u, potential, count(DISTINCT common) AS commonNeighbors
ORDER BY commonNeighbors DESC
LIMIT 10
RETURN potential.name AS recommendedFriend, commonNeighbors
节点嵌入算法
节点嵌入算法将节点转换为低维向量表示,便于机器学习模型使用。
FastRP(Fast Random Projection)
FastRP 是一种高效的节点嵌入算法,它使用随机投影技术生成节点向量。
应用场景:
- 节点分类
- 链接预测
- 聚类分析
- 可视化
-- 生成节点嵌入
CALL gds.fastRP.stream('socialGraph', {
embeddingDimension: 128,
randomSeed: 42
})
YIELD nodeId, embedding
RETURN gds.util.asNode(nodeId).name AS name, embedding
-- 写入嵌入向量到数据库
CALL gds.fastRP.write('socialGraph', {
embeddingDimension: 128,
writeProperty: 'embedding'
})
YIELD nodePropertiesWritten
-- 使用嵌入向量进行相似度计算
MATCH (a:Person {name: '张三'}), (b:Person {name: '李四'})
WITH a.embedding AS embA, b.embedding AS embB
RETURN gds.similarity.cosine(embA, embB) AS similarity
参数说明:
embeddingDimension:嵌入向量维度,常见值 128、256iterationWeights:迭代权重,控制不同跳数邻居的影响randomSeed:随机种子,确保结果可复现
实战案例
案例一:社交网络影响力分析
-- 1. 创建图投影
CALL gds.graph.project('socialNetwork',
['Person', 'Post'],
['POSTED', 'FRIEND', 'LIKED']
)
-- 2. 计算 PageRank 识别影响力用户
CALL gds.pageRank.write('socialNetwork', {
writeProperty: 'influence'
})
-- 3. 发现社区
CALL gds.louvain.write('socialNetwork', {
writeProperty: 'community'
})
-- 4. 分析结果
MATCH (p:Person)
RETURN p.name, p.influence, p.community
ORDER BY p.influence DESC
LIMIT 20
-- 5. 查找每个社区中的意见领袖
MATCH (p:Person)
WITH p.community AS community, p
ORDER BY p.influence DESC
WITH community, collect(p.name)[0..3] AS topInfluencers
RETURN community, topInfluencers
案例二:欺诈团伙检测
-- 1. 创建交易网络图
CALL gds.graph.project('fraudNetwork',
['Account', 'Transaction'],
{
TRANSFERRED: {
properties: ['amount']
}
}
)
-- 2. 使用弱连通分量发现关联账户
CALL gds.wcc.write('fraudNetwork', {
writeProperty: 'cluster'
})
-- 3. 分析每个簇的特征
MATCH (a:Account)
WITH a.cluster AS cluster, count(a) AS size, sum(a.totalAmount) AS totalAmount
WHERE size > 5 -- 簇大小超过阈值
RETURN cluster, size, totalAmount
ORDER BY size DESC
-- 4. 计算中介中心性识别关键账户
CALL gds.betweenness.stream('fraudNetwork')
YIELD nodeId, score
WITH gds.util.asNode(nodeId) AS account, score
ORDER BY score DESC
LIMIT 10
RETURN account.accountId AS accountId, account.totalAmount AS amount, score
案例三:推荐系统
-- 1. 创建用户-商品二部图
CALL gds.graph.project('recommendationGraph',
['User', 'Product'],
{
PURCHASED: {
orientation: 'UNDIRECTED'
}
}
)
-- 2. 计算用户相似度
CALL gds.nodeSimilarity.write('recommendationGraph', {
writeRelationshipType: 'SIMILAR',
writeProperty: 'score',
similarityCutoff: 0.3
})
-- 3. 基于相似用户推荐商品
MATCH (u:User {userId: 'u001'})-[s:SIMILAR]-(similar:User)-[:PURCHASED]->(p:Product)
WHERE NOT (u)-[:PURCHASED]->(p)
WITH p, sum(s.score) AS totalScore
ORDER BY totalScore DESC
LIMIT 10
RETURN p.name AS productName, totalScore AS recommendationScore
-- 4. 生成用户嵌入向量用于冷启动
CALL gds.fastRP.write('recommendationGraph', {
embeddingDimension: 256,
writeProperty: 'userEmbedding'
})
性能优化建议
1. 合理使用图投影
-- 只投影必要的数据
CALL gds.graph.project(
'optimizedGraph',
['Person'], -- 只包含需要的标签
['FRIEND'], -- 只包含需要的关系
{
nodeProperties: ['age'], -- 只加载需要的属性
relationshipProperties: [] -- 不需要关系属性则不加载
}
)
2. 选择合适的执行模式
- 探索性分析:使用
stream模式 - 持久化结果:使用
write模式 - 算法流水线:使用
mutate模式
3. 利用近似算法
对于大规模图,可以使用近似算法加速:
-- 近似中介中心性(使用采样)
CALL gds.betweenness.stream('largeGraph', {
samplingSize: 1000
})
-- 近似最近邻
CALL gds.knn.stream('embeddingGraph', {
topK: 10,
sampleRate: 0.5
})
4. 及时释放内存
-- 使用完毕后删除图投影
CALL gds.graph.drop('myGraph')
-- 估算算法内存消耗
CALL gds.pageRank.stats.estimate('myGraph', {
nodeLabels: ['Person']
})
YIELD requiredMemory
RETURN requiredMemory
小结
本章介绍了 Neo4j Graph Data Science 库的核心功能:
- 图投影:将数据库数据加载到内存,提升算法执行效率
- 中心性算法:识别图中最重要的节点
- 社区发现算法:发现节点群体和社区结构
- 路径查找算法:计算最优路径
- 相似度算法:计算节点相似程度
- 链接预测算法:预测未来关系
- 节点嵌入算法:生成向量表示
图算法是挖掘图数据价值的关键工具。合理选择和组合算法,可以解决复杂的数据分析问题。在实际应用中,建议先用小规模数据测试算法效果,再应用到生产环境。
参考资源
下一步
掌握了图算法后,可以查看 知识速查表 快速回顾所有重要概念和语法。