Skip to content
Great engineers know 3 min read · Window 1 practice problem ↓

last(ignorenulls)

Forward-fill missing values with last over a window, and the frame that makes it work.

You will learn

  • How to forward-fill missing values with last(ignorenulls)
  • Which window frame makes it work
  • How to backward-fill with first
  • Where forward-filling is wrong

Read first

Comfortable with these? Read on.

TL;DR F.last("price", ignorenulls=True) over a window ordered by time, with a frame from the start up to the current row, returns the most recent non-null value: a forward fill. first(ignorenulls=True) over the rest of the partition back-fills.

What it does

Sensors, prices and statuses often only record a value when it changes. To get a value for every row, carry the last known value forward until a new one appears.

Step by step

readings (sensor A)

tstemp
121
2null
3null
423.5
5null

forward-filled

tstemptemp_filled
12121
2null21
3null21
423.523.5
5null23.5
  1. 1
    The window is partitioned by sensor and ordered by ts.
  2. 2
    The frame runs from the first row of the partition to the current row.
  3. 3
    last(temp, ignorenulls=True) looks at that frame and returns the latest non-null temp.
  4. 4
    Sensor B's first row has no earlier value, so it stays null.

Run the example

PySparkSpark SQL
from pyspark.sql import functions as F
from pyspark.sql.window import Window

w = Window.partitionBy("sensor").orderBy("ts")
frame = w.rowsBetween(Window.unboundedPreceding, Window.currentRow)
result = readings.withColumn("temp_filled", F.last("temp", ignorenulls=True).over(frame))
SELECT sensor, ts, temp,
       LAST(temp, TRUE) OVER (
         PARTITION BY sensor ORDER BY ts
         ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS temp_filled
FROM readings

Switch to PySpark to edit and run this example in your browser.

Spark 3.2 and later also accept the standard form LAST(temp) IGNORE NULLS OVER (...).

Why the explicit frame

The default frame for an ordered window is a range up to the current row, so rows with the same ts are all in each other's frame. With duplicate timestamps, a row could pick up a value from a tied row that comes "after" it. An explicit rowsBetween(unboundedPreceding, currentRow) makes the behaviour exact, and is clearer to read.

Back-filling

To fill from the next known value instead, look forward: F.first("temp", ignorenulls=True) over rowsBetween(Window.currentRow, Window.unboundedFollowing). Combining both, forward fill then back fill, fills everything except partitions with no values at all.

When not to forward-fill

Forward-filling asserts that the value did not change. That is true for a status or a slowly changing price, and false for a sensor that went offline for a day. Consider capping how far a value can be carried, for example by also carrying the timestamp of the last reading with last(F.when(temp.isNotNull(), ts), True) and nulling fills older than a threshold.

Common mistakes

Forgetting ignorenulls

Plain last over the frame returns the current row's null.

Relying on the default frame with duplicate timestamps

Tied rows see each other. Use rowsBetween.

No partitionBy

Values leak from one sensor into the next.

Key takeaways

  • last(col, ignorenulls=True) over a running frame forward-fills.
  • Use an explicit rowsBetween(unboundedPreceding, currentRow) frame.
  • first(ignorenulls=True) over the following rows back-fills.
  • Only forward-fill values that really persist.

Check yourself

3 questions

1. What does last("v") (without ignorenulls) return over a running frame when the current v is null?

Show the answer

null. Without ignorenulls, last returns the last row of the frame, which is the current row.

2. Which frame forward-fills?

Show the answer

rowsBetween(unboundedPreceding, currentRow). The frame must include everything up to the current row so the latest non-null value is in it.

3. How do you back-fill?

Show the answer

first(ignorenulls=True) over the current and following rows. first over the rows ahead finds the next known value.

Practice it

Interview problems that use last(ignorenulls): write the PySpark, run it, and get graded on hidden tests.

Solve: Forward-Fill Missing Readings →

Go deeper

Primary sources: functions.last · Window functions (SQL)