pyspark折叠方法输出

时间:2015-03-19 16:38:14

标签: apache-spark pyspark

我对fold的输出感到惊讶,我无法想象它正在做什么。

我希望something.fold(0, lambda a,b: a+1)会返回something中元素的数量,因为折叠从0开始,并为每个元素添加1

sc.parallelize([1,25,8,4,2]).fold(0,lambda a,b:a+1 )
8

我来自Scala,折叠按照我所描述的方式工作。那么如何折叠应该在pyspark中工作?谢谢你的想法。

2 个答案:

答案 0 :(得分:5)

要了解这里发生了什么,让我们来看看Spark fold操作的定义。由于你正在使用PySpark,我将展示代码的Python版本,但Scala版本表现出完全相同的行为(你也可以browse the source on GitHub):

def fold(self, zeroValue, op):
    """
    Aggregate the elements of each partition, and then the results for all
    the partitions, using a given associative function and a neutral "zero
    value."
    The function C{op(t1, t2)} is allowed to modify C{t1} and return it
    as its result value to avoid object allocation; however, it should not
    modify C{t2}.
    >>> from operator import add
    >>> sc.parallelize([1, 2, 3, 4, 5]).fold(0, add)
    15
    """
    def func(iterator):
        acc = zeroValue
        for obj in iterator:
            acc = op(obj, acc)
        yield acc
    vals = self.mapPartitions(func).collect()
    return reduce(op, vals, zeroValue)

(相比之下,请参阅Scala implementation of RDD.fold)。

Spark fold首先折叠每个分区,然后折叠结果。问题是空分区被折叠到零元素,因此最终的驱动程序端折叠最终为每个分区折叠一个值,而不是每个非空< / em>分区。这意味着fold的结果对分区数量敏感:

>>> sc.parallelize([1,25,8,4,2], 100).fold(0,lambda a,b:a+1 )
100
>>> sc.parallelize([1,25,8,4,2], 50).fold(0,lambda a,b:a+1 )
50
>>> sc.parallelize([1,25,8,4,2], 1).fold(0,lambda a,b:a+1 )
1

在最后一种情况下,发生的事情是将单个分区向下折叠到正确的值,然后将该值折叠为驱动程序的零值以产生1。

似乎Spark的fold()操作实际上要求折叠函数除了关联之外还可以交换。 Spark中实际上还有其他地方强加了这一要求,例如,在整个运行过程中,混洗分区中元素的排序可能是非确定性的(参见SPARK-5750)。

我已经开通了Spark JIRA门票来调查此问题:https://issues.apache.org/jira/browse/SPARK-6416

答案 1 :(得分:1)

让我尝试给出一些简单的例子来解释火花的折叠方法。我将在这里使用pyspark。

rdd1 = sc.parallelize(list([]),1)

上一行将创建一个具有一个分区的空rdd

rdd1.fold(10, lambda x,y:x+y)

  

此产量输出为20

rdd2 = sc.parallelize(list([1,2,3,4,5]),2)

上一行将创建值1到5的rdd,并且将总共有2个分区

rdd2.fold(10, lambda x,y:x+y)

  

输出为45

因此,在上述情况下,为简单起见,这里发生的是第零个元素为10。因此,现在将把RDD中所有数字得到的总和加10(即第零个元素+ all其他元素=> 10 + 1 + 2 + 3 + 4 + 5 = 25)。现在我们也有两个分区(即分区数*零个元素=> 2 * 10 = 20) 折叠发出的最终输出是25 + 20 = 45

使用类似的过程很明显,为什么对rdd1的折叠操作会产生20作为输出。

当我们有rdd1.reduce(lambda x,y:x+y)之类的空列表时,还原失败

  

ValueError:不能减少()空的RDD

如果我们认为我们可以在rdd中使用空列表,则可以使用折叠 rdd1.fold(0, lambda x,y:x+y)

  

如预期的那样,将输出为0。