RDD聚合在火花中

时间:2016-05-04 04:07:22

标签: scala apache-spark rdd

我是一名Apache Spark学习者,遇到RDD动作aggregate,我不知道它是如何运作的。有人可以逐步详细解释并详细解释我们如何得到以下代码的结果

RDD input = {1,2,3,3}

RDD Aggregate function :

rdd.aggregate((0, 0))
((x, y) =>
(x._1 + y, x._2 + 1),
(x, y) =>
(x._1 + y._1, x._2 + y._2))

output : {9,4}

由于

2 个答案:

答案 0 :(得分:22)

如果您不确定发生了什么,最好遵循这些类型。为简洁省略隐式ClassTag,我们从这样的

开始
abstract class RDD[T] extends Serializable with Logging 

def aggregate[U](zeroValue: U)(seqOp: (U, T) ⇒ U, combOp: (U, U) ⇒ U): U 

如果您忽略所有其他参数,则会发现aggregate是一个从RDD[T]映射到U的函数。这意味着输入RDD中值的类型不必与输出值的类型相同。所以它明显不同于例如reduce

def reduce(func: (T, T) ⇒ T): T 

fold

def fold(zeroValue: T)(op: (T, T) => T): T

fold相同,aggregate需要zeroValue。怎么选择它?它应该是combOp的身份(中性)元素。

您还必须提供两个功能:

  • seqOp(U, T)映射到U
  • combOp(U, U)映射到U

基于此签名,您应该已经看到只有seqOp可以访问原始数据。它需要U类型的某个值,另一个类型为T,并返回类型U的值。在您的情况下,它是具有以下签名的函数

((Int, Int), Int) => (Int, Int) 

此时你可能怀疑它被用于某种类似折叠的操作。

第二个函数接受U类型的两个参数,并返回类型U的值。如前所述,应该清楚的是,它不会触及原始数据,只能对seqOp已经处理过的值进行操作。在您的情况下,此函数具有如下签名:

((Int, Int), (Int, Int)) => (Int, Int) 

那么我们如何能够将所有这些结合在一起呢?

  1. 首先使用标准Iterator.aggregate汇总每个分区,zeroValueseqOpcombOp传递为zseqopcombop各自。由于内部使用的InterruptibleIterator不会覆盖aggregate,因此应将其作为简单的foldLeft(zeroValue)(seqOp)

  2. 执行
  3. 使用combOp

  4. 汇总从每个分区收集的下一部分结果

    让我们假设输入RDD有三个分区,其值分布如下:

    • Iterator(1, 2)
    • Iterator(2, 3)
    • Iterator()

    您可以预期执行,忽略绝对顺序,将等同于:

    val seqOp = (x: (Int, Int), y: Int) => (x._1 + y, x._2 + 1)
    val combOp = (x: (Int, Int), y: (Int, Int)) => (x._1 + y._1, x._2 + y._2)
    
    Seq(Iterator(1, 2), Iterator(3, 3), Iterator())
      .map(_.foldLeft((0, 0))(seqOp))
      .reduce(combOp)
    
    单个分区的

    foldLeft可能如下所示:

    Iterator(1, 2).foldLeft((0, 0))(seqOp)
    Iterator(2).foldLeft((1, 1))(seqOp)
    (3, 2)
    

    以及所有分区

    Seq((3,2), (6,2), (0,0))
    

    组合将给你观察结果:

    (3 + 6 + 0, 2 + 2 + 0)
    (9, 4)
    

    通常,这是一个常见的模式,您可以在Spark中找到传递中性值的函数,一个用于处理每个分区值的函数和一个用于合并来自不同分区的部分聚合的函数。其他一些例子包括:

    • aggregateByKey
    • 用户定义的聚合函数
    • Spark上的
    • Aggregators Datasets

答案 1 :(得分:4)

以下是我对您的理解:

想象一下,你有两个节点,一个接受前两个列表元素{1,2}的输入,另一个接受{3,3}。 (这里的分区只是为了方便)

在第一个节点: “(x,y)=>(x._1 + y,x._2 + 1)”,第一个x是给定的(0,0),y是你的第一个元素1 ,你将有输出(0 + 1,0 + 1),然后你的第二个元素y = 2,输出(1 + 2,1 + 1),这是(3,2)

在第二个节点,相同的过程并行发生,你将拥有(6,2)。

(x,y)=>(x._1 + y._1,x._2 + y._2)”,告诉您合并两个节点,你会得到(9,4)

值得注意的一件事是(0,0)实际上已添加到结果中 长度(rdd)+1次。

scala> rdd.aggregate((1,1))((x,y)=>(x._1 + y,x._2 + 1),(x,y)=> (x._1 + y._1,x._2 + y._2)) res1:(Int,Int)=(14,9)