关键词:Community Detection Algorithms
一、说明
在本教程中,我们将探讨网络分析的两个基本方面:社区检测和使用子图。了解这些概念将使您能够发现复杂网络中隐藏的结构和关系。
二、何为社区,何为社区检测?
2.1 关于社区检测和相关研究
你们很多人都熟悉网络,对吧?你可能正在使用 Facebook、Instagram、Twitter 等社交媒体网站。它们就是社交网络。你可能正在与证券交易所打交道。你可能正在买入新股,也可能卖出现有股票等等。它们就是网络。不仅在技术领域,而且在我们的日常社交生活中,我们都与许多网络打交道。社区是许多网络的属性,特定网络可能包含多个社区,社区内的节点紧密相连。多个社区中的节点可以重叠。想想你的 Facebook 或 Instagram 帐户,想想你每天与谁互动。你可能与朋友、同事、家人以及生活中其他一些重要的人密切互动。他们在你的社交网络中构成了一个非常密集的社区。
M. Girvan 和 MEJ Newman 是社群发现领域的两位著名研究者。在他们的一项研究中,他们利用社交网络和生物网络研究了社群的结构属性。他们认为,网络节点在社群内部以紧密的群体形式紧密连接,而在社群之间则以松散的连接形式存在。
2.2 为什么要进行社区检测?
在分析不同的网络时,发现其中的社群可能至关重要。社群检测技术有助于社交媒体算法发现具有共同兴趣的群体,并让他们保持紧密联系。社群检测技术可用于机器学习,检测具有相似属性的群体,并根据各种原因提取群体。例如,这项技术可用于发现社交网络或股票市场中的操纵性群体。
2.3 社区检测与聚类
有人可能会认为社群检测类似于聚类。聚类是一种机器学习技术,它根据相似的数据点的属性将其归入同一聚类。尽管聚类可以应用于网络,但它是无监督机器学习中一个更广泛的领域,涉及多种属性类型。另一方面,社群检测专门针对网络分析,它依赖于一种称为“边”的属性类型。此外,聚类算法倾向于将单个外围节点与其应属于的社群分离。然而,聚类和社群检测技术都可以应用于许多网络分析问题,并且根据领域的不同,它们可能存在不同的优缺点。
三、关于社区检测技术
社区发现方法大致可分为两类:凝聚法和分裂法。凝聚法是将边逐一添加到仅包含节点的图中,边的添加顺序是从强边到弱边。分裂法与凝聚法相反,是将边从完整的图中逐一移除。
给定网络中的社区数量可能不限,且规模大小不一。这些特性使得社区检测过程非常困难。然而,在社区检测领域已提出了许多不同的技术。以下介绍四种流行的社区检测算法。所有这些列出的算法都可以在Python 的 cdlib 库中找到。
3.1. 鲁汶社区检测
Louvain 社区发现算法最初于 2008 年提出,是一种适用于大型网络的快速社区展开方法。该方法基于模块度,试图最大化社区中实际边数与预期边数之间的差异。然而,优化网络中的模块度是 NP 难题,因此必须使用启发式方法。Louvain 算法分为两个迭代重复的阶段:
1 节点的局部移动
2 网络聚合
该算法从一个由 N 个节点组成的加权网络开始。在第一阶段,该算法为网络中的每个节点分配一个不同的社区。然后,对于每个节点,算法会考虑其邻居,并评估其模块化增益通过从当前社区中移除特定节点并将其放入邻居社区中。如果增益为正且最大化,则该节点将被放入邻居社区。如果没有正增益,则节点将保留在同一个社区中。此过程重复应用于所有节点,直到不再有进一步的改进。当获得模块度的局部最大值时,Louvain 算法的第一阶段停止。在第二阶段中,该算法将构建一个新网络,将在第一阶段找到的社区视为节点。第二阶段完成后,该算法将对生成的网络重新应用第一阶段。重复这些步骤,直到网络不再发生变化且获得最大模块度。
Louvain 社区发现算法能够在过程中发现社区的社区。该算法因其易于实现且速度快而广受欢迎。然而,该算法的一个主要限制在于其对网络在主内存中的存储的使用。
下面给出了使用 python cdlib 库的 Louvain 社区检测算法的使用。
1)从 cdlib 导入算法
2)导入 networkx 作为 nx
3)G = nx.karate_club_graph()
4)coms = algorithms.louvain(G, weight=‘weight’, resolution=1., randomize=False)
3.2. 意外社区检测
由于模块度的局限性,引入了一种基于经典概率的度量方法,称为“意外度”(Surprise),用于评估网络社区划分的质量。该算法与鲁汶社区检测算法几乎相似,只是它使用意外度而非模块度。节点从一个社区移动到另一个社区,从而贪婪地提高意外度。这种方法考虑的是链接位于社区内的概率。在社区规模较小的情况下,使用意外度效果良好 ;而在社区规模较小的情况下,使用模块度效果良好。
下面给出了使用 python cdlib 库的 Surprise 社区检测算法的使用方法。
1)从 cdlib 导入算法
2)导入 networkx 作为 nx
3)G = nx.karate_club_graph()
4)coms = algorithms.surprise_communities(G)
3.3. 莱顿社区检测
在后来的研究中(2019 年),VA Traag 等人表明,Louvain 社区检测倾向于发现内部不连通的社区(连接性较差的社区)。在 Louvain 算法中,将一个充当社区中两个组成部分之间桥梁的节点移至新社区可能会导致旧社区的连接断开。如果旧社区进一步分裂,则不会出现这种情况。但根据 Traag 等人的研究,情况并非如此。由于旧社区中的其他节点与社区之间的连接紧密,旧社区可以保持为一个单一社区。此外,他们还表示,Louvain 倾向于发现连接性非常差的社区。因此,他们提出了速度更快的 Leiden 算法,该算法可以保证社区的连接性良好。
除了 Louvain 算法中使用的阶段之外,Leiden 算法还使用了另一个阶段,用于尝试优化已发现的分区。Leiden 算法的三个阶段如下:
1)节点的局部移动
2)分区细化
3)基于精细划分的网络聚合
在细化阶段,算法尝试从第一阶段提出的分区中识别出细化的分区。第一阶段提出的社区可能会在第二阶段进一步分裂成多个分区。细化阶段不遵循贪婪方法,可能会将节点与随机选择的社区合并,从而提高质量函数。这种随机性允许更广泛地发现分区空间。同样在第一阶段,莱顿采用了与鲁汶不同的方法。莱顿不是在第一次访问所有节点后访问网络中的所有节点,而是只访问那些邻域发生变化的节点。
下面给出了使用 python cdlib 库的莱顿社区检测算法的使用。
1)从 cdlib 导入算法
2)导入 networkx 作为 nx
3)G = nx.karate_club_graph()
4)coms = algorithms.leiden(G)
3.4. Walktrap 社区检测
Walktrap 是另一种基于随机游动的社区检测方法,其中通过网络中的随机游动来测量顶点之间的距离。Walktrap 是一种高效的算法,在最坏的情况下,时间复杂度为 O(mn²),空间复杂度为 O(n²)。但在大多数实际场景中,walktrap 的时间复杂度为 O((n²) log n),空间复杂度为 O(n²)。该算法的基本原理是,图 / 网络上的随机游动往往会陷入与社区相对应的紧密连接部分。Walktrap 使用随机游动的结果以自下而上的方式合并单独的社区。可以使用任何可用的质量标准来评估分区的质量。它可以是像 Louvain 社区检测中的模块化,也可以是任何其他度量。
下面给出了使用 python cdlib 库的 Walktrap 社区检测算法的使用方法。
1)从 cdlib 导入算法
2)导入 networkx 作为 nx
3)G = nx.karate_club_graph()
4)coms = algorithms.walktrap(G)
3.5 小结
社群检测非常适用于理解和评估大型复杂网络的结构。这种方法利用图或网络中边的属性,因此比聚类方法更适合网络分析。聚类算法倾向于将单个外围节点与其应归属的社群分离。目前已提出并实现了多种不同的网络社群检测算法。每种算法都有各自的优缺点,具体取决于网络的性质以及应用问题领域。
四、实验进行识别社区
社区检测算法旨在将网络划分为多个社区。有几种算法可用,但我们将重点介绍 Girvan-Newman 算法和 Louvain 方法。
4.1 社区检测与聚类
有人可能会争辩说,社区检测类似于聚类。聚类是一种机器学习技术,其中相似的数据点根据其属性分组到同一集群中。尽管聚类可以应用于网络,但它是无监督机器学习中一个更广泛的领域,涉及多种属性类型。另一方面,社区检测是专门为网络分析量身定制的,该网络分析依赖于称为 edges 的单个属性类型。此外,聚类算法倾向于将单个外围节点与它应该属于的社区分开。但是,聚类和社区检测技术都可以应用于许多网络分析问题,并且可能会根据域的不同而产生不同的优缺点。
4.2 使用 NetworkX 进行社区检测
NetworkX 提供了应用各种社区检测算法的工具。让我们从 Girvan-Newman 算法开始。
社区检测技术
- Girvan-Newman 算法
Girvan-Newman 算法通过逐步删除具有最高中介中心性的边来识别社区。以下是在 NetworkX 中实现 Girvan-Newman 算法的方法:
import networkx as nx
from networkx.algorithms.community import girvan_newman
import matplotlib.pyplot as pltG = nx.karate_club_graph()# Apply Girvan-Newman algorithm
communities = girvan_newman(G)# Get the first set of communities (you can iterate further for more levels)
node_groups = list(next(communities))# Assign colors to nodes based on their community
node_colors = []
for node in G.nodes():if node in node_groups[0]:node_colors.append('red')else:node_colors.append('blue')# Draw the graph with nodes colored by community
nx.draw(G, with_labels=True, node_color=node_colors)
plt.show()
Output:
4.3 . 鲁汶法
鲁汶方法是一种通过优化模块化来检测网络中社区的有效算法,模块化是一种评估网络内社区强度的度量。它的工作原理是在社区之间迭代移动节点,以最大限度地提高模块化,从而突出显示紧密连接的组。该方法快速、可扩展,并广泛用于各个领域,以揭示复杂网络中隐藏的结构。
鲁汶法的计算代码:
import networkx as nx
from community import community_louvain
import matplotlib.pyplot as plt# Assuming you have your graph G defined# Apply Louvain algorithm
partition = community_louvain.best_partition(G)# Get the community assignments for each node
node_colors = [partition[node] for node in G.nodes()]# Draw the graph with nodes colored by community
nx.draw(G, with_labels=True, node_color=node_colors)
plt.show()
输出:
4.4 图论中的子图
子图是由其顶点(节点)的子集和连接这些顶点的边形成的图形的一部分。分析子图有助于识别较大图中的关系、社区或特定特征。在 NetworkX 中,我们可以根据各种标准(例如特定节点、边或属性)轻松提取子图。
提取子图
可以根据特定节点或特定边提取子图。
import networkx as nx
import matplotlib.pyplot as plt # Load Zachary's Karate Club graph
G_karate = nx.karate_club_graph() plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1) # Visualize the original graph
nx.draw(G_karate, with_labels=True, node_color='lightblue', node_size=500, font_weight='bold') plt.title("Original Zachary's Karate Club Graph")
输出:
按节点提取:
您可以通过指定要包含的节点列表来创建子图。当您想要研究较大图形中一组特定节点之间的连接和关系时,这尤其有用。
例: 如何根据特定节点从图中提取子图:
# Specify a list of nodes for the subgraph
subgraph_nodes = [0, 1, 2, 3, 4, 5] """Extract the subgraph from Zachary's Karate
Club graph based on the specified nodes"""
subgraph = G_karate.subgraph(subgraph_nodes) # Visualize the subgraph
nx.draw(subgraph, with_labels=True, node_color='lightgreen', node_size=500, font_weight='bold') plt.title("Subgraph of Zachary's Karate Club based on nodes")
plt.show()
输出:
我们可以通过以下命令打印子图的节点和边:
# Print nodes and edges of the subgraph
print("Nodes in subgraph:", subgraph.nodes())
print("Edges in subgraph:", subgraph.edges())Nodes in subgraph: [0, 1, 2, 3, 4, 5]
Edges in subgraph: [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (1, 2), (1, 3), (2, 3)]
按边提取:
或者,您可以使用一组特定的边提取子图。此方法侧重于节点之间的关系,当边表示重要的交互或连接时,此方法可能很有用。
示例:这是一个示例代码,演示了如何根据特定边缘在 NetworkX 中提取子图:
# Specify a list of edges for the subgraph
subgraph_edges = [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (1, 2), (1, 3), (2, 3)] """Extract the subgraph from Zachary's Karate
Club graph based on the specified edges"""
subgraph_edges_extracted = G_karate.edge_subgraph(subgraph_edges) # Visualize the subgraph
plt.subplot(1, 2, 2)
nx.draw(subgraph_edges_extracted, with_labels=True, node_color='orange', node_size=500, font_weight='bold') plt.title("Subgraph of Zachary's Karate Club based on edges")
plt.tight_layout()
plt.show()
输出:
五、结论
在本教程中,我们探索了使用 NetworkX 的社区检测和子图。我们讨论了如何使用 Girvan-Newman 算法和 Louvain 方法识别社区,以及如何提取和可视化具有节点或边的子图。我们将在复杂网络系列:第 6 部分中深入探讨 PageRank 和 HITS 算法等高级主题,该主题衡量了网络中节点的重要性,并研究了与网络健壮性和弹性相关的概念。这些技术共同为分析和理解复杂网络提供了强大的工具。