Grocery / data_adapter.py
ukzada's picture
Upload 6 files
db72db0 verified
Raw
History Blame Contribute Delete
3.62 kB
"""Utilities to convert Pakistan_Food_Prices_2025.csv into the app's internal schema."""
from pathlib import Path
import pandas as pd
CATEGORY_MAP = {
"Dairy": "Dairy",
"Fruit": "Fruits",
"Grain": "Grains",
"Pulses": "Grains",
"Meat": "Protein",
"Vegetable": "Vegetables",
"Oil": "Snacks",
"Beverage": "Snacks",
"Condiment": "Snacks",
}
def infer_diet_type(item: str, raw_category: str) -> str:
"""Infer a diet label so the existing app UX/model interface can remain unchanged."""
item_lower = str(item).lower()
raw_category = str(raw_category)
if any(token in item_lower for token in ["beef", "mutton", "pomfret"]):
return "Restricted"
if any(token in item_lower for token in ["chicken", "fish", "rohu"]):
return "Keto"
if raw_category in {"Vegetable", "Pulses"}:
return "Diabetic"
if any(token in item_lower for token in ["sugar", "ghee", "oil"]):
return "Restricted"
if raw_category in {"Fruit", "Dairy"}:
return "Normal"
return "All"
def build_app_dataset_from_pakistan_csv(csv_path: Path) -> pd.DataFrame:
"""Build internal dataset schema from raw Pakistan food price data.
Preprocessing steps:
1. Load raw CSV and validate required columns.
2. Drop unused columns for modeling (City, Month, Source).
3. Clean text fields (Item, Category).
4. Convert price to numeric and filter invalid values.
5. Aggregate duplicates by item/category using median price.
6. Map categories to app categories and infer diet labels.
"""
raw_df = pd.read_csv(csv_path)
required = {"Item", "Category", "Price_per_Kg"}
missing = required - set(raw_df.columns)
if missing:
raise ValueError(f"Missing required columns in raw dataset: {sorted(missing)}")
# Keep only columns needed for recommendation objective.
df = raw_df[["Item", "Category", "Price_per_Kg"]].copy()
df = df.dropna(subset=["Item", "Category", "Price_per_Kg"])
# Standardize text for stable grouping and label inference.
df["Item"] = df["Item"].astype(str).str.strip()
df["Category"] = df["Category"].astype(str).str.strip()
df = df[(df["Item"] != "") & (df["Category"] != "")]
# Price cleanup.
df["Price_per_Kg"] = pd.to_numeric(df["Price_per_Kg"], errors="coerce")
df = df.dropna(subset=["Price_per_Kg"])
df = df[df["Price_per_Kg"] > 0]
# Aggregate duplicate products across cities/months/sources by median price.
grouped = (
df.groupby(["Item", "Category"], as_index=False)["Price_per_Kg"]
.median()
.rename(columns={"Price_per_Kg": "price"})
)
# Mild outlier control using IQR clipping to keep model robust.
q1 = grouped["price"].quantile(0.25)
q3 = grouped["price"].quantile(0.75)
iqr = q3 - q1
if iqr > 0:
lower = max(0.0, q1 - 1.5 * iqr)
upper = q3 + 1.5 * iqr
grouped["price"] = grouped["price"].clip(lower=lower, upper=upper)
product = grouped["Item"]
category = grouped["Category"].map(CATEGORY_MAP).fillna("Snacks")
price = grouped["price"].astype(float)
diet_type = [infer_diet_type(item, cat) for item, cat in zip(grouped["Item"], grouped["Category"])]
out = pd.DataFrame(
{
"product": product,
"category": category,
"price": price,
"diet_type": diet_type,
}
)
out = out.drop_duplicates(subset=["product"]).reset_index(drop=True)
return out