如何构建一个RDD,其中每个元素等于前面输入元素的总和?

时间:2015-01-27 11:29:31

标签: scala apache-spark

我在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代码是什么?

2 个答案:

答案 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。然后,您可以将桶的总和(全局)添加到子列表(本地)中的总和,然后将所有结果存储回磁盘。

总的来说,我所描述的是一种分而治之的方法。基本上将列表放在列表列表中,其中每个较小的列表都适合内存。