在spark中组合/加入ID行

时间:2016-11-17 23:33:26

标签: scala apache-spark functional-programming

给定一个充满ID行的文件,例如

i1, i2, i5
i3, i4
i2, i6, i7
i4, i8
i9, i3

如何通过链接相同的ID加入他们?因此,对于上面的示例,行1通过i2链接到行3,行2分别通过i4和i3链接到行4和5。这将为您提供以下(重复删除)

i1, i2, i5, i6, i7
i3, i4, i8, i9

我可以通过遍历行来完成它,但是想知道你将如何以功能性的方式实现它?

4 个答案:

答案 0 :(得分:1)

当您使用Apache Spark时,您可以使用内置的GraphX组件为您完成工作。

import org.apache.spark.graphx._

def cross[Y](xs: Traversable[Y], ys: Traversable[Y]) = for { x <- xs; y <- ys } yield (x, y)

val data = sc.parallelize(List(
    "1\t5\t3", 
    "3\t9\t30",
    "7\t10\t12",
    "10\t7\t13"
))

val prep = data.map(x => x.split("\t").map(_.toLong).toList)

val vertex = prep
  .flatMap(x => x)
  .map(x => x -> s"ID=$x")

val edges = prep
  .map(x => cross(x, x))
  .flatMap(x => x)
  .map(x => new Edge(x._1, x._2, "likes"))

val graph = Graph(vertex, edges)
val linked = graph
  .connectedComponents
  .vertices
  .map(_.swap)
  .groupByKey

linked.take(10).foreach(println)

将打印出以下结果:

(1,CompactBuffer(30, 3, 9, 1, 5))
(7,CompactBuffer(7, 10, 12, 13))

Cross只是创建两个列表的叉积,因此我们可以在所有顶点之间创建边。

connectedComponents函数将遍历图形并找到共享边缘的所有顶点并创建一个新图形,其中每个顶点是顶点Id的元组 - &gt; “主要”顶点ID。

所以:

graph.connectedComponents.vertices.take(10).foreach(println)

会打印出来

(30,1)
(1,1)
(3,1)
(5,1)
(7,7)
(9,1)
(10,7)
(12,7)
(13,7)

如您所见,已选择1和7作为“主要顶点”并链接到第一个图形中的所有连接的顶点。因此,简单的交换和组将所有连接的ID组合在一起。

答案 1 :(得分:1)

适用于Spark 2.0 +的代码

val spark: SparkSession = SparkSession.builder.master("local").getOrCreate;
val df = spark.sparkContext.parallelize(
  List(
    "i1, i2, i5",
    "i3, i4",
    "i2, i6, i7",
    "i4, i8")
)

//Group lines with tokens count (determing by the last occurence of comma)
val rddGroupByTokensCount = df.map(row => (row.lastIndexOf(','), row.split(", ")))
  .groupBy(_._1)

//Now gather all the token to single place with flatMap and drop duplicates
val rddUniqueTokens = rddGroupByTokensCount.map(_._2.flatMap(_._2).toSet)

//print grouped unique tokens by the count in each line
rddUniqueTokens.collect().map(println)

<强>输出:

Set(i5, i1, i7, i2, i6)
Set(i3, i4, i8)

答案 2 :(得分:0)

我们可以使用O(n * n)的解决方案,而不是使用循环遍历所有行的O(n * k)解决方案,其中 k 是您的ID数量。像这样:

val input = ...//I will assume your input is an RDD[List]

val idArray = Array(id1, id2, id3, id4, id5, id6, id6)//Array containing all IDs
val result = sc.parallelize(idArray, k).map(x => (x, x))  
input = input.map(x => (x(0), if(x.length > 0) x.slice(1, x.length) else null))

//If you can afford to persist it would help greatly:
result.persist
input.persist

//We can make this loop smaller if k is large and your lists are small
//by setting the upper bound of the range to the length of the longest list.
//I'll leave this decision up to you.
for (i <- 0 to k){ 
    result = result.cogroup(input)
    input = input.map((t: (x, y)) => (y(0), if(y.length > 0) y.slice(1, y.length) else null))
}
result.map((t: (x, y)) => y.distinct)//we want distinct lists in output

result.unpersist
input.unpersist

答案 3 :(得分:0)

所以这可能是次优的,但我认为无论如何都值得发帖。它假定您的输入文件足够小,可以持久存储到内存中(因为这是所有的香草Scala)。

我决定通过将给定的id视为图形中的邻接,然后使用BFS列出所有连接的组件来解决这个问题。

List(i7, i1, i6, i2, i5)
List(i8, i4, i3, i9)

其中给出了以下输出:

{{1}}