参考
《An O(m) Algorithm for Cores Decomposition of Networks》
介绍
为了能够更好的处理大型图数据结构,有时需要将其按照要求进行分解,而k-cores便是其中一种方法。它可以从一个相对较大的图网络得到一个子图,这个子图中的所有节点的度都大于等于k。如下所示:

假设以上是一个由节点集 V V V ,和边集 E E E 组成的图 G = ( V , E ) G=(V,E) G=(V,E) ,外部不同颜色深度的“圈”所包含的子图,代表着计算不同k值时对应得到的子图 G s u b g r a p h , k G_{subgraph,k} Gsubgraph,k 。例如:
(1)当
k
=
0
k=0
k=0 时,图
G
G
G 所有的节点和边都作为子图
G
s
u
b
g
r
a
p
h
,
0
G_{subgraph,0}
Gsubgraph,0 的节点和边。因为所有点的degree(度,包含入度和出度),都大于等于0.
(2)当
k
=
1
k=1
k=1 时,可以看到右下角的那个点被排除在外,剩下部分的节点的degree都大于等于1
(3)当
k
=
2
k=2
k=2 时,计算方法跟前面的类似,但是有一些需要注意的规则。例如上图中绿色箭头所指的节点
A
A
A,它的degree为1,所以它将会排除在
G
s
u
b
g
r
a
p
h
,
2
G_{subgraph,2}
Gsubgraph,2 之外。节点
B
B
B(红色箭头所指)由于
A
A
A 被排除而失去一个degree,此时它的degree等于2,这个2才是作为判断
B
B
B 是否
G
s
u
b
g
r
a
p
h
,
2
G_{subgraph,2}
Gsubgraph,2 中的标准。
(4)当
k
=
3
k=3
k=3 时,同理。
所以,所谓 k k k 可以简单理解为与节点的degree相关的变量。
代码实现
算法的基本流程如下:
1、激活所有的节点。
2、针对所有激活节点相关的三元组(
S
→
T
S\rightarrow T
S→T ),计算
S
S
S 和
T
T
T 的degree,分四种情况考虑。
(1)如果
S
S
S 和
T
T
T 的 degree 都小于 0,将它们设置为deactivated状态。不发送消息。
(2)如果
S
S
S 和
T
T
T 的 degree 都小于
k
k
k ,将它们的degree置为 -1。发送消息。
(3)如果
S
S
S(或
T
T
T)的degree小于
k
k
k ,将它的degree置为-1,而
T
T
T(或
S
S
S)的degree减去1。发送消息。
(4)如果
S
S
S 和
T
T
T 的 degree 都大于 0,将它们设置为deactivated状态。不发送消息。
3、重复 2,直到没有激活节点。
4、使用mask得到子图
这里依然基于GraphX和Pregel来实现该算法。Pregel机制的原理可以参考《【大数据分析】基于Graphx的shortestpath源码解析》来理解。
package com.edata.bigdata.algorithm.networks
import org.apache.spark.graphx.{EdgeTriplet, Graph, Pregel, VertexId}
import scala.reflect.ClassTag
object KCores extends Serializable {
type MsgType = Int
private[this] var _core_num: MsgType = 0
def core_num: MsgType = _core_num
def core_num_=(value: MsgType): Unit = {
_core_num = value
}
private def makeMsg(x: Int) = x
def mergeMsg(msg1: MsgType, msg2: MsgType): MsgType = {
if (msg1 < 0 || msg2 < 0) {
-1
} else {
msg1 + msg2
}
}
def sendMsg(edge: EdgeTriplet[MsgType, _]): Iterator[(VertexId, MsgType)] = {
if (edge.srcAttr < 0 || edge.dstAttr < 0) {
Iterator.empty
} else if (edge.srcAttr < core_num && edge.dstAttr < core_num) {
Iterator((edge.srcId, -1), (edge.dstId, -1))
} else if (edge.srcAttr < core_num) {
Iterator((edge.srcId, -1), (edge.dstId, 1))
} else if (edge.dstAttr < core_num) {
Iterator((edge.dstId, -1), (edge.srcId, 1))
} else {
Iterator.empty
}
}
def vertexProgram(id: VertexId, attr: MsgType, msg: MsgType): MsgType = {
if (msg < 0) {
-1
} else {
math.max(attr - msg, 0)
}
}
def run[VD, ED: ClassTag](graph: Graph[VD, ED],k:Int):Graph[VD,ED] = {
core_num = k
val g = graph.mapVertices {(vid, attr)=>
}
val KCGraph = g.outerJoinVertices(g.degrees)((vid, attr, newData) => newData.getOrElse(0))
val initialMessage = makeMsg(0)
val pregel_graph = Pregel(KCGraph, initialMessage)(vertexProgram, sendMsg, mergeMsg)
val pregel_subgraph = pregel_graph.subgraph(vpred = (vid,v)=>v>0)
graph.mask(pregel_subgraph)
}
}
本文介绍了k-cores分解方法,用于处理大型图数据结构,通过选取度大于等于k的节点来构建子图。详细阐述了k-cores的概念,并提供了基于Scala和GraphX的Pregel算法实现过程。
1025

被折叠的 条评论
为什么被折叠?



