GraphX

时间:2018-05-03 09:41:14

标签: apache-spark spark-graphx

我想在Graph的节点之间做一个笛卡尔积。我想建立他们的距离矩阵。 也许这不是一个很好的方法,所以,任何建议都是受欢迎的。

这是我的代码,它不起作用,我没有任何警告或例外,它只是不起作用。我想也许是因为我试图使用相同的 RDD制作笛卡尔产品,但我不知道如何修复它,如何制作嵌套循环或者可以帮助我的东西计算这个矩阵。

val indexes1 = graph.vertices.map(_._1)
val indexes2 = graph.vertices.map(_._1)

val cartesian = indexes1.cartesian(indexes2).cache()
cartesian.map(pair => matrix.updated(pair._1, shortPathBetween(pair._1, pair._2)))

def shortPathBetween(v1:VertexId, v2:VertexId) : Int = {
    val path = ShortestPaths.run(graph, Seq(v2))
    val shortestPath = path.vertices.filter({case (vId, _ ) => vId == v1})
        .first()
        ._2
        .get(v2)

    shortestPath.getOrElse(-1)
}

1 个答案:

答案 0 :(得分:0)

我接近这个的方式是使用pregel API。 这允许从每个节点并行遍历图形。 如果你跟踪距离并在用边缘权重遍历时更新它们,你最终得到的顶点与每个(可到达的)其他顶点之间的距离。

例如,如果您采用此有向图:

directed graph

你可以在Spark GraphX中初始化这个:

val graphData = List(
    (0, 0, 1, 10.0),
    (1, 0, 2, 5.0),
    (2, 1, 2, 2.0),
    (3, 1, 3, 1.0),
    (4, 2, 1, 3.0),
    (5, 2, 3, 9.0),
    (6, 2, 4, 2.0),
    (7, 3, 4, 4.0),
    (8, 4, 0, 7.0),
    (9, 4, 3, 5.0)
  ).toDF("id", "from", "to", "distance")

  val vertexRDD: RDD[(Long, Int)] = graphData.flatMap(_.getValuesMap[Int](List("to", "from")).values).distinct().map(i => (i.toLong, i)).rdd
  val edgeRDD: RDD[Edge[Double]] = graphData.map(x => Edge(x.getInt(1), x.getInt(2), x.getDouble(3))).rdd
  val graph: Graph[Int, Double] = Graph(vertexRDD, edgeRDD)

pregel调用需要3个函数

  • vprog使用消息初始化每个顶点(在这种情况下为空Map [VertexId,Double]以跟踪距离)
  • sendMsg在每次迭代时应用的更新步骤(在这种情况下,通过添加边缘的权重来更新距离并返回带有消息的Iterator以发送到下一次迭代
  • mergeMsg合并两条消息(2 Map [VertexId,Double] s为1,保持最短距离)

在代码中,这可能看起来像:

def vprog(id: VertexId, orig: Map[VertexId, Double], newly: Map[VertexId, Double]): Map[VertexId, Double] = newly

def mergeMsg(a: Map[VertexId, Double], b: Map[VertexId, Double]): Map[VertexId, Double] = (a.toList ++ b.toList).groupBy(_._1).map{ // mapValues is not serializable :-(
    case (id, v) => id -> v.map(_._2).min // keep shortest distance in case of duplicate
}

def sendMsg(trip: EdgeTriplet[Map[VertexId, Double], Double]): Iterator[(VertexId, Map[VertexId, Double])] = {
    val w = trip.attr // weight of edge from src -> dst
    val distances = trip.dstAttr.mapValues(_ + w) + // update collected distances at dst + edge weight
      (trip.srcId -> 0.0, trip.dstId -> w) // set distance to src to 0  and to dst the edge weight

    // If src contains as much nodes as dst (we traversed all)
    if(trip.srcAttr.keySet.intersect(distances.keySet).size != distances.keySet.size)
      Iterator((trip.srcId, distances))
    else
      Iterator.empty
}

然后运行pregel,收集顶点并旋转地图以获得距离矩阵。

val initMap = Map.empty[VertexId, Double]

val result = graph
    .mapVertices((_,_) => initMap)
    .pregel(
      initialMsg = initMap,
      activeDirection = EdgeDirection.Out
    )(vprog, sendMsg, mergeMsg)
    .vertices
    .toDF("id","map")
    .select('id, explode('map))
    .groupBy("id")
    .pivot("key")
    .agg(min("value"))
    .orderBy("id")
    .show(false)

结果看起来像

+---+----+----+----+----+---+
|id |0   |1   |2   |3   |4  |
+---+----+----+----+----+---+
|0  |0.0 |8.0 |5.0 |11.0|7.0|
|1  |11.0|0.0 |2.0 |1.0 |4.0|
|2  |9.0 |3.0 |0.0 |4.0 |2.0|
|3  |11.0|21.0|16.0|0.0 |4.0|
|4  |7.0 |15.0|12.0|5.0 |0.0|
+---+----+----+----+----+---+

也许有其他/更好的方法,但这似乎在计算上不如计算作为笛卡尔积的节点之间的最短路径; - )