Leiden 算法(neo4j)

介绍

Leiden 算法是一种用于检测大型网络中社区的算法。该算法将节点分成不相交的社区,以便最大化每个社区的模块度得分。模块度量化了将节点分配给社区的质量,即与随机网络中的节点连接程度相比,社区中节点的连接密度如何。

Leiden 算法是一种分层聚类算法,通过贪婪地优化模块性,将社区递归合并为单个节点,并在压缩图中重复该过程。它修改了Louvain算法以解决其一些缺点,即 Louvain 发现的一些社区连接不紧密的情况。这是通过定期随机将社区分解为较小的连接良好的社区来实现的。

运行此算法需要足够的内存。运行此算法之前,我们建议您阅读内存估算

句法

本节介绍在每种执行模式下执行 Leiden 算法所使用的语法。我们描述了该语法的命名图变体。要了解有关一般语法变体的更多信息,请参阅语法概述

每个模式的莱顿语法
  • 流模式
  • 统计模式
  • 变异模式
  • 写入模式
在命名图表上以流模式运行 Leiden。
CALL gds.leiden.stream(
  graphName: String,
  configuration: Map
)
YIELD
  nodeId: Integer,
  communityId: Integer,
  intermediateCommunityIds: List of Integer

1. 参数

姓名类型默认选修的描述

图名

细绳

n/a

存储在目录中的图表的名称。

配置

地图

{}

是的

算法特定和/或图形过滤的配置。

表 2. 配置
姓名类型默认选修的描述

1.分辨率越高,社区越多,分辨率越低,社区越少。

节点标签

字符串列表

['*']

是的

使用给定的节点标签过滤命名的图表。具有任何给定标签的节点都将包括在内。

关系类型

字符串列表

['*']

是的

使用给定的关系类型过滤命名图。将包括具有任何给定类型的关系。

并发

整数

4

是的

用于运行算法的并发线程数。

工作编号

细绳

Generated internally

是的

可以提供一个ID来更轻松地跟踪算法的进度。

记录进度

布尔值

true

是的

如果禁用,则不会记录进度百分比。

关系权重属性

细绳

null

是的

用作权重的关系属性的名称。如果未指定,则算法运行时不加权。

最大等级

整数

10

是的

图形聚类然后压缩的最大级别数。

伽马

漂浮

1.0

是的

计算模块度时使用的分辨率参数。在内部,该值除以无权图的关系数,否则除以所有关系的权重总和。[1]

西塔

漂浮

0.01

是的

在将一个社区划分为更小的社区的同时控制随机性。

宽容

漂浮

0.0001

是的

迭代之间模块度的最小变化。如果模块度变化小于容差值,则认为结果稳定,算法返回。

包括中级社区

布尔值

false

是的

指示是否写入中间社区。如果设置为 false,则仅保留最终社区。

seed属性

细绳

n/a

是的

用于设置节点的初始社区。属性值必须是非负数。

最小社区规模

整数

0

是的

仅返回社区内大于或等于给定值的节点。

表 3. 结果
姓名类型描述

节点编号

整数

节点ID。

社区编号

整数

最终层的社区ID。

中间社区 ID

整数列表

每个级别的社区 ID。Null如果includeIntermediateCommunities设置为 false。

例子

以下所有示例都应在空数据库中运行。

这些示例以Cypher 投影为标准。未来版本中将不再使用原生投影。

在本节中,我们将展示在具体图表上运行 Leiden 社区检测算法的示例。目的是说明结果是什么样子,并提供如何在实际环境中使用该算法的指南。我们将在一个由少数几个以特定模式连接的节点组成的小型社交网络图上执行此操作。示例图如下所示:

以下 Cypher 语句将在 Neo4j 数据库中创建示例图:
CREATE
  (nAlice:User {name: 'Alice', seed: 42}),
  (nBridget:User {name: 'Bridget', seed: 42}),
  (nCharles:User {name: 'Charles', seed: 42}),
  (nDoug:User {name: 'Doug'}),
  (nMark:User {name: 'Mark'}),
  (nMichael:User {name: 'Michael'}),

  (nAlice)-[:LINK {weight: 1}]->(nBridget),
  (nAlice)-[:LINK {weight: 1}]->(nCharles),
  (nCharles)-[:LINK {weight: 1}]->(nBridget),

  (nAlice)-[:LINK {weight: 5}]->(nDoug),

  (nMark)-[:LINK {weight: 1}]->(nDoug),
  (nMark)-[:LINK {weight: 1}]->(nMichael),
  (nMichael)-[:LINK {weight: 1}]->(nMark);

