ReduceByKey + Map + Seq说明

时间:2017-04-04 10:41:48

标签: scala apache-spark spark-streaming scala-collections

我正在尝试理清 reduceByKey 如何运作,但这种情况令我感到困惑,我根本无法理解。

代码是:

vector

想象一下数据收入: [“oceania”,500],[“australia”,450]等。

展平 变量中,我正在尝试按市场类型或JSON中的第一种类型聚合数据。这里是生成元组:*第一个是计数器值,该值为1,         *第二个是费率并从Kafka收到,         *第三个是活动时间。例如 stream.foreachRDD((rdd: RDD[Record]) => { // convert string to PoJo and generate rows as tuple group val pairs = rdd .map(row => (row.timestamp(), jsonDecode(row.value()))) .map(row => (row._2.getType.name(), (1, row._2.getValue, row._1))) val flatten = pairs .reduceByKey((x, y) => (x._1 + y._1, x._2 + y._2, (y._3 + x._3) / 2)) .map(f => Row.fromSeq(Seq(f._1, f._2._2 / f._2._1, new Timestamp(f._2._3))))         *         *在地图中,         *方法2017-05-12 16:00:00是市场名称,         *我们将总费率除以项目总数f._1         *正如您所看到的,f._2._2 / f._2._1是平均活动时间

有人可以帮我解释一下是什么意思f._2._3(我的意思是我知道它的临时变量,但那里有什么或可能是什么)以及如何通过除f._2._3来计算总费率,什么是除法究竟?谢谢:))

1 个答案:

答案 0 :(得分:1)

对于每一行,您在RDD pairs中定义以下元素:

(marketType, (counter, rate, eventTime))

请注意,这是Tuple2,其第二个元素是Tuple3Tuple是特殊案例类,其n个元素(从1开始)被命名为_n。例如,要访问元素rate的{​​{1}},您必须执行ff._2._2的第二个元素,这是Tuple3的第二个元素{1}})。

由于您的元素具有特殊含义,您可能需要考虑定义案例类Tuple2,以便在编写MyRow(counter: Int, rate: Int, time: Timestamp)之类的内容时更清楚地了解代码的作用(通过方式,f._2._3的类型对我来说并不清楚,因为你只将它表示为eventTime,但你对它进行了数值运算。)

现在你的代码真正尝试做什么:

还原功能需要两个String(或Tuple3,如果你改变你的代码)并输出另一个(这里,你的减少函数总和计数器,速率,并使得之间的平均值eventTime上有两个值。

MyRow应用此缩减函数,只要它找到两个具有相同键的元素:由于reduce函数的输出与其输入的类型相同,因此可以对其应用,只要你的RDD上有其他具有相同密钥的值。

举个简单的例子,如果你有

reduceByKey

然后reduceByKey将输出

(key1, (1, 200, 2017/04/04 12:00:00))
(key1, (1, 300, 2017/04/04 12:00:00))
(key1, (1, 500, 2017/04/04 12:00:00))
(key2, (1, 500, 2017/04/04 12:00:00))

然后你的最后一个(key1, (3, 1000, 2017/04/04 12:00:00)) (key2, (1, 500, 2017/04/04 12:00:00)) 将通过计算总费率来解决这个问题:

map

您可能已经注意到我在所有示例中始终使用相同的时间。这是因为你在这个字段上的缩减功能会产生意想不到的结果,因为它不是关联。尝试执行与上述相同的练习,但使用不同的时间戳,您将看到(key1, (333, 2017/04/04 12:00:00)) (key2, (500, 2017/04/04 12:00:00)) 的缩减值将根据您应用缩减的顺序而有所不同。

让我们看看这个:我们想用这个函数减少4,8和16,所以我们可能希望这样做

key1

((4 + 8) / 2 + 16) / 2

取决于我们是想要从左侧还是右侧开始(在实际情况下,还有更多不同的可能性,它们将在Spark中发生,因为您并不总是知道您的值是如何分布在群集)。

计算上述两种可能性,我们会得到不同的值:(4 + (8 + 16) / 2) / 2 11,因此您会发现这可能会在现实案例中造成更大的问题。

在您的情况下,一个简单的解决方案是同时执行所有时间戳的总和(假设它们是8值,或者甚至是Long,以避免溢出),并且仅在最后通过具有实时平均值的值的数量。