我有一个具有以下架构的DataFrame
:
root
|- documentId
|- timestamp
|- anotherField
例如,
"d1", "2018-09-20 10:00:00", "blah1"
"d2", "2018-09-20 09:00:00", "blah2"
"d1", "2018-09-20 10:01:00", "blahnew"
请注意,为了便于理解(和我的方便),我将时间戳记显示为字符串。实际上,它是一个long
,代表自纪元以来的毫秒数。
如此处所示,存在重复的行(行1和3),这些行具有相同的documentId
但具有不同的timestamp
(并且可能还有其他字段)。我想对每个timestamp
进行重复数据删除并仅保留最新行(基于documentId
)。
一个简单的df.groupBy("documentId").agg(max("timestamp), ...)
在这里似乎不太可行,因为我不知道如何在与max("timestamp")
相符的行中保留其他字段。
因此,我想出了一种复杂的方法。
// first find the max timestamp corresponding to each documentId
val mostRecent = df
.select("documentId", "timestamp")
.groupBy("documentId")
.agg(max("timestamp"))
// now join with the original df on timestamp to retain
val dedupedDf = df.join(mostRecent, Seq("documentId", "timestamp"), "inner")
由此产生的dedupedDf
应该仅具有与每个documentId
的最新条目相对应的行。
尽管这行得通,但我认为这不是正确的(或有效的)方法,因为我使用的似乎是不必要的join
。
我该如何做得更好?我正在寻找纯粹的基于“ DataFrame”的解决方案,而不是基于RDD的方法(因为DataBricks的人在一个研讨会上反复告诉我们要使用DataFrames而不是RDD)。
答案 0 :(得分:3)
请参阅以下代码帮助您实现目标,
val df = Seq(
("d1", "2018-09-20 10:00:00", "blah1"),
("d2", "2018-09-20 09:00:00", "blah2"),
("d1", "2018-09-20 10:01:00", "blahnew")
).toDF("documentId","timestamp","anotherField")
import org.apache.spark.sql.functions.row_number
import org.apache.spark.sql.expressions.Window
val w = Window.partitionBy($"documentId").orderBy($"timestamp".desc)
val Resultdf = df.withColumn("rownum", row_number.over(w))
.where($"rownum" === 1).drop("rownum")
Resultdf.show()
输入:
+----------+-------------------+------------+
|documentId| timestamp|anotherField|
+----------+-------------------+------------+
| d1|2018-09-20 10:00:00| blah1|
| d2|2018-09-20 09:00:00| blah2|
| d1|2018-09-20 10:01:00| blahnew|
+----------+-------------------+------------+
输出:
+----------+-------------------+------------+
|documentId| timestamp|anotherField|
+----------+-------------------+------------+
| d2|2018-09-20 09:00:00| blah2|
| d1|2018-09-20 10:01:00| blahnew|
+----------+-------------------+------------+