该图有两个紧密相连的用户集群。这些集群通过一条边连接。关系属性weight 决定了节点之间各自关系的强度。

现在我们可以投影图形并将其存储在图形目录中。我们加载LINK关系并将方向设置为,UNDIRECTED因为这最适合莱顿算法。

以下语句将投影图形并将其存储在图形目录中。
MATCH (source:User)-[r:LINK]->(target:User)
RETURN gds.graph.project(
  'myGraph',
  source,
  target,
  {
    sourceNodeProperties: source { .seed },
    targetNodeProperties: target { .seed },
    relationshipProperties: r { .weight }
  },
  { undirectedRelationshipTypes: ['*'] }
)

在下面的例子中,我们将演示如何在该图上使用莱顿算法。

内存估计

首先,我们将使用该过程估算运行算法的成本estimate。这可以使用任何执行模式来完成。我们将write在本例中使用该模式。估算算法有助于了解在图形上运行算法对内存的影响。当您稍后在某种执行模式下实际运行算法时,系统将执行估算。如果估算显示执行超出内存限制的可能性非常高,则禁止执行。要了解有关此内容的更多信息,请参阅自动估算和执行阻止

有关更多详细信息estimate,请参阅内存估计

以下将估计以写入模式运行算法的内存要求:
CALL gds.leiden.write.estimate('myGraph', {writeProperty: 'communityId', randomSeed: 19})
YIELD nodeCount, relationshipCount, requiredMemory
表 13. 结果
节点数关系计数所需内存

6

14

“[551 KiB...551 KiB]”

数据流

stream执行模式下,算法会返回每个节点的社区 ID。这样我们就可以直接检查结果,或者在 Cypher 中对其进行后处理,而不会产生任何副作用。

有关该模式的更多详细信息stream,请参阅Stream

以下将运行算法并流式传输结果:
CALL gds.leiden.stream('myGraph', { randomSeed: 19 })
YIELD nodeId, communityId
RETURN gds.util.asNode(nodeId).name AS name, communityId
ORDER BY name ASC
表 14.结果
姓名社区编号

“爱丽丝”

1

“布里奇特”

1

“查尔斯”

1

“道格”

5

“标记”

5

“迈克尔”

5

我们对过程配置参数使用默认值。maxLevels设置为 10,gamma参数theta分别设置为 1.0 和 0.01。

统计资料

stats执行模式下,算法返回一行,其中包含算法结果的摘要。此执行模式没有任何副作用。通过检查返回项,它可用于评估算法性能。在下面的示例中,我们将省略返回时间。可以在语法部分computeMillis中找到该过程的完整签名。

下面将运行算法并以统计和测量值的形式返回结果
CALL gds.leiden.stats('myGraph', { randomSeed: 19 })
YIELD communityCount
表 15.结果
社区数量

2

合变

执行mutate模式扩展了该stats模式,但有一个重要的副作用:使用包含该节点社区 ID 的新节点属性更新命名图。使用强制配置参数指定新属性的名称mutateProperty。结果是单个摘要行,类似于stats,但带有一些附加指标。mutate当结合使用多种算法时,该模式特别有用。

有关该模式的更多详细信息mutate,请参阅Mutate

以下将运行该算法并将结果存储在myGraph
CALL gds.leiden.mutate('myGraph', { mutateProperty: 'communityId', randomSeed: 19 })
YIELD communityCount
表 16. 结果
社区数量

2

mutate模式下,该过程仅返回一行。结果包含元信息,例如已识别社区的数量。结果写入 GDS 内存图,而不是 Neo4j 数据库。

执行write模式扩展了该stats模式,并产生了一个重要的副作用:将每个节点的社区 ID 作为属性写入 Neo4j 数据库。新属性的名称使用强制配置参数指定writeProperty。结果是单个摘要行,类似于stats,但包含一些附加指标。该write模式允许将结果直接持久保存到数据库。

有关该模式的更多详细信息write,请参阅写入

