Look in either direction
lag reads an earlier row; lead reads a later row in the window order. Here order_id defines the sequence, not a date or elapsed time. An offset of 1 means one row away. At a boundary with no neighbor, Spark returns null. Partitioning would restart this sequence for each group.
Lesson reference: PySpark and Scala
Look in either direction
lag reads an earlier row; lead reads a later row in the window order. Here order_id defines the sequence, not a date or elapsed time. An offset of 1 means one row away. At a boundary with no neighbor, Spark returns null. Partitioning would restart this sequence for each group.
PySpark example
from pyspark.sql import Window
from pyspark.sql.functions import lag
result = orders.withColumn(
"neighbor_total", lag("total", 1).over(Window.orderBy("order_id"))
).select("order_id", "total", "neighbor_total").orderBy("order_id")
result.show(truncate=False)
Scala example
import org.apache.spark.sql.expressions.Window
import org.apache.spark.sql.functions._
val result = orders.withColumn(
"neighbor_total", lag("total", 1).over(Window.orderBy("order_id"))
).select("order_id", "total", "neighbor_total").orderBy("order_id")
result.show(false)
Change the offset
The offset is a row count within the ordered partition. lag(total, 1) reads one row back; increasing the offset looks farther back without changing the current row. Skipped order IDs do not create extra rows. The first rows return null when the requested neighbor does not exist.
PySpark example
from pyspark.sql import Window
from pyspark.sql.functions import lag
result = orders.withColumn(
"neighbor_total", lag("total", 1).over(Window.orderBy("order_id"))
).select("order_id", "total", "neighbor_total").orderBy("order_id")
result.show(truncate=False)
Scala example
import org.apache.spark.sql.expressions.Window
import org.apache.spark.sql.functions._
val result = orders.withColumn(
"neighbor_total", lag("total", 1).over(Window.orderBy("order_id"))
).select("order_id", "total", "neighbor_total").orderBy("order_id")
result.show(false)
Calculate a difference
Subtracting the previous total gives the change between adjacent rows. The first change is null because subtraction with null returns null. Treating a missing previous value as zero is a separate business choice: it makes the first change equal to its current total, not zero.
PySpark example
from pyspark.sql import Window
from pyspark.sql.functions import col, lag
result = orders.withColumn(
"previous_total", lag("total", 1).over(Window.orderBy("order_id"))
).withColumn(
"change", col("total") - col("previous_total")
).select("order_id", "total", "change").orderBy("order_id")
result.show(truncate=False)
Scala example
import org.apache.spark.sql.expressions.Window
import org.apache.spark.sql.functions._
val result = orders.withColumn(
"previous_total", lag("total", 1).over(Window.orderBy("order_id"))
).withColumn(
"change", col("total") - col("previous_total")
).select("order_id", "total", "change").orderBy("order_id")
result.show(false)