| """ |
| M5-inspired dataset loader. |
| |
| Generates a long-format DataFrame with realistic retail demand patterns, |
| then delegates to synthetic.py to overlay luxury-specific signals. |
| Avoids Kaggle auth requirements so the demo works out-of-the-box. |
| """ |
|
|
| import numpy as np |
| import pandas as pd |
|
|
| from src.data.synthetic import apply_luxury_overlay |
|
|
| |
|
|
| CATEGORIES = { |
| "leather_goods": ["LG_001", "LG_002", "LG_003", "LG_004", "LG_005"], |
| "ready_to_wear": ["RTW_001", "RTW_002", "RTW_003"], |
| "accessories": ["ACC_001", "ACC_002", "ACC_003", "ACC_004"], |
| "footwear": ["FW_001", "FW_002", "FW_003"], |
| } |
|
|
| STORES = ["Paris_Flagship", "Tokyo_Ginza", "NYC_5thAve", "Dubai_Mall", "London_Bond"] |
|
|
| PRICE_MAP = { |
| "leather_goods": (1200, 4500), |
| "ready_to_wear": (800, 3200), |
| "accessories": (300, 1200), |
| "footwear": (600, 2000), |
| } |
|
|
|
|
| def generate_dataset( |
| start: str = "2021-01-01", |
| end: str = "2024-12-31", |
| seed: int = 42, |
| ) -> pd.DataFrame: |
| """ |
| Returns a long-format DataFrame with columns: |
| unique_id, ds, y, price, category, store, event_name |
| Ready for neuralforecast ingestion (unique_id + ds + y convention). |
| """ |
| rng = np.random.default_rng(seed) |
| dates = pd.date_range(start, end, freq="D") |
| records = [] |
|
|
| for category, items in CATEGORIES.items(): |
| p_low, p_high = PRICE_MAP[category] |
| for item_id in items: |
| for store in STORES: |
| unique_id = f"{item_id}_{store}" |
| base_demand = rng.integers(5, 40) |
| price = round(rng.uniform(p_low, p_high), -1) |
|
|
| sales = _base_demand_series(dates, base_demand, category, rng) |
|
|
| df_item = pd.DataFrame({ |
| "unique_id": unique_id, |
| "ds": dates, |
| "y": sales.clip(0), |
| "price": price, |
| "category": category, |
| "store": store, |
| "event_name": "", |
| }) |
| records.append(df_item) |
|
|
| df = pd.concat(records, ignore_index=True) |
| df = apply_luxury_overlay(df, rng) |
| df["y"] = df["y"].clip(0).round().astype(int) |
| return df |
|
|
|
|
| def _base_demand_series( |
| dates: pd.DatetimeIndex, |
| base: int, |
| category: str, |
| rng: np.random.Generator, |
| ) -> np.ndarray: |
| """ |
| Compose a realistic base demand curve: |
| - Weekly seasonality |
| - Annual seasonality (Q4 Christmas peak, summer trough) |
| - Slight upward trend |
| - Gaussian noise |
| """ |
| n = len(dates) |
| t = np.arange(n) |
|
|
| |
| trend = base + 0.003 * t |
|
|
| |
| annual = 0.25 * base * np.sin(2 * np.pi * (t - 60) / 365) |
|
|
| |
| weekly = 0.10 * base * np.sin(2 * np.pi * t / 7) |
|
|
| |
| noise = rng.normal(0, base * 0.15, n) |
|
|
| return trend + annual + weekly + noise |
|
|