Business Analytics | Rossmann Retail

retail-forecast.

A promo lifts a Rossmann day by 38.8%

Daily sales analysis and forecasting for 1,115 Rossmann drugstores (844,392 open-day records, 2013-2015) in PySpark: eight charts on seasonality and promotions, then a Random Forest with per-store target encoding, tested honestly on the two months it has never seen.

Solo author1,115 stores · 2013-2015844,392 open-day rowsPySpark · 2 min run

844,392

open-day records · 1,115 stores · 2013-2015

+38.8%

average sales lift on promo days ($5,929 → $8,228)

$886

MAE per store-day on the unseen Jun-Jul 2015 window

0.837

R² · RMSE $1,256 · 58,611 test rows

Tech Stack

1PySpark
2Spark MLlib
3Random Forest
4Target Encoding
5StringIndexer + VectorAssembler
6Pandas
7Matplotlib
8Seaborn

The brief · problem → approach → result

01 / The problem

A drugstore chain plans staffing, logistics and stock per store per day. To do that well it needs to know what a typical day looks like, what promotions are worth, and how predictable daily sales really are from store attributes and the calendar alone.

02 / The approach

Take the public Rossmann Store Sales dataset (844,392 open-day records across 1,115 stores, 2013-2015), join the store attributes, read the seasonality in eight charts, then train a PySpark Random Forest with a per-store target encoding and test it on the two months it has never seen.

03 / The result

Promotions lift an ordinary day by 38.8% and the calendar gives the model its rhythm. On the unseen June-July 2015 window the model lands at MAE $886 per store-day (R² 0.837), with the store's own baseline dominating feature importance, followed by promotion and day-of-week.

The Pipeline

Load | Split | Encode | Evaluate|Index / 01
📥

Load & Join

1.1M rows ⋈ store table

🧱

Features

calendar parts from Date

✂️

Split

temporal: past → future

🎯

Encode

per-store average, train only

✅

Evaluate

MAE / RMSE / R² on unseen data

The split comes first and the encoding comes after it: the model only ever learns store averages from the training window, so no information from June and July 2015 can leak into the features. One-hot encoding 1,115 stores would blow up memory in Spark, so each store is represented by its historical average instead, with the global mean as a fallback for stores unseen in training.

What It Answers

Pattern | Promotion | Forecast|Index / 02
📅

Pattern

Both seasonalities are strong. Most stores close on Sundays, so demand piles up into a Monday spike, and every year peaks in November and December around Christmas shopping. The pattern repeats cleanly across 2013, 2014 and 2015.

RQ18 charts3 years of history
🏷️

Promotion

Promo days average $8,228 in sales against $5,929 without, a +38.8% lift on an ordinary day. The grouped chart shows the lift holding for every store type, so promotions are not just a big-store effect.

RQ2+38.8% liftAll store types
🌲

Forecast

Trained on everything before June 2015 and tested on the two unseen months after, the Random Forest with per-store target encoding reaches MAE $886, RMSE $1,256 and R² 0.837. Feature importance is led by the store baseline, then promotion and calendar.

RQ3MAE $88658,611 unseen rows

Honest By Construction

Leakage guards | Known limits|Index / 03

Guards that keep the test honest

  • 01Temporal split: train ends May 2015, test is the unseen June-July window.
  • 02Target encoding fit on the training window only, never the full data.
  • 03Stores unseen in training fall back to the global mean instead of nulls.
  • 04Categorical indexers are fitted on train, so the test set cannot add labels.

What the model cannot say

N

Not autoregressive

There are no lag features, so the model cannot use yesterday's sales. The natural upgrade is lags plus rolling validation.

A

A baseline, not a leaderboard entry

This is an end-to-end business analysis with a fixed feature set. Its metrics are not comparable to competition submissions that use extra data.

O

One model, no tuning

A 20-tree Random Forest with default-ish depth. It answers the business questions; squeezing the last percent of error was not the goal.

Built on the public Rossmann Store Sales competition dataset. The notebook is the single source of truth: research questions, method, figures, findings and limitations all live in one place, rerunnable top to bottom.

Inside The Notebook

One notebook, five sections|Index / 04
retail-demand-forecasting-rossmann-pyspark/
├── Research Questions          # 3 business questions up front
├── 1. Load & Join Data         # transactions ⋈ store attributes, open days
├── 2. Features                 # Year / Month / Day split from Date
├── 3. EDA & Visualization      # 8 charts: seasonality, promo, distribution
├── 4. Model                    # temporal split → target encoding → RF
└── 5. Evaluation & Conclusion  # metrics on unseen Jun-Jul 2015, answers

The Figures

Seasonality | Forecast | Drivers|Index / 05
Actual vs Predicted, Unseen Window
Fig. 1 · Actual vs Predicted, Unseen Window
Weekly Seasonality
Fig. 2 · Weekly Seasonality
Yearly Seasonality
Fig. 3 · Yearly Seasonality
Feature Importance
Fig. 4 · Feature Importance
Sales Distribution
Fig. 5 · Sales Distribution