Apache spark 查找特定节点的连接组件而不是整个图(GraphFrame/GraphX)
我已经在Spark中创建了一个GraphFrame,当前的图形如下所示: 基本上,会有很多这样的子图,其中每个子图都会彼此断开连接。给定一个特定的节点ID,我想找到子图中的所有其他节点。例如,如果节点ID为1,那么图将遍历并返回2,10,20,3,30 我已经创建了一个主题,但它没有给出正确的结果Apache spark 查找特定节点的连接组件而不是整个图(GraphFrame/GraphX),apache-spark,spark-dataframe,spark-graphx,graphframes,Apache Spark,Spark Dataframe,Spark Graphx,Graphframes,我已经在Spark中创建了一个GraphFrame,当前的图形如下所示: 基本上,会有很多这样的子图,其中每个子图都会彼此断开连接。给定一个特定的节点ID,我想找到子图中的所有其他节点。例如,如果节点ID为1,那么图将遍历并返回2,10,20,3,30 我已经创建了一个主题,但它没有给出正确的结果 testgraph.find("(a)-[]->(b); (c)-[]->(b)").filter("(a.id = '1')").show() 遗憾的是,连通分量函数考虑整个图。是否
testgraph.find("(a)-[]->(b); (c)-[]->(b)").filter("(a.id = '1')").show()
遗憾的是,连通分量函数考虑整个图。是否可以使用GraphFrame/GraphX获取给定特定节点ID的断开子图中的所有节点?获取与特定顶点相关的连接组件可以使用BFS遍历来完成,该遍历从该顶点开始,并在几个跃点上收集其所有邻居。 这可以简单地通过GraphX提供的PregelAPI来实现,我们应该在其中实现vertexProgram、sendMessage和mergeMessages函数。算法在收到初始消息时触发。该中心向其邻居发送一条消息,该消息将传播到其邻居,以此类推,直到覆盖连接的组件。每个接收到消息的顶点都会被检查,这样它就不会在接下来的迭代中被激活 以下是该方法的实施:
import org.apache.spark.graphx._
import org.apache.spark.{SparkConf, SparkContext}
object ConnectedComponent extends Serializable {
def main(args = Array[String]) = {
val conf = new SparkConf().setAppName("ConnectedComponent").setMaster("local")
val sc = new SparkContext(conf)
val vRDD = sc.objectFile[(VertexId,Int)]("/path/to/vertex/rdd/file/")
val eRDD = sc.objectFile[Edge[Int]]("/path/to/edge/rdd/file/")
val graph = Graph(vRDD, eRDD)
val centerOfCC = graph.pickRandomVertex()
var cc = extractCC(graph, center)
cc.vertices.collect.foreach(println)
sc.stop()
}
def extractCC(g: Graph[Int, Int], center: VertexId): Graph[Int, Int] = {
/* Return a subgraph of the input graph containing 'center' with the connected component
*/
val initialGraph = g.mapVertices((id, attr) => VertexData(attr, false, false, center))
val connectedComponent = initialGraph.pregel(initialMsg = 0)(vprog, sendMsg, mergeMsgs)
.subgraph(vpred = (id, attr) => attr.checked == true)
.mapVertices((id, vdata) => vdata.attr)
connectedComponent
}
case class VertexData( var attr : Int, // label of the vertex
var checked : Boolean, // check visited vertices
var propagate : Boolean, // allow forwarding msgs or not
var center: VertexId) // ID of the connectedComponent center
def vprog(id:VertexId, vdata: VertexData, msg: Int): VertexData = {
val attr : Int = vdata.attr
var checked : Boolean = vdata.checked
var propagate : Boolean = vdata.propagate
val center : VertexId = vdata.center
if (checked==false && msg == 0 && id==center) {
propagate = true
checked = true
}
else if(checked==false && msg == 1) {
propagate = true
checked = true
}
else if(checked == true && msg == 1){
propagate = false
}
new VertexData(attr, checked, propagate, center)
}
def sendMsg(triplet: EdgeTriplet[VertexData, Int]):Iterator[(VertexId, Int)] = {
var it : Iterator[(VertexId, Int)] = Iterator()
if(triplet.dstAttr.propagate==true)
it = it ++ Iterator((triplet.srcId, 1))
if(triplet.srcAttr.propagate==true)
it = it ++ Iterator((triplet.dstId, 1))
it
}
def mergeMsgs(a: Int, b: Int): Int = math.max(a, b)
}
但是处理整个图形有什么问题?通过
connectedComponents
算法的工作方式,可以很容易地在原始图中找到断开连接的子图。基本上,同一子图中的所有对象都将其顶点.attr
设置为子图中最小的顶点ID
。在这一点上,查找到给定节点的所有连接组件与查找结果图中具有相同的顶点.attr
值的连接组件
中的所有顶点一样容易。例如,在上图中,在运行连接组件
后,左侧子图中的每个顶点都将其attr
设置为1L
。右侧子图中的每个顶点都将其attr
设置为4L
。然后,您可以使用简单的RDD
操作过滤出正确子图中的节点。然而,当您有十亿个节点并且只需要找到三个或四个这样的子图时,运行connectedComponents
非常慢。在这种情况下,计算connectedComponents
的成本非常高。通常不会比connectedComponents
便宜。尽管如此,这对我来说很好。你确定你没有弄乱过滤器表达式中的类型吗?嗨@zero323你能解释一下为什么你认为它不会更便宜吗?我的理解是GraphFrame是基于DataFrame的。当我想基于一个特定的节点ID进行遍历时,它将首先从节点DataFrame中搜索它,然后遍历以找到与它关联的其他节点。与普通的图搜索不同,where可能需要随机开始并继续遍历,直到找到节点ID,然后遍历以获取与之关联的其他节点。