折叠动作如何在Spark中起作用?

时间:2018-01-20 16:27:26

标签: scala apache-spark fold

下面我有一个Spark fold操作的Scala示例:

val rdd1 = sc.parallelize(List(1,2,3,4,5), 3)
rdd1.fold(5)(_ + _)

这会产生输出35。有人可以详细解释这个输出是如何计算的吗?

3 个答案:

答案 0 :(得分:6)

取自Scaladocs here(强调我的):

  

@param zeroValue每个累计结果的初始值   op运算符的分区,以及。的初始值   结合不同的结果   op运算符的分区 - 这通常是中立的   元素(例如Nil用于列表连接或0用于求和)

zeroValue在您的情况下添加了四次(每个分区一个,加上一个来自分区的结果)。结果是:

(5 + 1) + (5 + 2 + 3) + (5 + 4 + 5) + 5 // (extra one for combining results)

答案 1 :(得分:4)

zeroValue为每个分区添加一次,并且应该是一个中性元素 - 在+的情况下它应该是0.确切的结果将取决于分区的数量,但它相当于:< / p>

rdd1.mapPartitions(iter => Iterator(iter.foldLeft(zeroValue)(_ + _))).reduce(_ + _)

这样:

val rdd1 = sc.parallelize(List(1,2,3,4,5),3)

将数据分发为:

scala> rdd1.glom.collect
res1: Array[Array[Int]] = Array(Array(1), Array(2, 3), Array(4, 5))

并且整个表达式相当于:

(5 + 1) + (5 + 2 + 3) + (5 + 4 + 5)

plus 5 for jobResult

答案 2 :(得分:3)

你知道Spark RDD执行分布式计算。

所以,这一行,

val rdd1 = sc.parallelize(List(1,2,3,4,5), 3)

告诉Spark它需要在这个RDD中支持3个分区,这将使它能够并行使用3个独立的执行器来运行计算。

现在,这一行,

rdd1.fold(5)(_ + _)

告诉spark使用5作为初始值折叠所有这些分区,然后再次从3个执行器中折叠所有这些分区结果,并将5作为初始值。

正常的Scala等价物可以写成,

val list = List(1, 2, 3, 4, 5)
val listOfList = list.grouped(2).toList
val listOfFolds = listOfList.map(l => l.fold(5)(_ + _))
val fold = listOfFolds.fold(5)(_ + _)

所以......如果您在RDD上使用fold,则需要提供zero value

但是你会问 - 为什么或何时有人会使用fold代替reduce

你的困惑在于你对zero value的看法。问题是RDD [T]的这个zero value并不完全取决于我们的类型T,而是取决于计算的性质。因此,zero value不一定是0

让我们考虑一个简单的例子,我们想在RDD中计算"largest number greater than 15" or "15"

我们可以使用reduce执行此操作吗?答案是不。但我们可以使用fold

来完成
val n15GT15 = rdd1.fold(15)({ case (acc, i) => Math.max(acc, i) })