给定一个充满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
我可以通过遍历行来完成它,但是想知道你将如何以功能性的方式实现它?
答案 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}}