Window.partitionBy & row_number
A window is a set of rows related to the current row. Unlike groupBy, a window function keeps every row — it just adds a column computed over the window. You define the window with Window.partitionBy(...).orderBy(...), then apply a function over it. row_number is the classic: number the rows 1, 2, 3… within each partition.
import org.apache.spark.sql.expressions.Window val w = Window.partitionBy($"category").orderBy($"amount".desc) def row_number(): Column // 1,2,3 … no ties, always distinct someFn.over(w) // apply a function over the window
import org.apache.spark.sql.expressions.Window
import org.apache.spark.sql.functions.row_number
val w = Window.partitionBy($"category").orderBy($"amount".desc)
orders.select(
$"product", $"category", $"amount",
row_number().over(w).as("rn")
).show(false)row_number partitioned by the group and ordered by your metric, then filter($"rn" <= 3). That's the idiomatic Spark way to get the 3 biggest orders per category — no self-join needed. Always give the window an orderBy for ranking functions; without one the numbering is non-deterministic.