Scala集合了第一个功能,给出了意外结果

时间:2019-02-06 16:40:36

标签: scala apache-spark apache-spark-sql

我在Scala Spark中使用了一个简单的groupby查询,其目的是获取已排序数据框中组中的第一个值。这是我的Spark数据框

+---------------+------------------------------------------+
|ID             |some_flag |some_type  |  Timestamp        |
+---------------+------------------------------------------+
|      656565654|      true|     Type 1|2018-08-10 00:00:00|
|      656565654|     false|     Type 1|2017-08-02 00:00:00|
|      656565654|     false|     Type 2|2016-07-30 00:00:00|
|      656565654|     false|     Type 2|2016-05-04 00:00:00|
|      656565654|     false|     Type 2|2016-04-29 00:00:00|
|      656565654|     false|     Type 2|2015-10-29 00:00:00|
|      656565654|     false|     Type 2|2015-04-29 00:00:00|
+---------------+----------+-----------+-------------------+

这是我的汇总查询

val sampleDF = df.sort($"Timestamp".desc).groupBy("ID").agg(first("Timestamp"), first("some_flag"), first("some_type"))

预期结果是

+---------------+-------------+---------+-------------------+
|ID             |some_falg    |some_type|  Timestamp        |
+---------------+-------------+---------+-------------------+
|      656565654|         true|   Type 1|2018-08-10 00:00:00|
+---------------+-------------+---------+-------------------+

但是跟随更奇怪的输出,并且像随机行一样不断变化

+---------------+-------------+---------+-------------------+
|ID             |some_falg    |some_type|  Timestamp        |
+---------------+-------------+---------+-------------------+
|      656565654|        false|   Type 2|2015-10-29 00:00:00|
+---------------+-------------+---------+-------------------+

还请注意,数据框中没有空值。我在做错事情的地方挠头。需要帮助!

2 个答案:

答案 0 :(得分:3)

您尝试获取所有第一个值的方式将返回错误的结果。每个列的值可能来自不同的行。

相反,每个组只应按降序排列order by时间戳,并获得第一行。一种简单的方法是使用row_number之类的函数。

import org.apache.spark.sql.functions._
import org.apache.spark.sql.expressions.Window

val sampleDF = df.withColumn("rnum",row_number().over(Window.partitionBy(col("ID")).orderBy(col("Timestamp").desc)))

sampleDF.filter(col("rnum") == 1).show

答案 1 :(得分:3)

只需添加到Vamsi的答案中即可;问题是groupBy结果组中的值没有以任何特定顺序返回(特别是考虑到Spark操作的分布式性质),因此first函数的名称可能会引起误解。它返回为该列找到的第一个非空值,即组中该列的几乎所有非空值。

groupBy之前对行进行排序不会以任何可重复的方式影响组内的顺序。

另请参见此blog post,该解释说明,由于上述行为,您从多个first调用中获得的值甚至可能不是来自该组中的同一行。

  

输入带有3列“ k,t,v”的数据

z, 1, null
z, 2, 1.5
z, 3, 2.4
  

代码:

df.groupBy("k").agg(
  $"k",
  first($"t"),
  first($"v")
)
  

输出:

z, 1, 1.5
  

此结果混合了2条记录!