以下将运行该算法并将结果存储在 Neo4j 数据库中:
CALL gds.leiden.write('myGraph', { writeProperty: 'communityId', randomSeed: 19 })
YIELD communityCount, nodePropertiesWritten
表 17.结果
社区数量节点属性

2

6

write模式下,该过程仅返回一行。结果包含元信息,例如已识别社区的数量。结果写入 Neo4j 数据库,而不是 GDS 内存图中。

加权

Leiden 算法也可以在加权图上运行,在计算模块度时考虑给定的关系权重。

以下将在加权图上运行算法并流式传输结果:
CALL gds.leiden.stream('myGraph', { relationshipWeightProperty: 'weight', randomSeed: 19 })
YIELD nodeId, communityId
RETURN gds.util.asNode(nodeId).name AS name, communityId
ORDER BY name ASC
表 18.结果
姓名社区编号

“爱丽丝”

4

“布里奇特”

1

“查尔斯”

1

“道格”

4

“标记”

5

“迈克尔”

5

利用加权关系,我们看到AliceDoug形成了他们自己的社区,因为他们的联系比所有其他联系都要强得多。

使用中间社区

如前所述,Leiden 是一种分层聚类算法。这意味着在每个聚类步骤之后,属于同一簇的所有节点都会减少为单个节点。同一簇的节点之间的关系成为自关系,与其他簇的节点的关系连接到簇代表。然后使用此压缩图运行下一级聚类。重复该过程,直到簇稳定。

为了证明这种迭代行为,我们需要构建一个更复杂的图。

CREATE (a:Node {name: 'a'})
CREATE (b:Node {name: 'b'})
CREATE (c:Node {name: 'c'})
CREATE (d:Node {name: 'd'})
CREATE (e:Node {name: 'e'})
CREATE (f:Node {name: 'f'})
CREATE (g:Node {name: 'g'})
CREATE (h:Node {name: 'h'})
CREATE (i:Node {name: 'i'})
CREATE (j:Node {name: 'j'})
CREATE (k:Node {name: 'k'})
CREATE (l:Node {name: 'l'})
CREATE (m:Node {name: 'm'})
CREATE (n:Node {name: 'n'})
CREATE (x:Node {name: 'x'})

CREATE (a)-[:TYPE]->(b)
CREATE (a)-[:TYPE]->(d)
CREATE (a)-[:TYPE]->(f)
CREATE (b)-[:TYPE]->(d)
CREATE (b)-[:TYPE]->(x)
CREATE (b)-[:TYPE]->(g)
CREATE (b)-[:TYPE]->(e)
CREATE (c)-[:TYPE]->(x)
CREATE (c)-[:TYPE]->(f)
CREATE (d)-[:TYPE]->(k)
CREATE (e)-[:TYPE]->(x)
CREATE (e)-[:TYPE]->(f)
CREATE (e)-[:TYPE]->(h)
CREATE (f)-[:TYPE]->(g)
CREATE (g)-[:TYPE]->(h)
CREATE (h)-[:TYPE]->(i)
CREATE (h)-[:TYPE]->(j)
CREATE (i)-[:TYPE]->(k)
CREATE (j)-[:TYPE]->(k)
CREATE (j)-[:TYPE]->(m)
CREATE (j)-[:TYPE]->(n)
CREATE (k)-[:TYPE]->(m)
CREATE (k)-[:TYPE]->(l)
CREATE (l)-[:TYPE]->(n)
CREATE (m)-[:TYPE]->(n);
以下语句将投影图形并将其存储在图形目录中。
MATCH (source:Node)
OPTIONAL MATCH (source)-[r:TYPE]->(target:Node)
RETURN gds.graph.project(
  'myGraph2',
  source,
  target,
  {},
  { undirectedRelationshipTypes: ['*'] }
)
流中间社区
以下将运行算法并流式传输结果(包括中间社区):
CALL gds.leiden.stream('myGraph2', {
  randomSeed: 23,
  includeIntermediateCommunities: true,
  concurrency: 1
})
YIELD nodeId, communityId, intermediateCommunityIds
RETURN gds.util.asNode(nodeId).name AS name, communityId, intermediateCommunityIds
ORDER BY name ASC
表 19. 结果
姓名社区编号中间社区 ID

“A”

4

[2, 4]

“b”

4

[2, 4]

