Scala 如何将包含(vertexId、edgeId)的贴图转换为GraphX RDD

Scala 如何将包含(vertexId、edgeId)的贴图转换为GraphX RDD,scala,apache-spark,rdd,Scala,Apache Spark,Rdd,从文件解析图形后,我得到一个贴图,其中键表示顶点(id),值表示边(id)。为了创建边(Vx->Vy),我们需要使用值(边id)连接地图条目。目标是从该表示创建GraphX图形 以下是我到目前为止的情况: tempLHM.foreach(x=>println(x)) (A.L0,A) (B.L0,B) (C.L0,C) (D.L0,D) (E.L0,E) (a.L0M1,A) (b.L0M1,B) (c.L0M1,n4) (a.L0M2,n4) (b.L0M2,D) (c.L0M2,n5

从文件解析图形后,我得到一个贴图,其中键表示顶点(id),值表示边(id)。为了创建边
(Vx->Vy)
,我们需要使用值(边id)连接地图条目。目标是从该表示创建GraphX图形

以下是我到目前为止的情况:

tempLHM.foreach(x=>println(x))

(A.L0,A)
(B.L0,B)
(C.L0,C)
(D.L0,D)
(E.L0,E)
(a.L0M1,A)
(b.L0M1,B)
(c.L0M1,n4)
(a.L0M2,n4)
(b.L0M2,D)
(c.L0M2,n5)
(a.L0M3,n5)
(b.L0M3,C)
(c.L0M3,E)
有没有一种直接的方法将这个hashmap映射到顶点和边RDD

tempLHM是一个可变的
LinkedHashMap[String,String]
。在上面的hashmap中,在元素(A.L0,A)和(A.L0M1,A)中,A.L0和A.L0M1是由公共值A(边)连接的关键点(顶点)

这就是我想要得到的

val vertex:RDD(vertexId, VertexName)  i.e ((A.L0).Long, A.L0), ((a.L0M1).Long, a.L0M1) etc

val edge:RDD((vertexId1, vertexId2), EdgeName) i.e ((A.L0).Long, (a.L0M1).Long), A)

假设您的数据具有这种结构

val d = Map("v1" -> "e1", "v2" -> "e1", "v3" -> "e2", "v4" -> "e2")
此处有两条边(“v1”、“v2”)和(“v3”、“v4”)

假设您有一个简单的图(而不是一条边可以连接多个节点的超图)。因此,此解决方案的假设是一条边仅连接两个节点,并且边只出现一次

import collection.mutable.{ HashMap, MultiMap, Set }
import java.security.MessageDigest
import org.apache.spark.graphx.Edge
import org.apache.spark.graphx.Graph

// a hacky way to go from string to Long since GraphX need Longs to
// represent vertex IDs. You might want to do something different 
// here to make sure that your IDs are unique.
def str2Long(s: String) = s.##.toLong

val d = Map("v1" -> "e1", "v2" -> "e1", "v3" -> "e2", "v4" -> "e2")

// We use a multi-map to create an inverse map (Edge->Set(Vertices))
val mm = new HashMap[String, Set[String]] with MultiMap[String, String]
d.foreach{ x => mm.addBinding(x._2,x._1) }

val edges = mm.map{ case(k,v) => Edge[String](str2Long(v.head),str2Long(v.last), k) }.toList
val vertices = d.keys.map(x => (str2Long(x), x)).toList

val edgeRdd = sc.parallelize(edges)
val vertexRdd = sc.parallelize(vertices)

val g = Graph(vertexRdd, edgeRdd)
如果打印得到的边和顶点:

g.vertices.foreach(println)
g.edges.foreach(println)


(3709,v3)
(3707,v1)
(3708,v2)
(3710,v4)
Edge(3709,3710,e2)
Edge(3707,3708,e1)
注意:此处的解决方案仅适用于适合单个节点内存的数据。从您的问题中,我看到您将数据加载到本地地图中,因此以下解决方案适用于您。如果您想在具有多个节点的大型数据集上运行此功能,则上述解决方案将不起作用


更新的解决方案 此解决方案比上述解决方案更具可扩展性。它确保您始终停留在RDD域中,而不需要在驱动程序中收集图形(例如,上面我们在scala映射中加载了所有原始数据,我们将在这里避免)。它还涵盖了不同节点之间具有公共边ID的情况(以类似于超图的方式)

假设文本文件具有以下格式:

v1,e1 
v2,e1 
v3,e2
v4,e2
在下面的代码中,我们首先读取原始数据,然后将它们转换为适当的顶点和边RDD

import org.apache.spark.graphx.Edge
import org.apache.spark.graphx.Graph

def str2Long(s: String) = s.##.toLong

val rawData: RDD[String] = sc.textFile("...")

val toBeJoined: RDD[(String, String)] 
  = rawData.map(_.split(",")).map{ case Array(x,y) => (y,x) }
请注意,我们的结果图是双向的:如果我们有边
(v1,v2)
,我们也有边
(v2,v1)


你能告诉我你所拥有和想要的全部类型吗?嗨@marios:谢谢你的代码。它确实有效,我开始更好地理解它。关于假设,a)该数据是一个超图,在某些情况下,多个节点可以连接到一条边。b) 数据最终将无法放入单个节点的内存中。我之所以使用HashMap,是因为我目前缺乏更好的解决方案/编码技能。请告诉我如何解决上述两个假设。我刚刚添加了一个更新,说明如何通过不加载scala集合(如哈希图)中的所有内容来实现此可伸缩性。为了练习,我编写了解决方案,但是如果我必须在真实环境中处理这个问题,我会尝试以“v1,v2”格式获取原始图形数据,而不是这种“v1,e1\n v2,e1”格式。这使得使用这种格式非常困难,而且你也会失去方向(如果你的图形是有方向的)。
val biDirectionalEdges: RDD[(String, (String, String))] 
  = toBeJoined.join(toBeJoined).filter{ case(e,(v1,v2)) => v1 != v2 }

val edgeRdd = 
  biDirectionalEdges.map{ case(e,v) => Edge[String](str2Long(v._1),str2Long(v._2), e) }
val vertexRdd = 
  toBeJoined.map(_._1).distinct.map(x => (str2Long(x), x))

val g = Graph(vertexRdd, edgeRdd)

// Verify that this is the right graph
g.vertices.take(10).foreach(println)
g.edges.take(10).foreach(println)