![](/img/trans.png)
[英]spark-sql error column is neither present in the group by, nor is it an aggregate function can't solve with first_value, collected_list
[英]Spark-SQL Window functions on Dataframe - Finding first timestamp in a group
我有下面的数据框(比如说UserData)。
uid region timestamp
a 1 1
a 1 2
a 1 3
a 1 4
a 2 5
a 2 6
a 2 7
a 3 8
a 4 9
a 4 10
a 4 11
a 4 12
a 1 13
a 1 14
a 3 15
a 3 16
a 5 17
a 5 18
a 5 19
a 5 20
该数据不过是用户(uid)在不同时间(时间戳)跨不同区域(region)传播的信息。 目前,为简单起见,时间戳显示为“ int”。 请注意,上述数据帧不一定按时间戳顺序递增。 另外,不同用户之间可能会有一些行。 为了简单起见,我仅以时间戳的单调递增顺序显示了单个用户的数据帧。
我的目标是-查找用户“ a”在每个区域以什么顺序花费了多少时间? 所以我最终的预期输出看起来像
uid region regionTimeStart regionTimeEnd
a 1 1 5
a 2 5 8
a 3 8 9
a 4 9 13
a 1 13 15
a 3 15 17
a 5 17 20
根据我的发现,Spark SQL Window函数可用于此目的。 我在下面尝试过
val w = Window
.partitionBy("region")
.partitionBy("uid")
.orderBy("timestamp")
val resultDF = UserData.select(
UserData("uid"), UserData("timestamp"),
UserData("region"), rank().over(w).as("Rank"))
但是从现在开始,我不确定如何获取regionTimeStart
和regionTimeEnd
列。 regionTimeEnd
柱也不过是“铅” regionTimeStart
除了在小组中的最后一项。
我看到聚合操作具有“第一个”和“最后一个”功能,但是为此,我需要基于('uid','region')对数据进行分组,这破坏了遍历路径的单调递增顺序,即在时间13,14,用户回来了到“ 1”区域,我希望保留该区域,而不是将其与时间1的初始区域“ 1”合并。
如果有人可以指导我,那将非常有帮助。 我是Spark的新手,与Python / JAVA Spark API相比,我对Scala Spark API有更好的了解。
窗口功能确实有用,尽管您的方法仅在假定用户仅访问给定区域一次的情况下才有效。 同样,您使用的窗口定义也不正确-多次调用partitionBy
只需返回具有不同窗口定义的新对象。 如果.partitionBy("region", "uid")
多个列进行分区,则应在一个调用中传递它们( .partitionBy("region", "uid")
)。
让我们从标记每个区域的连续访问开始:
import org.apache.spark.sql.functions.{lag, sum, not}
import org.apache.spark.sql.expressions.Window
val w = Window.partitionBy($"uid").orderBy($"timestamp")
val change = (not(lag($"region", 1).over(w) <=> $"region")).cast("int")
val ind = sum(change).over(w)
val dfWithInd = df.withColumn("ind", ind)
接下来,我们简单地汇总各组并找到潜在客户:
import org.apache.spark.sql.functions.{lead, coalesce}
val regionTimeEnd = coalesce(lead($"timestamp", 1).over(w), $"max_")
val result = dfWithInd
.groupBy($"uid", $"region", $"ind")
.agg(min($"timestamp").alias("timestamp"), max($"timestamp").alias("max_"))
.drop("ind")
.withColumn("regionTimeEnd", regionTimeEnd)
.withColumnRenamed("timestamp", "regionTimeStart")
.drop("max_")
result.show
// +---+------+---------------+-------------+
// |uid|region|regionTimeStart|regionTimeEnd|
// +---+------+---------------+-------------+
// | a| 1| 1| 5|
// | a| 2| 5| 8|
// | a| 3| 8| 9|
// | a| 4| 9| 13|
// | a| 1| 13| 15|
// | a| 3| 15| 17|
// | a| 5| 17| 20|
// +---+------+---------------+-------------+
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.