繁体   English   中英

spark Aggregationbykey-同一通话中的总和和移动平均值

[英]spark aggregatebykey - sum and running average in the same call

我正在学习火花,并且没有使用hadoop的经验。

问题

我正在尝试在对AggregateByKey的同一调用中计算总和和平均值。

让我分享到目前为止我所做的尝试。

设置数据

val categoryPrices = List((1, 20), (1, 25), (1, 10), (1, 45))
val categoryPricesRdd = sc.parallelize(categoryPrices)

尝试在对AggregateByKey的同一调用中计算平均值。 这是行不通的。

val zeroValue1 = (0, 0, 0.0) // (count, sum, average)
categoryPricesRdd.
    aggregateByKey(zeroValue1)(
        (tuple, prevPrice) => {
            val newCount = tuple._1 + 1
            val newSum = tuple._2 + prevPrice
            val newAverage = newSum/newCount
            (newCount, newSum, newAverage)
    },
    (tuple1, tuple2) => {
        val newCount1 = tuple1._1 + tuple2._1
        val newSum1 = tuple1._2 + tuple2._2
        // TRYING TO CALCULATE THE RUNNING AVERAGE HERE
        val newAverage1 = ((tuple1._2 * tuple1._1) + (tuple2._2 * tuple2._1))/(tuple1._1 + tuple2._1)
       (newCount1, newSum1, newAverage1)
    }
).
collect.
foreach(println)

结果:每次打印不同的平均值

  • 第一次:(1,(4,100,70.0))
  • 第二次:(1,(4,100,52.0))

只需先求和,然后在单独的操作中计算平均值。 这可行。

val zeroValue2 = (0, 0) // (count, sum, average)
categoryPricesRdd.
    aggregateByKey(zeroValue2)(
        (tuple, prevPrice) => {
            val newCount = tuple._1 + 1
            val newSum = tuple._2 + prevPrice
            (newCount, newSum)
        },
        (tuple1, tuple2) => {
            val newCount1 = tuple1._1 + tuple2._1
            val newSum1 = tuple1._2 + tuple2._2
            (newCount1, newSum1)
        }
    ).
    map(rec => {
        val category = rec._1
        val count = rec._2._1
        val sum = rec._2._2
        (category, count, sum, sum/count)
    }).
    collect.
    foreach(println)

每次都打印相同的结果:(1,4,100,25)

我想我了解seqOp和CombOp之间的区别。 鉴于操作可以在不同服务器上的多个分区之间拆分数据,我的理解是seqOp对单个分区中的数据进行操作,然后combOp合并从不同分区接收的数据。 如果这是错误的,请更正。

但是,有一些我不太了解的基本知识。 看起来我们无法在同一调用中同时计算总和和平均值。 如果是这样,请帮助我理解原因。

seqOpaverage聚合有关的计算:

val newAverage = newSum/newCount

并在combOp

val newAverage1 = ((tuple1._2 * tuple1._1) + (tuple2._2 * tuple2._1)) / (tuple1._1 + tuple2._1)

是不正确的。

假设前三个元素在一个分区中,最后一个元素在另一个分区中。 您的seqOp将生成(计数,总和,平均值)元组,如下所示:

Partition #1: [20, 25, 10]
  --> (1, 20, 20/1)
  --> (2, 45, 45/2)
  --> (3, 55, 55/3)

Partition #2: [45]
  --> (1, 45, 45/1)

接下来,跨分区combOp将合并两个分区中的2个元组,得到:

((55 * 3) + (45 * 1)) / 4
// Result: 52

从上述步骤可以看到,如果RDD元素的顺序或分区不同,则average可能会不同。

您的第二种方法有效,因为按定义, average是总和超过总计数,因此在先计算总和和计数值之后可以更好地进行计算。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM