2021SC@SDUSC
目录
2021SC@SDUSC
聚合操作
1.聚合消息
2.计算度数
聚合操作
在很多Graph的分析任务中,聚合兄弟顶点的信息是关键步骤。例如,想要知道每个 用户的粉丝数以及这些粉丝的平均年龄,这就会用到聚合操作。很多迭代图的算法(例如, PageRank、Shortest Path > Connected component)都会多次聚合相邻顶点的属性。
1.聚合消息
GraphX的核心聚合操作是aggregateMessages,这个操作应用用户定义的sendMsg函数到 Graph的每个EdgeTriplet上,然后使用mergeMsg函数在目的顶点上聚合。aggregateMessages 的接口定义见下列代码清单。
class Graph[VDr ED] {
def aggregateMessages[Msg: ClassTag]( sendMsg: EdgeContext[VD, ED, Msg] => Unit,
mergeMsg: (Msg, Msg) => Msg, tripletFields: TripletFields = TripletFields.All)
:VertexRDD[Msg]
注 GrapliX的早期版本提供了 mapReduceTriplets来处理聚合操作。
aggregateMessages相 比于mapReduceTriplets有很大的性能提升,
所以Spark官网建议将mapReduceTriplets 迁移到 aggregateMessageso
现在我们来计算每个用户的粉丝的平均年龄,见下列代码清单。
import org.apache.spark.graphx.util.GraphGenerators
//创建一个graph,使用age作为顶点属性。为简单起见,使用随机的graph
val graph: Graph[Double, Int]=
GraphGenerators.logNormalGraph(sc, numVertices = 100).mapVertices( (id, _)=> id.toDouble )
//计算年龄比用户自己大的粉丝的数量和所有粉丝的年龄总和
val olderFollowers: VertexRDD[(Int, Double)] = graph.aggregateMessages[(Int, Double)]( triplet =〉{ // Map函数
if (triplet.srcAttr > triplet.dstAttr)(
//每个用户计数为1,将计数与年龄一起发送给目的顶点 triplet.sendToDst(1, triplet.srcAttr)
}
},
//累加计数与年龄
(a, b) => (a._1 + b._l, a._2 + b._2) // Reduce函数
)
//每个顶点收到的消息,用粉丝总年龄除以粉丝总数.得到粉丝平均年龄
val avgAgeOfOlderFollowers: VertexRDD[Double]=
olderFollowers.mapValues( (id, value) => value match { case (count, totalAge)
=> totalAge / count })
//显示计算结果
avgAgeOfOlderFollowers.collect.foreach(printin())
2.计算度数
一种常见的聚合任务是计算每个顶点的度,即每个顶点相邻的边数。人们常常需要知道 每个顶点的出度、入度以及度数。GraphOps中包含了计算每个顶点的度数的操作集合。以计 算图中所有顶点的最大出度、最大入度以及最大度数为例,见下列代码清单。
//首先定义一个reduce函数用于计算最大度数
def max(a: (Vertexld, Int), b: (Vertexld, Int)): (Vertexld, Int) = {
if (a._2 > b._2) a else b
}
//计算每种度数的最大值的顶点
val maxInDegree: (Vertexld, Int) - graph.inDegrees»reduce(max)
val maxOutDegree: (Vertexld, Int) = graph.outDegrees.reduce(max)
val maxDegrees: (Vertexld, Int) = graph.degrees.reduce(max)
- 收集相邻顶点
在有些情况下需要收集每个顶点的相邻顶点以及相邻顶点的属性,可以使用collectNeigh- borlds 和 collectNeighbors,见下列代码清单。
class GraphOps[VD, ED] {
def collectNeighborIds(edgeDirection: EdgeDirection): VertexRDD[Array
[Vertexld]]
def collectNeighbors(edgeDirection: EdgeDirection): VertexRDD[ Array
[(Vertexld, VD)]]
}
由于这些聚合操作都通过复制信息,并且需要大量的通信,所以十分消耗资源。尽可能 将这些操作转换为使用aggregateMessages。