| """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)}")
|
|
|
|
|
| df = raw_df[["Item", "Category", "Price_per_Kg"]].copy()
|
| df = df.dropna(subset=["Item", "Category", "Price_per_Kg"])
|
|
|
|
|
| df["Item"] = df["Item"].astype(str).str.strip()
|
| df["Category"] = df["Category"].astype(str).str.strip()
|
| df = df[(df["Item"] != "") & (df["Category"] != "")]
|
|
|
|
|
| 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]
|
|
|
|
|
| grouped = (
|
| df.groupby(["Item", "Category"], as_index=False)["Price_per_Kg"]
|
| .median()
|
| .rename(columns={"Price_per_Kg": "price"})
|
| )
|
|
|
|
|
| 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
|
|
|