NutriLoop / training /load_kaggle_data.py
AB1N05's picture
Nutriloop V2 Backend - Global Model Architected
1fa55f4
Raw
History Blame Contribute Delete
5.08 kB
"""
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
# Kaggle "Restaurant Sales" / food waste tracker column mapping
# Expected columns: date, item_name, quantity, restaurant_id
# Auto-detects both formats:
# - Restaurant Sales: date, item_name, quantity, restaurant_id
# - Food Waste Tracker: Date, Food Name, Quantity Purchased (kg), Location
COLUMN_MAPPING = {
"date": "date",
"item_name": "item_name",
"quantity": "quantity",
"restaurant_id": "restaurant_id",
}
# Food Waste Tracker specific mapping (detected by "Food Category" column)
FOOD_WASTE_MAPPING = {
"Date": "date",
"Food Name": "item_name",
"Quantity Purchased (kg)": "quantity",
"Location": "restaurant_id", # Treat each location as a "restaurant"
}
def _detect_and_remap(df: pd.DataFrame) -> pd.DataFrame:
"""Auto-detect CSV format and remap columns to standard names."""
cols = set(df.columns)
# Detect Food Waste Tracker format
if "Food Category" in cols and "Food Name" in cols:
print("[NutriLoop] Detected: Food Waste Tracker format")
mapping = FOOD_WASTE_MAPPING
else:
# Standard column mapping
mapping = {}
for k, v in COLUMN_MAPPING.items():
# Direct match
if v in df.columns:
mapping[v] = v
else:
# Case-insensitive match
for col in df.columns:
if col.lower().strip() == k.lower():
mapping[col] = v
break
# Check required columns
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)
# Apply Food Waste 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}")
# Try to detect the delimiter
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}'")
# Read CSV
df = pd.read_csv(path, delimiter=delimiter)
print(f"[NutriLoop] Raw CSV columns: {list(df.columns)}")
# Auto-detect format and remap columns
df = _detect_and_remap(df)
# Parse dates
print(f"[NutriLoop] Parsing dates (sample: {df['date'].iloc[0]})")
df["sale_date"] = pd.to_datetime(df["date"], errors="coerce")
# Drop rows with invalid dates
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"])
# Ensure quantity is numeric (kg floats from food waste data → grams as int)
df["quantity"] = pd.to_numeric(df["quantity"], errors="coerce").fillna(0)
df["quantity"] = (df["quantity"] * 1000).round().astype(int) # kg → grams
# Drop rows with null restaurant_id or item_name
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")
# Clean strings
df["restaurant_id"] = df["restaurant_id"].astype(str).str.strip()
df["item_name"] = df["item_name"].astype(str).str.strip()
# Sort by date
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 only required columns
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)}")