Leiden
简介
Leiden 算法是一种用于检测大型网络中社区的算法。该算法将节点划分为不相交的社区,以最大化每个社区的模块度得分。模块度量化了节点分配到社区的质量,即与在随机网络中相比,社区内的节点连接有多紧密。
Leiden 算法是一种层次聚类算法,它通过贪婪地优化模块度,递归地将社区合并为单个节点,并在压缩后的图中重复此过程。它改进了 Louvain 算法以解决其部分缺陷,即 Louvain 发现的一些社区连接不紧密的情况。这是通过定期随机地将社区分解为较小的、连接紧密的社区来实现的。
有关此算法的更多信息,请参阅
语法
本节涵盖执行 Leiden 算法所使用的语法。
CALL Neo4j_Graph_Analytics.graph.leiden(
'CPU_X64_XS', (1)
{
['defaultTablePrefix': '...',] (2)
'project': {...}, (3)
'compute': {...}, (4)
'write': {...} (5)
}
);
| 1 | 计算池选择器。 |
| 2 | 表引用的可选前缀。 |
| 3 | 项目配置。 |
| 4 | 计算配置。 |
| 5 | 写入配置。 |
| 名称 | 类型 | 默认 | 可选 | 描述 |
|---|---|---|---|---|
computePoolSelector |
字符串 |
|
否 |
用于运行 Leiden 作业的计算池选择器。 |
配置 |
Map |
|
否 |
用于图项目、算法计算和结果回写的配置。 |
配置映射由以下三个条目组成。
| 有关以下项目配置的更多详细信息,请参阅 项目文档。 |
| 名称 | 类型 |
|---|---|
nodeTables |
节点表列表。 |
relationshipTables |
关系类型到关系表的映射。 |
| 名称 | 类型 | 默认 | 可选 | 描述 |
|---|---|---|---|---|
resultProperty |
字符串 |
|
是 |
将回写到 Snowflake 数据库的节点属性。 |
relationshipWeightProperty |
字符串 |
|
是 |
用作权重的关系属性名称。如果未指定,算法将作为无权重运行。 |
seedProperty |
字符串 |
|
是 |
用于设置节点的初始社区。属性值必须为非负数。 |
maxLevels |
整数 |
|
是 |
图进行聚类并随后压缩的最大层级数。 |
tolerance |
浮点数 |
|
是 |
迭代之间模块度的最小变化值。如果模块度变化小于该容差值,则结果被视为稳定,算法终止。 |
includeIntermediateCommunities |
布尔值 |
|
是 |
指示是否写入中间社区。如果设置为 false,则仅保留最终的社区。 |
gamma |
浮点数 |
|
是 |
计算模块度时使用的分辨率参数。在内部,该值对于无权图除以关系总数,否则除以所有关系权重之和。[1] |
theta |
浮点数 |
|
是 |
控制将社区分解为较小社区时的随机性。 |
1. 分辨率越高,产生的社区越多;分辨率越低,产生的社区越少。 |
||||
| 有关以下写入配置的更多详细信息,请参阅写入文档。 |
| 名称 | 类型 | 默认 | 可选 | 描述 |
|---|---|---|---|---|
nodeProperty |
字符串 |
|
是 |
将回写到 Snowflake 数据库的节点属性。 |
示例
在本节中,我们将展示在具体图上运行 Leiden 社区检测算法的示例。目的是说明结果的样子,并提供关于如何在实际环境中使用该算法的指南。我们将在一个小型社交网络图上进行演示,其中少数节点以特定模式连接。示例图如下所示
CREATE OR REPLACE TABLE EXAMPLE_DB.DATA_SCHEMA.USERS (NODEID VARCHAR, SEED NUMBER);
INSERT INTO EXAMPLE_DB.DATA_SCHEMA.USERS VALUES
('Alice', 42),
('Bridget', 42),
('Charles', 42),
('Doug', NULL),
('Mark', NULL),
('Michael', NULL);
CREATE OR REPLACE TABLE EXAMPLE_DB.DATA_SCHEMA.LINKS (SOURCENODEID VARCHAR, TARGETNODEID VARCHAR, WEIGHT FLOAT);
INSERT INTO EXAMPLE_DB.DATA_SCHEMA.LINKS VALUES
('Alice', 'Bridget', 1),
('Alice', 'Charles', 1),
('Charles', 'Bridget', 1),
('Alice', 'Doug', 5),
('Mark', 'Doug', 1),
('Mark', 'Michael', 1),
('Michael', 'Mark', 1);
该图有两个联系紧密的 Users(用户)集群。在这些集群之间有一条单一的边。连接每个组件中节点的这些关系具有一个 weight(权重)属性,该属性决定了关系的强度。
我们加载方向设置为 UNDIRECTED(无向)的 LINK 关系,因为这与 Leiden 算法配合使用效果最佳。
在 Snowflake 中拥有节点和关系表后,我们现在可以将其作为算法作业的一部分进行投影。在以下示例中,我们将演示如何在此时该图上使用 Leiden 算法。
运行作业
运行 Leiden 作业涉及三个步骤:投影 (Project)、计算 (Compute) 和写入 (Write)。
要运行查询,需要为应用程序、您的消费者角色和您的环境设置必要的权限。请参阅 入门 页面以了解更多信息。
我们还假设应用程序名称为默认的 Neo4j_Graph_Analytics。如果您在安装过程中选择了不同的应用程序名称,请将其替换为该名称。
CALL Neo4j_Graph_Analytics.graph.leiden('CPU_X64_XS', {
'defaultTablePrefix': 'EXAMPLE_DB.DATA_SCHEMA',
'project': {
'nodeTables': [ 'USERS' ],
'relationshipTables': {
'LINKS': {
'sourceTable': 'USERS',
'targetTable': 'USERS',
'orientation': 'UNDIRECTED'
}
}
},
'compute': {
'randomSeed': 19
},
'write': [{
'nodeLabel': 'USERS',
'outputTable': 'USERS_COMMUNITY'
}]
});
| JOB_ID | JOB_STATUS | JOB_START | JOB_END | JOB_RESULT |
|---|---|---|---|---|
job_7783bee73d084df19e254550b9a3a186 |
SUCCESS |
2025-07-16 08:56:33.449 |
2025-07-16 08:56:38.060 |
{
"leiden_1": {
"communityCount": 2,
"communityDistribution": {
"max": 3,
"mean": 3,
"min": 3,
"p1": 3,
"p10": 3,
"p25": 3,
"p5": 3,
"p50": 3,
"p75": 3,
"p90": 3,
"p95": 3,
"p99": 3,
"p999": 3
},
"computeMillis": 109,
"configuration": {
"concurrency": 2,
"consecutiveIds": false,
"gamma": 1,
"includeIntermediateCommunities": false,
"maxLevels": 10,
"nodeLabels": [
"*"
],
"randomSeed": 19,
"relationshipTypes": [
"*"
],
"resultProperty": "community",
"seedProperty": null,
"theta": 0.01,
"tolerance": 1.000000000000000e-04
},
"didConverge": true,
"modularities": [
0.3571428571428571
],
"modularity": 0.3571428571428571,
"nodeCount": 6,
"ranLevels": 1
},
"project_1": {
"graphName": "snowgraph",
"nodeCount": 6,
"nodeLabels": ...,
"nodeMillis": 634,
"relationshipCount": 14,
"relationshipMillis": 828,
"relationshipTypes": ...,
"totalMillis": 1462
},
"write_node_property_1": {
"copyIntoTableMillis": 1735,
"nodeLabel": "USERS",
"nodeProperty": "community",
"outputTable": "EXAMPLE_DB.DATA_SCHEMA.USERS_COMMUNITY",
"rowsWritten": 6,
"stageUploadMillis": 1243,
"writeMillis": 3264
}
} |
返回的结果包含有关作业执行和结果分布的信息。此外,每个节点的社区 ID 已写回 Snowflake 数据库。我们可以像这样查询它
SELECT * FROM EXAMPLE_DB.DATA_SCHEMA.USERS_COMMUNITY;
| NODEID | 社区版 |
|---|---|
Alice |
2 |
Bridget |
2 |
Charles |
2 |
Doug |
5 |
Mark |
5 |
Michael |
5 |
除了随机种子外,我们对过程配置参数使用默认值。maxLevels 设置为 10,gamma 和 theta 参数分别设置为 1.0 和 0.01。
加权
Leiden 算法也可以在加权图上运行,在计算模块度时将给定的关系权重考虑在内。
CALL Neo4j_Graph_Analytics.graph.leiden('CPU_X64_XS', {
'defaultTablePrefix': 'EXAMPLE_DB.DATA_SCHEMA',
'project': {
'nodeTables': [ 'USERS' ],
'relationshipTables': {
'LINKS': {
'sourceTable': 'USERS',
'targetTable': 'USERS',
'orientation': 'UNDIRECTED'
}
}
},
'compute': {
'randomSeed': 19,
'relationshipWeightProperty': 'WEIGHT'
},
'write': [{
'nodeLabel': 'USERS',
'outputTable': 'USERS_COMMUNITY_WEIGHTED'
}]
});
| JOB_ID | JOB_STATUS | JOB_START | JOB_END | JOB_RESULT |
|---|---|---|---|---|
job_7783bee73d084df19e254550b9a3a186 |
SUCCESS |
2025-07-16 08:56:33.449 |
2025-07-16 08:56:38.060 |
{
"leiden_1": {
"communityCount": 3,
"communityDistribution": {
"max": 2,
"mean": 2,
"min": 2,
"p1": 2,
"p10": 2,
"p25": 2,
"p5": 2,
"p50": 2,
"p75": 2,
"p90": 2,
"p95": 2,
"p99": 2,
"p999": 2
},
"computeMillis": 142,
"configuration": {
"concurrency": 2,
"consecutiveIds": false,
"gamma": 1,
"includeIntermediateCommunities": false,
"maxLevels": 10,
"nodeLabels": [
"*"
],
"randomSeed": 19,
"relationshipTypes": [
"*"
],
"relationshipWeightProperty": "WEIGHT",
"resultProperty": "community",
"seedProperty": null,
"theta": 0.01,
"tolerance": 1.000000000000000e-04
},
"didConverge": true,
"modularities": [
0.2933884297520661
],
"modularity": 0.2933884297520661,
"nodeCount": 6,
"ranLevels": 1
},
"project_1": {
"graphName": "snowgraph",
"nodeCount": 6,
"nodeLabels": ...,
"nodeMillis": 225,
"relationshipCount": 14,
"relationshipMillis": 437,
"relationshipTypes": ...,
"totalMillis": 662
},
"write_node_property_1": {
"copyIntoTableMillis": 2014,
"nodeLabel": "USERS",
"nodeProperty": "community",
"outputTable": "EXAMPLE_DB.DATA_SCHEMA.USERS_COMMUNITY_WEIGHTED",
"rowsWritten": 6,
"stageUploadMillis": 1355,
"writeMillis": 3660
}
} |
SELECT * FROM EXAMPLE_DB.DATA_SCHEMA.USERS_COMMUNITY;
| NODEID | COMMUNITY_ID |
|---|---|
Alice |
3 |
Bridget |
2 |
Charles |
2 |
Doug |
3 |
Mark |
5 |
Michael |
5 |
使用加权关系,我们看到 Alice 和 Doug 形成了他们自己的社区,因为他们的链接比其他所有链接都要强得多。
种子节点
通过提供种子属性,可以增量运行 Leiden 算法。如果指定,种子属性为已加载节点的子集提供初始社区映射。该算法将尝试保留种子社区 ID。
CALL Neo4j_Graph_Analytics.graph.leiden('CPU_X64_XS', {
'defaultTablePrefix': 'EXAMPLE_DB.DATA_SCHEMA',
'project': {
'nodeTables': [ 'USERS' ],
'relationshipTables': {
'LINKS': {
'sourceTable': 'USERS',
'targetTable': 'USERS',
'orientation': 'UNDIRECTED'
}
}
},
'compute': {
'randomSeed': 19,
'seedProperty': 'SEED'
},
'write': [{
'nodeLabel': 'USERS',
'outputTable': 'USERS_COMMUNITY_SEEDED'
}]
});
| JOB_ID | JOB_STATUS | JOB_START | JOB_END | JOB_RESULT |
|---|---|---|---|---|
job_79891de200694d55a9b6822a5a9c8993 |
SUCCESS |
2025-07-16 09:25:30.436 |
2025-07-16 09:25:35.139 |
{
"leiden_1": {
"communityCount": 2,
"communityDistribution": {
"max": 3,
"mean": 3,
"min": 3,
"p1": 3,
"p10": 3,
"p25": 3,
"p5": 3,
"p50": 3,
"p75": 3,
"p90": 3,
"p95": 3,
"p99": 3,
"p999": 3
},
"computeMillis": 96,
"configuration": {
"concurrency": 2,
"consecutiveIds": false,
"gamma": 1,
"includeIntermediateCommunities": false,
"maxLevels": 10,
"nodeLabels": [
"*"
],
"randomSeed": 19,
"relationshipTypes": [
"*"
],
"resultProperty": "community",
"seedProperty": "SEED",
"theta": 0.01,
"tolerance": 1.000000000000000e-04
},
"didConverge": true,
"modularities": [
0.3571428571428571
],
"modularity": 0.3571428571428571,
"nodeCount": 6,
"ranLevels": 1
},
"project_1": {
"graphName": "snowgraph",
"nodeCount": 6,
"nodeLabels": ...,
"nodeMillis": 195,
"relationshipCount": 14,
"relationshipMillis": 396,
"relationshipTypes": ...,
"totalMillis": 591
},
"write_node_property_1": {
"copyIntoTableMillis": 1162,
"nodeLabel": "USERS",
"nodeProperty": "community",
"outputTable": "EXAMPLE_DB.DATA_SCHEMA.USERS_COMMUNITY_SEEDED",
"rowsWritten": 6,
"stageUploadMillis": 1706,
"writeMillis": 3152
}
} |
SELECT * FROM EXAMPLE_DB.DATA_SCHEMA.USERS_COMMUNITY;
| NODEID | COMMUNITY_ID |
|---|---|
Alice |
42 |
Bridget |
42 |
Charles |
42 |
Doug |
45 |
Mark |
45 |
Michael |
45 |
可以看出,使用带种子的图,节点 Alice 保留了其初始社区 ID 42。另一个社区被分配了一个新的社区 ID,保证该 ID 大于最大的种子社区 ID。
使用中间社区
如前所述,Leiden 是一种层次聚类算法。这意味着在每一步聚类之后,属于同一个簇的所有节点都会被缩减为一个节点。同一簇内节点之间的关系变为自关系,与其他簇节点的关系则连接到该簇的代表节点。然后使用这个压缩后的图来运行下一级聚类。重复此过程,直到簇稳定为止。
为了演示这种迭代行为,我们需要构建一个更复杂的图。
CREATE OR REPLACE TABLE EXAMPLE_DB.DATA_SCHEMA.NODES (NODEID STRING);
INSERT INTO EXAMPLE_DB.DATA_SCHEMA.NODES VALUES
('a'),
('b'),
('c'),
('d'),
('e'),
('f'),
('g'),
('h'),
('i'),
('j'),
('k'),
('l'),
('m'),
('n'),
('x');
CREATE OR REPLACE TABLE EXAMPLE_DB.DATA_SCHEMA.TYPES (SOURCENODEID STRING, TARGETNODEID STRING);
INSERT INTO EXAMPLE_DB.DATA_SCHEMA.TYPES VALUES
('a', 'b'),
('a', 'd'),
('a', 'f'),
('b', 'd'),
('b', 'x'),
('b', 'g'),
('b', 'e'),
('c', 'x'),
('c', 'f'),
('d', 'k'),
('e', 'x'),
('e', 'f'),
('e', 'h'),
('f', 'g'),
('g', 'h'),
('h', 'i'),
('h', 'j'),
('i', 'k'),
('j', 'k'),
('j', 'm'),
('j', 'n'),
('k', 'm'),
('k', 'l'),
('l', 'n'),
('m', 'n');
现在我们可以看到该算法的迭代流程
CALL Neo4j_Graph_Analytics.graph.leiden('CPU_X64_XS', {
'defaultTablePrefix': 'EXAMPLE_DB.DATA_SCHEMA',
'project': {
'nodeTables': [ 'NODES' ],
'relationshipTables': {
'TYPES': {
'sourceTable': 'NODES',
'targetTable': 'NODES',
'orientation': 'UNDIRECTED'
}
}
},
'compute': {
'randomSeed': 23,
'includeIntermediateCommunities': true
},
'write': [{
'nodeLabel': 'NODES',
'outputTable': 'NODES_INTERMEDIATE_COMMUNITY'
}]
});
| JOB_ID | JOB_STATUS | JOB_START | JOB_END | JOB_RESULT |
|---|---|---|---|---|
job_d2aa3973d9744157b2a6be3cc30a3ee6 |
SUCCESS |
2025-07-16 09:09:59.400 |
2025-07-16 09:10:05.025 |
{
"leiden_1": {
"communityCount": 3,
"communityDistribution": {
"max": 7,
"mean": 5,
"min": 3,
"p1": 3,
"p10": 3,
"p25": 3,
"p5": 3,
"p50": 5,
"p75": 7,
"p90": 7,
"p95": 7,
"p99": 7,
"p999": 7
},
"computeMillis": 104,
"configuration": {
"concurrency": 2,
"consecutiveIds": false,
"gamma": 1,
"includeIntermediateCommunities": true,
"maxLevels": 10,
"nodeLabels": [
"*"
],
"randomSeed": 23,
"relationshipTypes": [
"*"
],
"resultProperty": "community",
"seedProperty": null,
"theta": 0.01,
"tolerance": 1.000000000000000e-04
},
"didConverge": true,
"modularities": [
0.37599999999999995,
0.3816
],
"modularity": 0.3816,
"nodeCount": 15,
"ranLevels": 2
},
"project_1": {
"graphName": "snowgraph",
"nodeCount": 15,
"nodeLabels": ...,
"nodeMillis": 1124,
"relationshipCount": 50,
"relationshipMillis": 681,
"relationshipTypes": ...,
"totalMillis": 1805
},
"write_node_property_1": {
"copyIntoTableMillis": 1440,
"nodeLabel": "NODES",
"nodeProperty": "community",
"outputTable": "EXAMPLE_DB.DATA_SCHEMA.NODES_INTERMEDIATE_COMMUNITY",
"rowsWritten": 15,
"stageUploadMillis": 1393,
"writeMillis": 3142
}
} |
SELECT * FROM EXAMPLE_DB.DATA_SCHEMA.NODES_INTERMEDIATE_COMMUNITY;
| NODEID | INTERMEDIATE_COMMUNITIES |
|---|---|
a |
[3, 1] |
b |
[3, 1] |
c |
[14, 1] |
d |
[3, 1] |
e |
[14, 1] |
f |
[14, 1] |
g |
[8, 2] |
h |
[8, 2] |
i |
[8, 2] |
j |
[12, 3] |
k |
[12, 3] |
l |
[12, 3] |
m |
[12, 3] |
n |
[12, 3] |
x |
[14, 1] |
在此示例图中,在第一次迭代后我们看到 4 个集群,在第二次迭代中这些集群被合并为三个。