| """ |
| Load and clean Kaggle restaurant sales data. |
| Accepts a CSV file path, parses and validates the data, returns a clean DataFrame. |
| """ |
| import argparse |
| from datetime import datetime |
| from pathlib import Path |
|
|
| import pandas as pd |
|
|
|
|
| |
| |
| |
| |
| |
| COLUMN_MAPPING = { |
| "date": "date", |
| "item_name": "item_name", |
| "quantity": "quantity", |
| "restaurant_id": "restaurant_id", |
| } |
|
|
| |
| FOOD_WASTE_MAPPING = { |
| "Date": "date", |
| "Food Name": "item_name", |
| "Quantity Purchased (kg)": "quantity", |
| "Location": "restaurant_id", |
| } |
|
|
|
|
| def _detect_and_remap(df: pd.DataFrame) -> pd.DataFrame: |
| """Auto-detect CSV format and remap columns to standard names.""" |
| cols = set(df.columns) |
|
|
| |
| if "Food Category" in cols and "Food Name" in cols: |
| print("[NutriLoop] Detected: Food Waste Tracker format") |
| mapping = FOOD_WASTE_MAPPING |
| else: |
| |
| mapping = {} |
| for k, v in COLUMN_MAPPING.items(): |
| |
| if v in df.columns: |
| mapping[v] = v |
| else: |
| |
| for col in df.columns: |
| if col.lower().strip() == k.lower(): |
| mapping[col] = v |
| break |
| |
| required = ["restaurant_id", "item_name", "quantity", "date"] |
| missing = [c for c in required if c not in mapping.values()] |
| if missing: |
| raise ValueError(f"Missing required columns: {missing}. Available: {list(df.columns)}") |
| return df.rename(columns=mapping) |
|
|
| |
| df = df.rename(columns=mapping) |
| return df |
|
|
|
|
| def load_kaggle_data(csv_path: str) -> pd.DataFrame: |
| """ |
| Load and clean Kaggle restaurant sales CSV. |
| |
| Args: |
| csv_path: Path to the Kaggle CSV file |
| |
| Returns: |
| Clean pandas DataFrame with columns: restaurant_id, item_name, quantity, sale_date |
| """ |
| print(f"[NutriLoop] Loading Kaggle data from {csv_path}") |
| path = Path(csv_path) |
|
|
| if not path.exists(): |
| raise FileNotFoundError(f"CSV file not found: {csv_path}") |
|
|
| |
| with open(path, "r", encoding="utf-8") as f: |
| first_line = f.readline() |
|
|
| if ";" in first_line: |
| delimiter = ";" |
| elif "," in first_line: |
| delimiter = "," |
| else: |
| delimiter = "," |
|
|
| print(f"[NutriLoop] Detected delimiter: '{delimiter}'") |
|
|
| |
| df = pd.read_csv(path, delimiter=delimiter) |
| print(f"[NutriLoop] Raw CSV columns: {list(df.columns)}") |
|
|
| |
| df = _detect_and_remap(df) |
|
|
| |
| print(f"[NutriLoop] Parsing dates (sample: {df['date'].iloc[0]})") |
| df["sale_date"] = pd.to_datetime(df["date"], errors="coerce") |
|
|
| |
| invalid_dates = df["sale_date"].isna().sum() |
| if invalid_dates > 0: |
| print(f"[NutriLoop] Dropping {invalid_dates} rows with invalid dates") |
| df = df.dropna(subset=["sale_date"]) |
|
|
| |
| df["quantity"] = pd.to_numeric(df["quantity"], errors="coerce").fillna(0) |
| df["quantity"] = (df["quantity"] * 1000).round().astype(int) |
|
|
| |
| before = len(df) |
| df = df.dropna(subset=["restaurant_id", "item_name"]) |
| after = len(df) |
| if before - after > 0: |
| print(f"[NutriLoop] Dropped {before - after} rows with null restaurant_id or item_name") |
|
|
| |
| df["restaurant_id"] = df["restaurant_id"].astype(str).str.strip() |
| df["item_name"] = df["item_name"].astype(str).str.strip() |
|
|
| |
| df = df.sort_values("sale_date") |
|
|
| print(f"[NutriLoop] Clean DataFrame: {len(df)} rows, date range: {df['sale_date'].min()} to {df['sale_date'].max()}") |
| print(f"[NutriLoop] Unique restaurants: {df['restaurant_id'].nunique()}") |
| print(f"[NutriLoop] Unique items: {df['item_name'].nunique()}") |
|
|
| |
| return df[["restaurant_id", "item_name", "quantity", "sale_date"]] |
|
|
|
|
| if __name__ == "__main__": |
| parser = argparse.ArgumentParser(description="Load and inspect Kaggle restaurant sales data") |
| parser.add_argument("csv_path", help="Path to the Kaggle CSV file") |
| parser.add_argument("--head", type=int, default=20, help="Number of rows to display") |
| args = parser.parse_args() |
|
|
| df = load_kaggle_data(args.csv_path) |
| print("\nFirst rows:") |
| print(df.head(args.head).to_string(index=False)) |
| print(f"\nTotal rows: {len(df)}") |