我正在处理一个代表事件流的数据集(比如从网站上发布的跟踪事件)。所有活动都有时间戳。我们经常遇到的一个用例是尝试找到给定字段的第一个非空值。因此,例如像我们一样的东西:
val eventsDf = spark.read.json(jsonEventsPath)
case class ProjectedFields(visitId: String, userId: Int, timestamp: Long ... )
val projectedEventsDs = eventsDf.select(
eventsDf("message.visit.id").alias("visitId"),
eventsDf("message.property.user_id").alias("userId"),
eventsDf("message.property.timestamp"),
...
).as[ProjectedFields]
projectedEventsDs.groupBy($"visitId").agg(first($"userId", true))
上述代码的问题在于无法保证馈送到first
聚合函数的数据的顺序。我希望它按timestamp
排序,以确保它是时间戳的第一个非null userId,而不是任何随机的非null userId。
有没有办法在分组中定义排序?
使用Spark 2.10
BTW,SPARK DataFrame: select the first row of each group中为Spark 2.10建议的方法是在分组之前进行排序 - 这不起作用。例如,以下代码:
case class OrderedKeyValue(key: String, value: String, ordering: Int)
val ds = Seq(
OrderedKeyValue("a", null, 1),
OrderedKeyValue("a", null, 2),
OrderedKeyValue("a", "x", 3),
OrderedKeyValue("a", "y", 4),
OrderedKeyValue("a", null, 5)
).toDS()
ds.orderBy("ordering").groupBy("key").agg(first("value", true)).collect()
有时会返回Array([a,y])
,有时会返回Array([a,x])
答案 0 :(得分:5)
使用我的心爱的窗口(......体验生活变得更简单!)
import org.apache.spark.sql.expressions.Window
val byKeyOrderByOrdering = Window
.partitionBy("key")
.orderBy("ordering")
.rangeBetween(Window.unboundedPreceding, Window.unboundedFollowing)
import org.apache.spark.sql.functions.first
val firsts = ds.withColumn("first",
first("value", ignoreNulls = true) over byKeyOrderByOrdering)
scala> firsts.show
+---+-----+--------+-----+
|key|value|ordering|first|
+---+-----+--------+-----+
| a| null| 1| x|
| a| null| 2| x|
| a| x| 3| x|
| a| y| 4| x|
| a| null| 5| x|
+---+-----+--------+-----+
注意:不知何故,Spark 2.2.0-SNAPSHOT(今天建成)无法给出正确的答案,没有rangeBetween
我认为应该是默认的无限范围。