ML Engineer MasterClass (October) | 4 seats left

Spark · Running totals
AmazonAmazon Analytics
Amazon · Window Functions

Running totals

Accumulate values with explicit row frames and partition-specific resets.

Step 1 of 6 · Learn

Accumulate through the current row

sum over an ordered window adds values from the start through the current row. rowsBetween(unboundedPreceding, currentRow) makes that frame explicit. The example processes order_id ascending. Reversing the window order changes which rows have already contributed, even if the final table remains sorted ascending. A global window is appropriate here only because the teaching dataset is small.

Lesson reference: PySpark and Scala

Accumulate through the current row

sum over an ordered window adds values from the start through the current row. rowsBetween(unboundedPreceding, currentRow) makes that frame explicit. The example processes order_id ascending. Reversing the window order changes which rows have already contributed, even if the final table remains sorted ascending. A global window is appropriate here only because the teaching dataset is small.

PySpark example

from pyspark.sql import Window
from pyspark.sql.functions import col, sum

result = orders.withColumn(
    "running_total", sum("total").over(
        Window.orderBy(col("order_id").asc())
        .rowsBetween(Window.unboundedPreceding, Window.currentRow)
    )
).select("order_id", "status", "customer_id", "total", "running_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(
    "running_total", sum("total").over(
        Window.orderBy(col("order_id").asc())
        .rowsBetween(Window.unboundedPreceding, Window.currentRow)
    )
).select("order_id", "status", "customer_id", "total", "running_total").orderBy("order_id")
result.show(false)

Restart for each group

partitionBy(status) keeps a separate accumulator for each status. Interleaved rows of other statuses do not contribute. Within each group, order_id determines the sequence and the explicit row frame includes the current row. Changing the partition key changes where totals restart.

PySpark example

from pyspark.sql import Window
from pyspark.sql.functions import col, sum

result = orders.withColumn(
    "running_total", sum("total").over(
        Window.partitionBy("status").orderBy(col("order_id").asc())
        .rowsBetween(Window.unboundedPreceding, Window.currentRow)
    )
).select("order_id", "status", "customer_id", "total", "running_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(
    "running_total", sum("total").over(
        Window.partitionBy("status").orderBy(col("order_id").asc())
        .rowsBetween(Window.unboundedPreceding, Window.currentRow)
    )
).select("order_id", "status", "customer_id", "total", "running_total").orderBy("order_id")
result.show(false)

Count qualifying rows so far

count(*) over the same row frame counts every row encountered. A conditional sum can count only matching rows without removing the other rows from the result. Use 1 for matches and 0 otherwise. Filtering the DataFrame first would remove rows that should remain visible.

PySpark example

from pyspark.sql import Window
from pyspark.sql.functions import count

result = orders.withColumn(
    "running_count", count("*").over(
        Window.orderBy("order_id").rowsBetween(Window.unboundedPreceding, Window.currentRow)
    )
).select("order_id", "status", "running_count").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(
    "running_count", count("*").over(
        Window.orderBy("order_id").rowsBetween(Window.unboundedPreceding, Window.currentRow)
    )
).select("order_id", "status", "running_count").orderBy("order_id")
result.show(false)
example.pyPySpark
Ordered rowsGrow the row frameAccumulate values
Window sequenceOrder IDs in window order1001 → 1002 → 1003 → 1004 → 1005 → 1006
Source orders6 rows
order_idcustomer_idstatustotalitem_count
1001101Delivered89.52
1002102Shipped1493
1003101Cancelled351
1004103Delivered219.994
1005104Delivered49.991
1006105Shipped1202
Accumulate through the current row
Result6 rows
order_idstatuscustomer_idtotalrunning_total
1001Delivered10189.589.5
1002Shipped102149238.5
1003Cancelled10135273.5
1004Delivered103219.99493.49
1005Delivered10449.99543.48
1006Shipped105120663.48
Order 1002 includes 1001 and 1002: 89.50 + 149.00 = 238.50. The final row includes all six orders.

The example is loaded in the editor. Run it as written, then try a small change.

Runs on the Spark backend. First startup may take a moment.

Run your code to see the result.