跳到主要内容

图算法

Neo4j Graph Data Science(GDS)库是一套高性能的图算法集合,提供了超过 50 种经过优化的图算法,用于解决复杂的数据分析问题。这些算法可以帮助你发现隐藏的模式、识别关键节点、预测关系、检测异常等。本章将介绍 GDS 库的核心概念和常用算法的实际应用。

什么是图算法

图算法是对图结构数据进行数学分析的方法,它们利用节点之间的关系来揭示数据的深层特征。与传统的统计分析不同,图算法关注的是实体之间的连接和拓扑结构,这使得它们在处理关联数据时具有独特的优势。

图算法的应用场景

图算法在许多领域都有广泛的应用:

社交网络分析:识别影响力用户、发现社区结构、分析信息传播路径。例如,Twitter 使用 PageRank 算法来识别具有高影响力的用户,Facebook 使用社区发现算法来优化好友推荐。

金融风控:检测欺诈团伙、分析交易网络、识别洗钱模式。图算法可以发现传统方法难以察觉的复杂欺诈网络,例如循环转账、多层转移资金等模式。

推荐系统:基于用户行为模式推荐商品、发现相似用户、计算物品相似度。许多电商平台的"猜你喜欢"功能背后都有图算法的支持。

知识图谱:实体链接、关系推理、知识补全。图算法可以帮助构建更完整的知识网络,支持智能问答和语义搜索。

网络安全:识别关键基础设施节点、检测异常流量、分析攻击路径。在网络安全运维中,图算法可以快速定位潜在的安全风险。

GDS 库概述

安装 GDS 库

GDS 库需要单独安装。Neo4j Desktop 用户可以通过插件市场一键安装,服务器版用户需要下载并配置插件。

Neo4j Desktop 安装

  1. 打开 Neo4j Desktop
  2. 选择你的数据库项目
  3. 点击 "Add" → "Plugins"
  4. 找到 "Graph Data Science Library" 并点击 "Install"
  5. 重启数据库

服务器版安装

  1. 从 Neo4j 官网下载 GDS 库 JAR 文件
  2. 将 JAR 文件放入 $NEO4J_HOME/plugins 目录
  3. 编辑 conf/neo4j.conf,添加:
    dbms.security.procedures.unrestricted=gds.*
    dbms.security.procedures.allowlist=gds.*
  4. 重启 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:最大迭代次数,默认 20
  • dampingFactor:阻尼系数,表示用户继续点击链接的概率,默认 0.85
  • relationshipWeightProperty:关系权重属性名

度中心性(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、256
  • iterationWeights:迭代权重,控制不同跳数邻居的影响
  • 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 库的核心功能:

  1. 图投影:将数据库数据加载到内存,提升算法执行效率
  2. 中心性算法:识别图中最重要的节点
  3. 社区发现算法:发现节点群体和社区结构
  4. 路径查找算法:计算最优路径
  5. 相似度算法:计算节点相似程度
  6. 链接预测算法:预测未来关系
  7. 节点嵌入算法:生成向量表示

图算法是挖掘图数据价值的关键工具。合理选择和组合算法,可以解决复杂的数据分析问题。在实际应用中,建议先用小规模数据测试算法效果,再应用到生产环境。

参考资源

下一步

掌握了图算法后,可以查看 知识速查表 快速回顾所有重要概念和语法。