Data Analytics project 08
Sales Forecasting Data Analytics Project
Build a complete sales forecasting portfolio project with documented metrics, reproducible Python code, validation checks, and responsible interpretation.
Explore Data Analytics training in VizagView all project ideas
Business question
Do lag and rolling features improve final-period forecast error relative to a same-weekday baseline?
Dataset and grain
A seeded 540-day synthetic daily revenue series with trend, weekly and monthly seasonality, and noise.
Requirements
Python 3.10 or later with numpy, pandas, and scikit-learn installed.
Method and validation checks
Create only prior-day features, hold out the final 20 percent in chronological order, fit on earlier dates, and compare holdout MAE against lag seven.
- The split is chronological and never shuffled
- Rolling averages are shifted before calculation
- The baseline is evaluated on the same holdout rows
- MAE is reported in revenue units
Complete Python code
Save the code as da_sales_forecasting.py. Review the stated model and field assumptions before using another dataset.
"""Time-ordered sales forecasting with a seasonal baseline."""
from __future__ import annotations
import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_absolute_error
def make_demo_sales(days: int = 540, seed: int = 42) -> pd.DataFrame:
rng = np.random.default_rng(seed)
x = np.arange(days)
revenue = 120_000 + 95 * x + 17_000 * np.sin(2 * np.pi * x / 7) + 8_000 * np.sin(2 * np.pi * x / 30) + rng.normal(0, 6_500, days)
return pd.DataFrame({"date": pd.date_range("2024-07-01", periods=days), "revenue": np.maximum(revenue, 0)})
def build_features(data: pd.DataFrame) -> pd.DataFrame:
frame = data.copy().sort_values("date")
frame["day_of_week"] = pd.to_datetime(frame["date"]).dt.dayofweek
for lag in (1, 7, 14, 28):
frame[f"lag_{lag}"] = frame["revenue"].shift(lag)
frame["rolling_28"] = frame["revenue"].shift(1).rolling(28).mean()
return frame.dropna().reset_index(drop=True)
def evaluate_forecast(data: pd.DataFrame) -> dict[str, float]:
frame = build_features(data)
split = int(len(frame) * 0.8)
train, test = frame.iloc[:split], frame.iloc[split:]
features = ["day_of_week", "lag_1", "lag_7", "lag_14", "lag_28", "rolling_28"]
model = RandomForestRegressor(n_estimators=180, min_samples_leaf=3, random_state=42, n_jobs=-1)
model.fit(train[features], train["revenue"])
prediction = model.predict(test[features])
return {"model_mae": float(mean_absolute_error(test["revenue"], prediction)),
"lag_7_baseline_mae": float(mean_absolute_error(test["revenue"], test["lag_7"])),
"test_days": float(len(test))}
def main() -> None:
print({k: round(v, 2) for k, v in evaluate_forecast(make_demo_sales()).items()})
print("The final 20% is held out chronologically; all features use prior observations.")
if __name__ == "__main__":
main()
Build and run the project
Run python da_sales_forecasting.py to compare a random-forest forecast with a seven-day seasonal baseline.
- Confirm the source grain and field definitions.
- Reconcile record counts and additive totals.
- Validate rate denominators and date filters.
- Review outliers and missing values.
- Read the interpretation limits before sharing conclusions.
Expected analytical output
Model MAE, seven-day-baseline MAE, and the number of held-out days.
Interpretation and responsible-use limits
Synthetic fit is not a business forecast. Real forecasting needs event and promotion calendars, anomalies, prediction intervals, backtesting windows, revisions, and monitoring.
Ways to extend the project
Add walk-forward validation, holiday and promotion features, quantile intervals, hierarchy reconciliation, drift alerts, and forecast-versus-actual reporting.
Continue learning Data Analytics
Try the next project, return to the Softenant project library, or explore the Data Analytics course in Vizag for guided SQL, Excel, Power BI, Python, dashboard, and portfolio practice.