任何人都可以详细解释下面如何在spark中产生agrregate行为产生(9,4)
的结果val rdd = sc.parallelize(List(1,2,3,3))
rdd.aggregate((0, 0))
((x, y) =>
(x._1 + y, x._2 + 1),
(x, y) =>
(x._1 + y._1, x._2 + y._2))
res : (9,4)
答案 0 :(得分:3)
基本上聚合说:我们想要一个元组(a,b),其中a是所有元素的总和,b是它们的计数。
这是通过初始化为(0,0)完成的,然后我们有两个函数:
第一个函数只是在我们一次得到一个元素时进行求和,即通过将值添加到第一个元素并将1(count)加到第二个元素来从单个元素更新元组。
第二个函数合并两个结果,因此它只是元素添加
让我们考虑输入数据的示例:
假设1,2在分区1中,3,3在分区3中。
分区1计算
分区1将以(0,0)开头。
然后第一个功能开始工作。
当我们添加一个(1,1)时。第一个元素是sum(0 + y,其中y是1),第二个元素是count(0 + 1)。
现在我们加2,所以得到(1 + 2,1 + 1)=(3,2)。再次,第一个元素是我们到目前为止看到的值的总和,第二个元素是它们的计数。
分区2计算
在第二个分区上,我们再次以(0,0)开始,然后从第一个分区得到(3,1),从第二个分区得到(6,2)。
合并结果
现在第二个功能合并两个: 我们通过将两个元素相加并得到(9,4)
来合并(3,2)和(6,2)答案 1 :(得分:1)
这是 Spark 2.1.0 (这应该不重要,但是......)
转到the official documentation of aggregate
(又名 scaladoc )并阅读:
使用给定的组合函数和中性"零值"聚合每个分区的元素,然后聚合所有分区的结果。此函数可以返回与此RDD,T的类型不同的结果类型U。因此,我们需要一个操作将T合并到U中,并且需要一个操作来合并两个U,如scala.TraversableOnce 。允许这两个函数修改并返回它们的第一个参数,而不是创建一个新的U来避免内存分配。
签名如下(删除隐含参数并不特别有趣):
aggregate[U](zeroValue: U)(seqOp: (U, T) ⇒ U, combOp: (U, U) ⇒ U): U
scaladoc说:
zeroValue seqOp运算符的每个分区的累积结果的初始值,以及combOp运算符的不同分区的组合结果的初始值 - 这通常是中性元素(例如,列表连接为Nil,或者求和为0)
在您的情况下,zeroValue
为(0, 0)
。
seqOp 用于在分区中累积结果的运算符
在您的情况下,seqOp
是(x, y) => (x._1 + y, x._2 + 1)
,这是一个接受两对的函数,不幸名为x
和y
(我称之为{{1}至少和p1
至少使用模式匹配和部分函数,即p2
)。
鉴于您已获得case ((x1, y1), (x2, y2)) => ...
个分区(可以使用n
查看),rdd.getNumPartition
将被称为seqOp
次。
scaladoc说:
combOp 用于合并来自不同分区的结果的关联运算符
这意味着n
将结合combOp
的所有结果并应用该函数:
seqOp
它再次写得太糟糕,所以你看得太多了,我甚至打电话给一个噪音。我按如下方式编写函数:
(x, y) => (x._1 + y._1, x._2 + y._2)
按照类型并给出正确的名称,最终Scala中的所有内容变得更加容易; - )
答案 2 :(得分:0)
让我们变得简单
情况1:没有分区,只是独立存在( seqOp )。专注于(x,y)=> (x._1 + y,x._2 + 1),忽略分区。
{1,2,3,3}
x._1 y x._2
(0+1,0+1) 0 1 0
(1+2,1+1) 1 2 1
(3+3,2+1) 3 3 2
(6+3,3+1) 6 3 3
Result:(9,4)
情况2:具有分区,假设2个分区( combOp ){1,2},{3,3}。专注于 (x,y)=> (x._1 + y,x._2 +1),
(x,y)=> (x._1 + y._1,x._2 + y._2))
{1,2}
x._1 y x._2
(0+1,0+1) 0 1 0
(1+2,1+1) 1 2 1
Result1: (3,2)
{3,3}
x._1 y x._2
(0+3,0+1) 0 3 0
(3+3,1+1) 3 3 1
Result2: (6,2)
Final result:(9,4)