我在RDD集合中有一个数字列表。从这个列表中我需要创建另一个RDD列表,其中每个元素等于它前面所有元素的总和。如何在Spark中构建这样的RDD?
以下Scala代码说明了我想在Spark中实现的目标:
object Test {
def main(args: Array[String]) {
val lst: List[Float] = List(1, 2, 3)
val result = sum(List(), 0, lst)
println(result)
}
def sum(acc: List[Float], runningSum: Float, list: List[Float]): List[Float] = {
list match {
case List() => acc.reverse
case List(x, _*) => {
val newSum = runningSum + x
sum(newSum :: acc, newSum, list.tail)
}
}
}
运行此结果:
List(1.0, 3.0, 6.0)
此示例的等效Spark代码是什么?
答案 0 :(得分:0)
所以这在Spark中有点棘手,但有一个选择是执行mapPartitionsWithIndex并计算每个分区的总和,然后将结果收集回驱动程序。然后,您可以使用它并执行另一个mapPartitionsWithIndex,并使用先前分区的总和作为起点计算总和,然后继续从此点开始添加元素。这有点慢,但我们不需要依赖于收集或将所有元素带回驱动程序。
e.g。
val c = rdd.mapPartitionsWithIndex((f, i) => List((f, i.sum)).iterator).collect()
val sumRdd = rdd.mapPartitionsWithIndex{(f, i) =>
val previousElem = c.filter(_._1 < f).map(_._2).sum
var e = previousElem
i.map{ ne =>
val ret = e
e = e + ne
ret
}}
答案 1 :(得分:0)
正如其他人所提到的那样,你的问题是RDD没有订购的概念,所以你必须提供你的定义&#34;所有在它之前的元素&#34;作为输入的一部分,例如,作为行:
1 1.0
2 3.0
3 7.0
我当然假设您无法在驱动程序的内存中加载列表。鉴于你的问题类型,我担心没有简单的解决办法,但我会尽力勾勒出一个问题。
您的问题基本上需要顺序读取,但您可以在较小的块中执行。例如,您可以通过调用map(x => (x._1%10000, x._1, x._2))
在10000个项目的块上对列表进行分区。您可以根据内存限制调整的实际数字。
然后,您可以使用groupByKey()
方法将给定子列表中的所有项目放在一起。这会将整个子列表放在内存中给定执行程序上。
然后,您可以像在示例中使用sum()
方法一样,对结果子列表进行排序和计算。这会给你一个带有桶顺序,总和和列表的元组:
(1,3270,List(17, 12, 15)
这个RDD你可以映射到另外两个,首先,映射到只(1,3271)以获得子列表的总和。其次,映射到(1,17),(1,12)...元组,你可以坚持。
您可以收集的第一张地图(它非常小,每10000个值只有1个元组)。一旦在驱动程序上,排序并求和以获得所有先前子列表的总和。把它作为一个新的RDD放回去,然后你可以用上面的第二个RDD做join
。然后,您可以将桶的总和(全局)添加到子列表(本地)中的总和,然后将所有结果存储回磁盘。
总的来说,我所描述的是一种分而治之的方法。基本上将列表放在列表列表中,其中每个较小的列表都适合内存。