下面我有一个Spark fold
操作的Scala示例:
val rdd1 = sc.parallelize(List(1,2,3,4,5), 3)
rdd1.fold(5)(_ + _)
这会产生输出35
。有人可以详细解释这个输出是如何计算的吗?
答案 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)
答案 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) })