Spaces:
Sleeping
Sleeping
| from __future__ import annotations | |
| from pathlib import Path | |
| from typing import Iterable | |
| import numpy as np | |
| import pandas as pd | |
| NUMERIC_FEATURES = [ | |
| "hour_of_day", | |
| "day_of_week", | |
| "is_weekend", | |
| "is_festival", | |
| "inventory_level", | |
| "inventory_days_cover", | |
| "competitor_price", | |
| "click_through_rate", | |
| "conversion_rate", | |
| "units_sold_last_5m", | |
| "units_sold_last_1h", | |
| "base_cost", | |
| "current_price", | |
| "demand_index", | |
| "inventory_pressure", | |
| "competitor_gap", | |
| ] | |
| CATEGORICAL_FEATURES = ["category", "brand", "customer_segment"] | |
| TARGET_COLUMN = "optimal_price" | |
| KAGGLE_RETAIL_NUMERIC_FEATURES = [ | |
| "qty", | |
| "freight_price", | |
| "product_name_lenght", | |
| "product_description_lenght", | |
| "product_photos_qty", | |
| "product_weight_g", | |
| "product_score", | |
| "customers", | |
| "weekday", | |
| "weekend", | |
| "holiday", | |
| "volume", | |
| "comp_1", | |
| "ps1", | |
| "fp1", | |
| "comp_2", | |
| "ps2", | |
| "fp2", | |
| "comp_3", | |
| "ps3", | |
| "fp3", | |
| "lag_price", | |
| "month", | |
| "year", | |
| ] | |
| KAGGLE_RETAIL_CATEGORICAL_FEATURES = ["product_id", "product_category_name"] | |
| KAGGLE_RETAIL_TARGET_COLUMN = "unit_price" | |
| def add_derived_features(frame: pd.DataFrame) -> pd.DataFrame: | |
| enriched = frame.copy() | |
| enriched["demand_index"] = ( | |
| enriched["units_sold_last_1h"] * 0.55 | |
| + enriched["units_sold_last_5m"] * 0.35 | |
| + enriched["conversion_rate"] * 100 * 0.10 | |
| ) | |
| enriched["inventory_pressure"] = np.where( | |
| enriched["inventory_level"] <= 20, | |
| 1.25, | |
| np.where(enriched["inventory_level"] <= 60, 1.05, 0.92), | |
| ) | |
| enriched["competitor_gap"] = ( | |
| enriched["current_price"] - enriched["competitor_price"] | |
| ) / enriched["competitor_price"].clip(lower=1.0) | |
| return enriched | |
| def load_training_data(path: Path) -> pd.DataFrame: | |
| frame = pd.read_csv(path) | |
| return add_derived_features(frame) | |
| def load_kaggle_retail_training_data(path: Path) -> pd.DataFrame: | |
| frame = pd.read_csv(path) | |
| if "month_year" in frame.columns: | |
| parsed_month_year = pd.to_datetime(frame["month_year"], dayfirst=True, errors="coerce") | |
| if "month" not in frame.columns: | |
| frame["month"] = parsed_month_year.dt.month | |
| if "year" not in frame.columns: | |
| frame["year"] = parsed_month_year.dt.year | |
| numeric_defaults = [ | |
| "comp_1", | |
| "ps1", | |
| "fp1", | |
| "comp_2", | |
| "ps2", | |
| "fp2", | |
| "comp_3", | |
| "ps3", | |
| "fp3", | |
| "lag_price", | |
| ] | |
| for column in numeric_defaults: | |
| if column not in frame.columns: | |
| frame[column] = np.nan | |
| if "volume" not in frame.columns: | |
| frame["volume"] = ( | |
| frame.get("product_name_lenght", 0).fillna(0) | |
| * frame.get("product_description_lenght", 0).fillna(0) | |
| * frame.get("product_photos_qty", 0).fillna(0).clip(lower=1) | |
| ) | |
| if "weekday" not in frame.columns: | |
| frame["weekday"] = 0 | |
| if "weekend" not in frame.columns: | |
| frame["weekend"] = 0 | |
| if "holiday" not in frame.columns: | |
| frame["holiday"] = 0 | |
| ensure_columns( | |
| frame, | |
| KAGGLE_RETAIL_NUMERIC_FEATURES | |
| + KAGGLE_RETAIL_CATEGORICAL_FEATURES | |
| + [KAGGLE_RETAIL_TARGET_COLUMN], | |
| ) | |
| return frame | |
| def split_xy( | |
| frame: pd.DataFrame, | |
| numeric_features: list[str] | None = None, | |
| categorical_features: list[str] | None = None, | |
| target_column: str = TARGET_COLUMN, | |
| ) -> tuple[pd.DataFrame, pd.Series]: | |
| selected_numeric = numeric_features or NUMERIC_FEATURES | |
| selected_categorical = categorical_features or CATEGORICAL_FEATURES | |
| features = frame[selected_numeric + selected_categorical].copy() | |
| target = frame[target_column].copy() | |
| return features, target | |
| def ensure_columns(frame: pd.DataFrame, required_columns: Iterable[str]) -> None: | |
| missing = [column for column in required_columns if column not in frame.columns] | |
| if missing: | |
| raise ValueError(f"Missing required columns: {missing}") | |