Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get the last value using spark window function

Suppose I have a dataframe like this.

val df = sc.parallelize(Seq(
            (1.0, 1,"Matt"), 
            (1.0, 2,"John"),
            (1.0, 3,null.asInstanceOf[String]),
            (-1.0, 2,"Adam"), 
            (-1.0, 4,"Steve"))
          ).toDF("id", "timestamp","name")

I want to get the last non-null value for each id ordered by timestamp. This is my window

val partitionWindow = Window.partitionBy($"id").orderBy($"timestamp".desc)

I am creating a distinct windowed data

val filteredDF = df.filter($"name".isNotNull).withColumn("firstName", first("name") over (partitionWindow)).drop("timestamp","name").distinct

and joining it back to the actual data

val joinedDF = df.join(filteredDF, windowDF.col("id") === filteredDF.col("id")).drop(filteredDF.col("id"))

joinedDF.show()

It works fine, but I dont like this solution, can anyone suggest me something better?

Also, can anyone tell me why does last function not work? I tried this and the results are not correct

 val partitionWindow = Window.partitionBy($"id").orderBy($"timestamp")

val windowDF = df.withColumn("lastName", last("name") over (partitionWindow))
like image 281
Srinivas Avatar asked Jul 02 '26 19:07

Srinivas


1 Answers

If you want to propagate the last known value (it is not the same as logic used with join) you should:

  • ORDER BY timestamp.
  • Take last ignoring nulls:
val partitionWindow = Window.partitionBy($"id").orderBy($"timestamp")

df.withColumn("lastName", last("name", true) over (partitionWindow)).show
// +----+---------+-----+--------+
// |  id|timestamp| name|lastName|
// +----+---------+-----+--------+
// |-1.0|        2| Adam|    Adam|
// |-1.0|        4|Steve|   Steve|
// | 1.0|        1| Matt|    Matt|
// | 1.0|        2| John|    John|
// | 1.0|        3| null|    John|
// +----+---------+-----+--------+

If you want to take the last value globally:

  • ORDER BY timestamp.
  • Set unbounded frame.
  • Take last ignoring nulls:
val partitionWindow = Window.partitionBy($"id").orderBy($"timestamp")
  .rowsBetween(Window.unboundedPreceding, Window.unboundedFollowing)

df.withColumn("lastName", last("name", true) over (partitionWindow)).show
// +----+---------+-----+--------+
// |  id|timestamp| name|lastName|
// +----+---------+-----+--------+
// |-1.0|        2| Adam|   Steve|
// |-1.0|        4|Steve|   Steve|
// | 1.0|        1| Matt|    John|
// | 1.0|        2| John|    John|
// | 1.0|        3| null|    John|
// +----+---------+-----+--------+
like image 125
Alper t. Turker Avatar answered Jul 05 '26 12:07

Alper t. Turker