Handle ties
rank gives equal item counts the same rank and leaves gaps afterward. With counts 4, 3, 2, 2, 1, 1, the ranks are 1, 2, 3, 3, 5, 5. dense_rank removes the gaps. Do not add order_id to this ranking window: it would break the ties you want to preserve. The final display sort is separate.
Lesson reference: PySpark and Scala
Handle ties
rank gives equal item counts the same rank and leaves gaps afterward. With counts 4, 3, 2, 2, 1, 1, the ranks are 1, 2, 3, 3, 5, 5. dense_rank removes the gaps. Do not add order_id to this ranking window: it would break the ties you want to preserve. The final display sort is separate.
PySpark example
from pyspark.sql import Window
from pyspark.sql.functions import col, rank
result = orders.withColumn(
"item_rank", rank().over(Window.orderBy(col("item_count").desc()))
).select("order_id", "item_count", "item_rank").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(
"item_rank", rank().over(Window.orderBy(col("item_count").desc()))
).select("order_id", "item_count", "item_rank").orderBy("order_id")
result.show(false)
Choose one row per key
row_number assigns a distinct position within each customer. Filtering position 1 keeps the lowest-total order here. This is a business selection rule, not removal of identical records. Unlike dropDuplicates on customer_id, the ordered window specifies which order survives. order_id breaks equal-total ties.
PySpark example
from pyspark.sql import Window
from pyspark.sql.functions import col, row_number
result = orders.withColumn(
"position", row_number().over(
Window.partitionBy("customer_id").orderBy(col("total").asc(), col("order_id").asc())
)
).filter(col("position") == 1).select("order_id", "customer_id", "total").orderBy("customer_id")
result.show(truncate=False)
Scala example
import org.apache.spark.sql.expressions.Window
import org.apache.spark.sql.functions._
val result = orders.withColumn(
"position", row_number().over(
Window.partitionBy("customer_id").orderBy(col("total").asc(), col("order_id").asc())
)
).filter(col("position") === 1).select("order_id", "customer_id", "total").orderBy("customer_id")
result.show(false)
Keep a fixed number of rows
row_number with total descending and order_id ascending assigns a stable position to every order. Filtering the position keeps exactly the requested number when enough rows exist. Filtering rank instead can return extra rows at a tied cutoff. A global window is useful for this small example but sends all rows to one window partition.
PySpark example
from pyspark.sql import Window
from pyspark.sql.functions import col, row_number
result = orders.withColumn(
"position", row_number().over(Window.orderBy(col("total").desc(), col("order_id").asc()))
).filter(col("position") <= 2).select("order_id", "total", "position").orderBy("position")
result.show(truncate=False)
Scala example
import org.apache.spark.sql.expressions.Window
import org.apache.spark.sql.functions._
val result = orders.withColumn(
"position", row_number().over(Window.orderBy(col("total").desc(), col("order_id").asc()))
).filter(col("position") <= 2).select("order_id", "total", "position").orderBy("position")
result.show(false)