2004年Newman等人提出了模块度Q,之后广泛应用于衡量社区的划分质量,下面公式适用于无向无权的同质网络。若社区内节点的边数越多,Q值越大,相反,Q值越小。
Q = 1 2 m ∑ i , j [ A i j − d i d j 2 m ] δ ( C i , C j ) Q=\frac{1}{2m}\sum_{i,j}\Big[A_{ij}-\frac{d_id_j}{2m}\Big]\delta(C_i,C_j) Q=2m1i,j∑[Aij−2mdidj]δ(Ci,Cj)
m 为 网 络 中 的 边 数 ; A i j 为 邻 接 矩 阵 A 中 的 元 素 , m为网络中的边数;A_{ij}为邻接矩阵A中的元素, m为网络中的边数;Aij为邻接矩阵A中的元素,
若 节 点 i , j 相 连 , 则 A i j = 1 , 否 则 A i j = 0 ; 若节点i,j相连,则A_{ij}=1,否则A_{ij}=0; 若节点i,j相连,则Aij=1,否则Aij=0;
C i 为 节 点 i 所 属 的 社 区 , C j 为 节 点 j 所 属 的 社 区 ; C_i为节点i所属的社区,C_j为节点j所属的社区; Ci为节点i所属的社区,Cj为节点j所属的社区;
当 i , j 处 于 同 一 社 区 时 , δ ( C i , C j ) = 1 , 否 则 为 0 ; 当i,j处于同一社区时,\delta(C_i,C_j)=1,否则为0; 当i,j处于同一社区时,δ(Ci,Cj)=1,否则为0;
d i 代 表 节 点 i 的 度 ; d_i代表节点i的度; di代表节点i的度;
d i d j 2 m 表 示 在 随 机 网 络 中 节 点 i 和 节 点 j 之 间 存 在 边 的 可 能 性 。 \frac{d_id_j}{2m}表示在随机网络中节点i和节点j之间存在边的可能性。 2mdidj表示在随机网络中节点i和节点j之间存在边的可能性。
模块度Q也可表示为
Q = ∑ c = 1 k [ l c m − ( d c 2 m ) 2 ] Q=\sum_{c=1}^{k}\Big[\frac{l_c}{m}-(\frac{d_c}{2m})^2\Big] Q=c=1∑k[mlc−(2mdc)2]
上 式 中 , m 为 网 络 中 的 总 边 数 ; k 为 社 区 个 数 , 上式中,m为网络中的总边数;k为社区个数, 上式中,m为网络中的总边数;k为社区个数,
l c 为 社 区 c 内 部 的 连 接 边 数 , d c 为 社 区 c 内 所 有 节 点 的 度 之 和 。 l_c为社区c内部的连接边数,d_c为社区c内所有节点的度之和。 lc为社区c内部的连接边数,dc为社区c内所有节点的度之和。
其中,Q的取值范围为[-0.5, 1]
。
注:模块度Q适用于已知真实社区或未知真实社区划分结果的评估
NMI(normalized mutual information
)标准化互信息,也称为归一化互信息,是目前广泛使用的一种社区划分评价指标,用于度量算法所得到的社区和真实社区划分之间的相似程度,也用于评估聚类结果的相似程度。
N M I ( R , F ) = − 2 ∑ i = 1 C R ∑ j = 1 C F N i j l o g ( N i j S N i ∗ N ∗ j ) ∑ C R N i ∗ l o g ( N i ∗ / S ) + ∑ C F N ∗ j l o g ( N ∗ j / S ) NMI(R,F)=\frac{-2\sum_{i=1}^{C_R}\sum_{j=1}^{C_F}N_{ij}log(\frac{N_{ij}S}{N_{i*}N_{*j}})}{\sum^{C_R}N_{i*}log(N_{i*}/S)+\sum^{C_F}N_{*j}log(N_{*j}/S)} NMI(R,F)=∑CRNi∗log(Ni∗/S)+∑CFN∗jlog(N∗j/S)−2∑i=1CR∑j=1CFNijlog(Ni∗N∗jNijS)
其 中 , 矩 阵 N 中 的 行 表 示 真 实 的 社 区 , 列 表 示 算 法 得 到 的 社 区 ; 其中,矩阵N中的行表示真实的社区,列表示算法得到的社区; 其中,矩阵N中的行表示真实的社区,列表示算法得到的社区;
矩 阵 中 第 i 行 的 元 素 表 示 为 N i ∗ , 第 j 列 的 元 素 表 示 为 N ∗ j ; 矩阵中第i行的元素表示为N_{i*},第j列的元素表示为N_{*j}; 矩阵中第i行的元素表示为Ni∗,第j列的元素表示为N∗j;
N i j 表 示 真 实 社 区 与 算 法 所 得 到 的 社 区 相 同 的 节 点 个 数 ; S 为 节 点 个 数 ; N_{ij}表示真实社区与算法所得到的社区相同的节点个数;S为节点个数; Nij表示真实社区与算法所得到的社区相同的节点个数;S为节点个数;
C R 表 示 真 实 社 区 个 数 , C F 表 示 算 法 所 得 到 的 的 社 区 个 数 。 C_R表示真实社区个数,C_F表示算法所得到的的社区个数。 CR表示真实社区个数,CF表示算法所得到的的社区个数。
当 算 法 所 得 到 的 的 社 区 划 分 与 网 络 中 的 真 实 社 区 划 分 完 全 一 致 时 , N M I = 1 ; 当算法所得到的的社区划分与网络中的真实社区划分完全一致时,NMI=1; 当算法所得到的的社区划分与网络中的真实社区划分完全一致时,NMI=1;
当 算 法 所 得 到 的 划 分 社 区 与 网 络 中 的 真 实 社 区 划 分 完 全 独 立 时 , N M I = 0 。 当算法所得到的划分社区与网络中的真实社区划分完全独立时,NMI=0。 当算法所得到的划分社区与网络中的真实社区划分完全独立时,NMI=0。
显然,NMI的取值范围为[0,1]
。
注:NMI适用于已知真实社区结构的数据集上的社区划分评估
import networkx as nx
import math
class Evaluate:
"""
社区划分结果评估
"""
def __init__(self, G, cur_community, real_community):
"""
评价指标初始化
:param G: 图
:param cur_community: 当前社区划分结果 {id: nodes}
:param real_community: 真实社区结果{id : nodes}
:return: null
"""
self.G = G
self.cur_community = cur_community
self.real_community = real_community
def Q(self):
"""
计算划分社区的模块度Q(modularity)
未知和已知社区的评估指标
:return: Q_value
"""
nodes_number = self.G.number_of_nodes()
edges_number = self.G.number_of_edges()
Q_value = 0
for key in self.cur_community.keys():
#社区c
c = self.cur_community[key]
#社区内结点度数之和
degrees = 0
#社区内部边的总数
inter_edges = 0
for u in c:
u_neighbors = set(nx.all_neighbors(self.G, u))
degrees += len(u_neighbors)
for nbr in u_neighbors:
if nbr in c:
inter_edges += 1
#因为是无向图,所以边数要除以2
inter_edges /= 2
#cq = inter_edges / edges_number - math.pow(degrees / (2 * edges_number), 2)
cq = inter_edges / edges_number - (degrees / (2 * edges_number)) ** 2
Q_value += cq
return Q_value
def NMI(self):
"""
标准化互信息值( Normalized mutual information)
已知真实社区划分结果的评价指标
行代表真实社区划分(real_community)
列代表当前社区划分结果(cur_community)
:return:NMI_value
"""
nodes_number = self.G.number_of_nodes()
r_id = [key for key in self.real_community]
c_id = [key for key in self.cur_community]
#分子
sum_rc = 0
#分母两项之和
sum_r = sum_c = 0
for i in range(0, len(r_id)):
temp = 0
nodes_i = set(self.real_community[r_id[i]])
for j in range(0, len(c_id)):
nodes_j = set(self.cur_community[c_id[j]])
common = nodes_i & nodes_j
a = len(common) * nodes_number / (len(nodes_i) * len(nodes_j))
if a > 0:
temp += len(common) * math.log10(a)
sum_rc += temp
for i in range(0, len(r_id)):
nodes_i = set(self.real_community[r_id[i]])
sum_r += len(nodes_i) * math.log10(len(nodes_i) / nodes_number)
for j in range(0, len(c_id)):
nodes_j = set(self.cur_community[c_id[j]])
sum_c += len(nodes_j) * math.log10(len(nodes_j) / nodes_number)
sum_rc = sum_rc * (-2)
NMI_value = sum_rc / (sum_r + sum_c)
return NMI_value
[1]Newman M E J,Girvan M. Finding and evaluating community structure in networks.[J]. Physical review. E, Statistical, nonlinear, and soft matter physics,2004,69(2 Pt 2).
[2] 赵卫绩,张凤斌,刘井莲.复杂网络社区发现研究进展[J].计算机科学,2020,47(02):10-20.
本人理解尚浅,若有错误之处欢迎大家指出!