“C”

5

[7, 5]

“d”

4

[2, 4]

“e”

5

[6, 5]

“F”

5

[7, 5]

“G”

5

[7, 5]

“H”

5

[11, 5]

“我”

5

[11, 5]

“j”

1

[12, 1]

“k”

1

[12, 1]

“我”

1

[12, 1]

“米”

1

[12, 1]

“n”

1

[12, 1]

“X”

5

[6, 5]

播种

通过提供种子属性,可以逐步运行 Louvain 算法。如果指定,种子属性将为已加载节点的子集提供初始社区映射。该算法将尝试保留种子社区 ID。

下面将运行算法并传输种子图的结果:
CALL gds.leiden.stream('myGraph2', {
  randomSeed: 23,
  includeIntermediateCommunities: true,
  concurrency: 1
})
YIELD nodeId, communityId, intermediateCommunityIds
RETURN gds.util.asNode(nodeId).name AS name, communityId, intermediateCommunityIds
ORDER BY name ASC
表 20. 结果
姓名社区编号中间社区 ID

“爱丽丝”

四十二

无效的

“布里奇特”

四十二

无效的

“查尔斯”

四十二

无效的

“道格”

四十五

无效的

“标记”

四十五

无效的

“迈克尔”

四十五

无效的

可以看出,使用种子图,节点Alice保留其初始社区 ID 42。另一个社区已被分配一个新的社区 ID,该 ID 保证大于最大种子社区 ID。请注意,consecutiveIds配置选项不能与种子结合使用,以保留种子值

变异中间社区
以下将运行该算法并使用中间社区改变内存图:
CALL gds.leiden.stream('myGraph2', {
  randomSeed: 23,
  includeIntermediateCommunities: true,
  concurrency: 1
})
YIELD nodeId, communityId, intermediateCommunityIds
RETURN gds.util.asNode(nodeId).name AS name, communityId, intermediateCommunityIds
ORDER BY name ASC
表 21.结果
社区数量模块化模块化

3

0.3624

[0.3296, 0.3624]

以下从内存图中流式传输变异的属性:
CALL gds.leiden.stream('myGraph2', {
  randomSeed: 23,
  includeIntermediateCommunities: true,
  concurrency: 1
})
YIELD nodeId, communityId, intermediateCommunityIds
RETURN gds.util.asNode(nodeId).name AS name, communityId, intermediateCommunityIds
ORDER BY name ASC
表 22. 结果
姓名中级社区

“A”

[2, 4]

“b”

[2, 4]

“C”

[7, 5]

“d”

[2, 4]

“e”

[6, 5]

“F”

[7, 5]

“G”

[7, 5]

“H”

[11, 5]

“我”

[11, 5]

“j”

[12, 1]

“k”

[12, 1]

“我”

[12, 1]

“米”

[12, 1]

“n”

[12, 1]

“X”

[6, 5]

撰写中间社区
以下将运行该算法并将中间社区写入 Neo4j 数据库:
CALL gds.leiden.stream('myGraph2', {
  randomSeed: 23,
  includeIntermediateCommunities: true,
  concurrency: 1
})
YIELD nodeId, communityId, intermediateCommunityIds
RETURN gds.util.asNode(nodeId).name AS name, communityId, intermediateCommunityIds
ORDER BY name ASC
表 23. 结果
社区数量模块化模块化

3

0.3624

[0.3296, 0.3624]

下面从 Neo4j 数据库流式传输写入的属性:
CALL gds.leiden.stream('myGraph2', {
  randomSeed: 23,
  includeIntermediateCommunities: true,
  concurrency: 1
})
YIELD nodeId, communityId, intermediateCommunityIds
RETURN gds.util.asNode(nodeId).name AS name, communityId, intermediateCommunityIds
ORDER BY name ASC
表 24.结果
姓名中级社区

“A”

[2, 4]

“b”

[2, 4]

“C”

[7, 5]

“d”

[2, 4]

“e”

[6, 5]

“F”

[7, 5]

“G”

[7, 5]

“H”

[11, 5]

“我”

[11, 5]

“j”

[12, 1]

“k”

[12, 1]

“我”

[12, 1]

“米”

[12, 1]

“n”

[12, 1]

“X”

[6, 5]

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北京橙溪 www.enwing.com

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值