我正在尝试理清 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
来计算总费率,什么是除法究竟?谢谢:))
答案 0 :(得分:1)
对于每一行,您在RDD pairs
中定义以下元素:
(marketType, (counter, rate, eventTime))
请注意,这是Tuple2
,其第二个元素是Tuple3
。 Tuple
是特殊案例类,其n
个元素(从1
开始)被命名为_n
。例如,要访问元素rate
的{{1}},您必须执行f
(f._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
,以避免溢出),并且仅在最后通过具有实时平均值的值的数量。