在一个网络中,不同的节点起着大小不同的作用。以社交网络为例,有意见领袖的大V,有死寂沉沉的僵尸粉;以交通网络为例,有至关重要的交通枢纽,有无关痛痒的备用中转站。在使用复杂网络分析业务问题时,如何区分网络中不同节点的重要性程度,就是一个需要考虑的问题。为了解决我们自己的业务问题,顺便了解了一下相关的方法,特记录一下,若有益于相关领域的同学,则幸甚。
一、要实现的目标
对网络中的节点定义一个指标,可用来定量地衡量每个节点在网络中重要性的大小。
二、相关的一些方法总结
分析节点的重要性,业界较多地从网络拓扑结构出发,也有考虑传播动力学视角的【1】。菜鸟新上路,咱们先了解常用的一些方法即可。
- 局部特征
节点度 这恐怕是常简单也最多使用的一个指标了。表示与节点所直接连接的边的数量,对于有向图,又分入度与出度。对于节点i, 对于图G中的节点j, 当i与j存在边相连时, a(i,j)=1,否则a(i,j)=0. 那么,节点i的度可以如下计算:
这个定义既直观又实用。但是也确实存在一定不足。比如下图中节点2与节点4的度都是3,它们的重用性真是相同的吗? 如果我们从网络连接密切程度来看,2的邻居节点1,3,6之间存在较多的边,它们共同形成了一个较紧密的小圈子。而4的邻居3,5,8之间并无边相连。那么从连接紧密性的角度,我们倾向于认为2更重要。但是,可以发现4是通往7,8,9,10的一个必经之地,如果破坏了4,那么这个图就不连通了。这时我们会倾向于认为4更重要。所以,如果能在考虑节点度时,同时引入一些其它信息,则更加妥当。
在Spark的Graphx中,建立Graph对象后,直接调用 .degrees 即可得到节点度的值。
集聚系数 上面我们有涉及到紧密性的一个说法,那么,在图论中,有一个指标可以衡量这种属性,就是集聚系数(clustering coefficient)。有局部集聚系数,也有全局集聚系数。对于节点i, 它的邻居节点N(i)之间所存在的边数,与可能存在的最大边数的比值,就叫节点i的局部集聚系数。网络的全局集聚系数就是所有节点的集聚系数求平均值。
不要小看这个指标,它在评估一个网络是否可以称为小世界网络时,具有重要意义。
在Graphx中,可以直接用 .triangleCount 来获得邻居节点之间实际存在的边数,再除以上式分母上的值,即可。
文章【2】【3】就是在度的基础上,增加了这个系数的因素来作为衡量节点重要性的评价方法。文章【2】的方法:
其中f是节点与其邻居度数之和,g是一个标准化后的指标,具体参考该文即可。俺觉得这个真心有点复杂啊,文章也没有讲清楚为啥一定要这样搞。相比之下,文章【3】就轻便多了,直接把度数与集聚系数作了一下加权,如下:
为了简便,我们这边目前采用了类似于文章【3】的方法。后面有精力再尝试更复杂的方法。
- 全局特征
可以看出来,上面讨论的方法仅考虑节点i及其邻居的信息,属于局部性质的指标。 我们再看下有哪些全局特征可以用。但要注意的是,全局指标往往涉及到网络整体的计算,对于我厂这样大规模的网络数据,计算复杂度不得不考虑。
特征向量法 上面讨论节点度时,默认所有邻居具有同等重要性,但现实中,如果总办领导们是你有且仅有的朋友,而我即便认识鹅厂其余人当中的1W人,恐怕你也比我更重要。即是说,邻居节点本身的重要性会影响所评价节点的重要性。其实,这个也是我们自己的业务场景中期望能考虑进来的一个因素。该指标计算公式如下(相当于对邻居节点的重要性作了线性加权,而加权权重是图G的邻接矩阵的特征向量):
中心度 一个点如果到其它所有点的最短路径越小,则该点就越居于网络的中心位置。计算方法如下所示,其中di,j 表示i到j的最短路径长度。把分子N-1放到分母上,就相当于是所有最短路径的平均值。这个定义还是蛮直观的吧,但感觉计算量肯定不小啊。
点介数 我们这样想吧,如果一个网络中80%的最短路径都经过点i, 那i 是不是很重要?这种点就是重要“交通枢纽”一样的角色。其计算方法如下,分子是所有经过点i的最短路径数,分母是所有最短路径数:
此处我想用“等等等等”来表达诸如以上的指标真心太多了。大家使用时要结合自己的业务场景进行借鉴。
三、我们的应用
目前,俺们也是新入门去应用这个东东。使用的是比较简单的,度数与集聚系数加权的方法。我们在Spark中自定义了一个函数,代码如下:
def calNodeImportanceIndex(graph :Graph[String,Int], revFlag :Boolean = true) = {
//revFlag 用来控制集聚系数是正向作用还是反向作用
val nodeDegree = graph.degrees
val nodeClusterEdge = graph.triangleCount.cache()
val nodeInfo = nodeClusterEdge.vertices.leftOuterJoin(nodeDegree).map{case (id,(clusterEdgesNum, degree)) =>
val total :Double = degree.get*(degree.get-1)/2
val clusterCoeff :Double = if(total == 0) 0 else clusterEdgesNum.toDouble/total
val revClusterCoeff = if(clusterCoeff == 0.0) 0.0 else 1.toDouble/clusterCoeff
if (revFlag) (id,revClusterCoeff,degree) else (id,clusterCoeff,degree)
}.cache()
val maxRevClusterCoeff = nodeInfo.map(x => x._2).max
val minRevClusterCoeff = nodeInfo.map(x => x._2).min
val maxDegree = nodeInfo.map(x => x._3.get).max
val minDegree = nodeInfo.map(x => x._3.get).min
nodeInfo.map{case (id,clusterCoeff,degree) =>
val stdRevClusterCoeff :Double= (clusterCoeff-minRevClusterCoeff)/(maxRevClusterCoeff-minRevClusterCoeff)
val stdDegree :Double = (degree.get - minDegree).toDouble/(maxDegree - minDegree).toDouble
val finalIndex = 0.2*stdRevClusterCoeff + 0.8*stdDegree
(id,finalIndex)
}
}
参考文献
【1】Borge-Holthoefer J, Moreno Y 2012 Phys. Rev. E 85 026116
【2】基于度与集聚系数的网络节点重要性度量方法研究 任卓明
【3】网络节点重要度评价方法研究 叶春森