Window functions let you do calculations across rows related to the current row without grouping data. This helps keep all rows while adding useful info.
Window functions in Apache Spark
from pyspark.sql.window import Window from pyspark.sql.functions import row_number, rank, sum windowSpec = Window.partitionBy('category').orderBy('value') # Example: Add row number within each category result = df.withColumn('row_num', row_number().over(windowSpec))
partitionBy splits data into groups (like categories).
orderBy defines the order inside each group for calculations.
from pyspark.sql.window import Window from pyspark.sql.functions import row_number windowSpec = Window.partitionBy('department').orderBy('salary') df.withColumn('row_num', row_number().over(windowSpec))
from pyspark.sql.window import Window from pyspark.sql.functions import sum windowSpec = Window.partitionBy('store').orderBy('date').rowsBetween(Window.unboundedPreceding, 0) df.withColumn('running_total', sum('sales').over(windowSpec))
from pyspark.sql.window import Window from pyspark.sql.functions import rank windowSpec = Window.partitionBy('region').orderBy('profit') df.withColumn('rank', rank().over(windowSpec))
This program creates sales data for two categories over dates. It adds a running total of sales per category ordered by date and a row number for each row within the category.
from pyspark.sql import SparkSession from pyspark.sql.window import Window from pyspark.sql.functions import row_number, sum spark = SparkSession.builder.appName('WindowFunctionsExample').getOrCreate() data = [ ('A', '2024-01-01', 100), ('A', '2024-01-02', 150), ('A', '2024-01-03', 200), ('B', '2024-01-01', 50), ('B', '2024-01-02', 80) ] columns = ['category', 'date', 'sales'] df = spark.createDataFrame(data, columns) windowSpec = Window.partitionBy('category').orderBy('date').rowsBetween(Window.unboundedPreceding, 0) result = df.withColumn('running_total', sum('sales').over(windowSpec)) result = result.withColumn('row_num', row_number().over(Window.partitionBy('category').orderBy('date'))) result.show()
Window functions do not reduce the number of rows like groupBy does.
Use partitionBy to define groups, and orderBy to define order inside groups.
Common window functions include row_number(), rank(), sum(), avg(), and lag().
Window functions let you add calculations across related rows without grouping.
They keep all rows and add new columns with useful info like running totals or ranks.
Use partitionBy and orderBy to control how data is grouped and ordered for these calculations.
Practice
Solution
Step 1: Understand window function behavior
Window functions calculate values across related rows but keep all original rows intact.Step 2: Compare with grouping
Grouping reduces rows by aggregation, but window functions add columns without reducing rows.Final Answer:
To perform calculations across rows related to the current row without collapsing the data -> Option AQuick Check:
Window functions add calculations without grouping ≠ A [OK]
- Confusing window functions with groupBy aggregations
- Thinking window functions filter rows
- Assuming window functions join datasets
Solution
Step 1: Recall PySpark window spec syntax
The correct methods are partitionBy() and orderBy() chained on Window.Step 2: Check method order and names
partitionBy comes first, then orderBy; groupBy and sortBy are incorrect here.Final Answer:
Window.partitionBy('department').orderBy('salary') -> Option CQuick Check:
Use partitionBy then orderBy methods [OK]
- Using groupBy instead of partitionBy
- Using sortBy instead of orderBy
- Using incorrect method names like partition or order
from pyspark.sql import SparkSession
from pyspark.sql.window import Window
from pyspark.sql.functions import rank
spark = SparkSession.builder.getOrCreate()
data = [("Alice", "Sales", 5000), ("Bob", "Sales", 4000), ("Charlie", "Sales", 5000)]
df = spark.createDataFrame(data, ["name", "department", "salary"])
windowSpec = Window.partitionBy("department").orderBy("salary")
df = df.withColumn("rank", rank().over(windowSpec))
df.orderBy("salary").show()Solution
Step 1: Understand rank() behavior
Rank assigns the same rank to ties and skips ranks after ties.Step 2: Apply rank over salary ascending
Bob has salary 4000 ranked 1; Alice and Charlie both have 5000, so both get rank 2.Final Answer:
[ ("Bob", 4000, 1), ("Alice", 5000, 2), ("Charlie", 5000, 2) ] -> Option AQuick Check:
rank() ties share rank, next rank skips [OK]
- Confusing rank() with dense_rank()
- Assigning different ranks to tied values
- Not ordering data before applying rank
from pyspark.sql.window import Window
from pyspark.sql.functions import row_number
windowSpec = Window.partitionBy('department')
df = df.withColumn('row_num', row_number().over(windowSpec.orderBy('salary')))Solution
Step 1: Check windowSpec usage
windowSpec is a WindowSpec object; calling orderBy on it is valid and returns a new WindowSpec.Step 2: Confirm chaining
Chaining orderBy on windowSpec is allowed to refine the window specification.Final Answer:
windowSpec.orderBy('salary') is valid; no error in code -> Option BQuick Check:
WindowSpec supports chaining orderBy to refine window [OK]
- Calling orderBy on windowSpec instead of Window
- Assuming row_number can't be used with partitionBy
- Forgetting to import Window or functions
Solution
Step 1: Define window for 3-day moving average per store
Partition by 'store' to separate stores, order by 'date' for time sequence, use rowsBetween(-2, 0) for current and previous 2 rows.Step 2: Apply avg() over this window
Use avg('sales').over(windowSpec) to compute moving average over the defined window.Final Answer:
Use Window.partitionBy('store').orderBy('date').rowsBetween(-2, 0) and avg('sales').over(windowSpec) -> Option DQuick Check:
rowsBetween(-2,0) for 3-day window, partition by store [OK]
- Using rangeBetween instead of rowsBetween for row count
- Not partitioning by store
- Using wrong rowsBetween range (like 0 to 2)
Ready to prove it?
Pass the test and this pattern moves unknown
