GCC Supermarket AI

Five scikit-learn models for supermarket analytics in a GCC (Kuwait-oriented) setting: daily product sales, a High/Medium/Low demand class, customer basket value, stockout risk and promotion response. The script supermarket_ai.py builds a synthetic transaction log of 50,000 transactions, covering 200 products, 8,000 customers, 12 categories and 2 years starting 2023-01-01. It applies a hand-coded GCC calendar (Ramadan, Eid, Friday/Saturday weekend, summer heat, month-end) and engineers aggregate features. It then compares five algorithms per task with 3-fold cross-validation and saves the best models, label encoders and sample CSVs. These are demonstration models trained on simulated data.

Author: AgenThink, Kuwait City

All files are in the gcc_supermarket_ai/ subfolder of this repository.

Models

Test metrics are on a 20% held-out split (random_state=42). CV is the mean 3-fold cross-validation score on the 80% training split. All numbers come from automl_results.json.

# File Task Type Selected algorithm CV score Test score Test MAE
1 model_sales_forecast.pkl Daily product quantity (sales forecast) Regression GradientBoosting R² 0.2810 R² 0.28 1.929 units
2 model_demand_class.pkl Demand class (High / Medium / Low) Classification (3 classes) ExtraTrees Acc 0.5456 Acc 0.544 –
3 model_basket_value.pkl Customer basket value Regression ExtraTrees R² 0.9969 R² 0.999 ⚠️ 0.304 KWD
4 model_stockout_risk.pkl Stockout risk Classification DecisionTree Acc 0.9998 Acc 1.0 ⚠️ –
5 model_promo_response.pkl Promotion response (quantity > 2) Classification ExtraTrees Acc 0.6848 Acc 0.682 –

The candidates were RandomForest, GradientBoosting, ExtraTrees and DecisionTree (100 trees and depth 12 for the forests; depth 5 for GradientBoosting; depth 10 for DecisionTree), plus Ridge (α=10) for regression or LogisticRegression for classification. Every candidate's CV score is in all_scores. The saved estimator classes match the best_algo values in the JSON.

⚠️ How to read these scores:

  • Stockout risk (1.0 accuracy) is true by construction. The label is stock_level / (roll7_qty/7 + 0.01) < 3. Both stock_level and roll7_qty are model inputs, so the model only relearns a deterministic rule.
  • Basket value (R² 0.999) is close to deterministic. The target is the sum of price × quantity in the basket, and the inputs include basket_items (total quantity) and avg_price.
  • Sales forecast and demand class use rolling features that include the current day. roll7_qty, roll30_qty and roll7_sales are computed with rolling(..., min_periods=1) over each product's rows, and the window includes the day being predicted. These scores therefore do not measure true forward-looking forecasting. The rolling windows also count rows (days on which the product sold), not calendar days.
  • The promotion response label does not depend on the discount. The generator draws quantity from Poisson(2.2 × segment multiplier × seasonality), so the discount has no effect on quantity. The model can only learn segment and seasonality effects, not promotion lift.

Dashboard

GCC Supermarket AI dashboard

The dashboard shows model scores, the monthly sales trend, sales by category, actual vs. predicted quantities, the demand confusion matrix, basket value distributions, feature importances, weekend vs. weekday sales, basket value by segment, the algorithm heatmap and monthly seasonality.

Repository files

File (in gcc_supermarket_ai/) Description
supermarket_ai.py Full pipeline: synthetic data, feature engineering, AutoML, dashboard, and saving of models, encoders and CSVs
automl_results.json For each model: type, selected algorithm, CV and test metrics, CV scores of all candidates, feature list and feature importances
model_sales_forecast.pkl GradientBoostingRegressor (about 0.4 MB)
model_demand_class.pkl ExtraTreesClassifier (about 18 MB)
model_basket_value.pkl ExtraTreesRegressor (about 21 MB)
model_stockout_risk.pkl DecisionTreeClassifier (about 3 KB)
model_promo_response.pkl ExtraTreesClassifier (about 12 MB)
encoder_category.pkl LabelEncoder for product category, producing cat_enc
encoder_segment.pkl LabelEncoder for customer segment, producing seg_enc
encoder_demand.pkl LabelEncoder for the demand label (decodes model_demand_class output)
gcc_supermarket_sample_data.csv 5,000 randomly sampled synthetic transactions
gcc_daily_product_aggregates.csv First 2,000 rows of the daily product aggregate table. These cover product IDs 0–9 only, because the table is sorted by product and date
gcc_supermarket_ai_dashboard.png Dashboard image (about 0.6 MB)

All pickles were saved with joblib and record scikit-learn 1.8.0 as the training version.

Training data

All data is synthetic, generated by supermarket_ai.py with np.random.seed(42).

  • Catalogue. There are 12 categories, each with a hand-set sampling weight, average price, margin and shelf life: Fresh Produce, Meat & Poultry, Dairy & Eggs, Bakery, Beverages, Snacks & Confectionery, Frozen Foods, Pantry & Dry Goods, Personal Care, Household, Arabic Spices & Herbs, and International Foods. Product prices are the category average × U(0.5, 2.0).

  • Customers. There are six segments with fixed shares: Premium 12%, Regular 38%, Budget 25%, Expat-Western 10%, Expat-Asian 10%, Wholesale 5%. Each segment has a quantity multiplier; Wholesale is 4.5×.

  • Calendar multipliers, applied to quantity only; transaction dates are drawn uniformly:

    • Ramadan ×1.65, using a fixed window of Mar 10–Apr 20 in both years
    • Eid al-Fitr ×2.1 (Apr 20–28)
    • Eid al-Adha ×1.85 (Jun 16–23)
    • July–August ×0.78
    • Kuwait National Day window ×1.45 (Feb 24–27)
    • Friday ×1.35, Saturday ×1.28, Monday ×0.88
    • Day 27 or later of the month ×1.15

    These fixed Gregorian windows only approximate the lunar Islamic calendar and do not match the actual 2023/2024 dates.

  • Other fields. 22% of lines are on promotion, with a 5–35% discount. stock_level is uniform in 0–499 and is not linked to sales. Temperature is a monthly mean plus noise.

CSV columns

  • gcc_supermarket_sample_data.csv has one row per synthetic transaction line. Columns: transaction_id, date, year, month, day_of_week (0 = Mon), is_weekend (Fri/Sat), is_ramadan, is_eid, season_multiplier, temperature_c, customer_id, customer_segment, customer_freq (visits per year), product_id, category, base_price, actual_price, discount_pct, on_promotion, quantity, sales_value, gross_profit, stock_level, days_to_reorder, shelf_life_days, is_perishable (shelf life ≤ 14 days), and month_year (a helper column added for the dashboard).
  • gcc_daily_product_aggregates.csv has one row per (date, product). Columns: date, product_id, daily_qty, daily_sales, txn_count, roll7_qty, roll30_qty, roll7_sales, demand_label. demand_label is Low, Medium or High, split at the 33rd and 66th percentiles of daily_qty.

Features per model

The order matches features in the JSON and model.feature_names_in_.

  1. Sales forecast: target daily_qty. Features: roll7_qty, roll30_qty, roll7_sales, on_promotion, avg_price, is_weekend, is_ramadan, is_eid, temperature_c, season_multiplier, cat_enc
  2. Demand class: target demand label. Features: roll7_qty, roll30_qty, on_promotion, avg_price, is_weekend, is_ramadan, is_eid, temperature_c, cat_enc
  3. Basket value: target basket_value (sum of sales per customer per day). Features: basket_items, basket_products, on_promo_items, avg_price, customer_freq, is_weekend, is_ramadan, is_eid, temperature_c, season_multiplier, seg_enc
  4. Stockout risk: target 1 if the estimated days of stock is below 3. Features: stock_level, days_to_reorder, roll7_qty, shelf_life_days, is_perishable, is_weekend, is_ramadan, is_eid, temperature_c, cat_enc
  5. Promotion response: target 1 if quantity > 2 on a promoted line. Trained on promoted lines only. Features: base_price, discount_pct, stock_level, customer_freq, is_weekend, is_ramadan, is_eid, temperature_c, season_multiplier, cat_enc, seg_enc

Encoders. LabelEncoder sorts classes alphabetically:

  • encoder_category: Arabic Spices & Herbs=0, Bakery=1, Beverages=2, Dairy & Eggs=3, Fresh Produce=4, Frozen Foods=5, Household=6, International Foods=7, Meat & Poultry=8, Pantry & Dry Goods=9, Personal Care=10, Snacks & Confectionery=11.
  • encoder_segment: Budget=0, Expat-Asian=1, Expat-Western=2, Premium=3, Regular=4, Wholesale=5.
  • encoder_demand: High=0, Low=1, Medium=2. The "classes": ["Low","Medium","High"] list in automl_results.json is not the integer order. Always decode predictions with encoder_demand.inverse_transform.

How to use

# pip install scikit-learn==1.8.* pandas joblib huggingface_hub
import joblib, pandas as pd
from huggingface_hub import hf_hub_download

repo, sub = "agenthinkmesh/gcc_supermarket_ai", "gcc_supermarket_ai/"
load = lambda f: joblib.load(hf_hub_download(repo, sub + f))

le_cat, le_seg, le_dem = load("encoder_category.pkl"), load("encoder_segment.pkl"), load("encoder_demand.pkl")

# Demand class (High / Medium / Low)
demand_model = load("model_demand_class.pkl")
X = pd.DataFrame([{
    "roll7_qty": 6.0, "roll30_qty": 5.0, "on_promotion": 1.0, "avg_price": 4.5,
    "is_weekend": 1, "is_ramadan": 1, "is_eid": 0, "temperature_c": 26.0,
    "cat_enc": le_cat.transform(["Dairy & Eggs"])[0],
}], columns=demand_model.feature_names_in_)
print(le_dem.inverse_transform(demand_model.predict(X)))

# Promotion response (probability that quantity > 2)
promo_model = load("model_promo_response.pkl")
X = pd.DataFrame([{
    "base_price": 6.0, "discount_pct": 0.2, "stock_level": 250, "customer_freq": 12.0,
    "is_weekend": 1, "is_ramadan": 0, "is_eid": 0, "temperature_c": 30.0,
    "season_multiplier": 1.35, "cat_enc": le_cat.transform(["Bakery"])[0],
    "seg_enc": le_seg.transform(["Regular"])[0],
}], columns=promo_model.feature_names_in_)
print(promo_model.predict_proba(X)[0, 1])

The large ExtraTrees pickles take 12–21 MB each. Loading a pickle can execute arbitrary code, so only load files from sources you trust.

Re-running training

pip install numpy pandas matplotlib seaborn scikit-learn joblib
# The output directory is hard-coded: edit OUTPUT_DIR = "/home/claude/gcc_supermarket_ai" near the top first
python supermarket_ai.py

The script regenerates the data, models, encoders, CSV samples, automl_results.json and the dashboard. The transaction generator is a 50,000-iteration Python loop, so it takes a little while.

Intended use

  • A worked example of an end-to-end grocery-retail AutoML pipeline: synthetic data, feature engineering, model selection and dashboarding, with GCC calendar effects.
  • Teaching and prototyping, and a scaffold to retrain on real point-of-sale and inventory data.

Out of scope: real replenishment, pricing, promotion planning or customer targeting without retraining on real data and proper validation.

Limitations and risks

  • Synthetic data only. Real retail performance is unknown.
  • Scores are optimistic because of target leakage or deterministic labels. See the notes under "Models". The only non-trivial tasks, sales forecast (R² 0.28) and demand class (accuracy 0.544), show modest performance even with the leakage.
  • Simplified calendar. Ramadan and Eid use fixed Gregorian dates, not the actual lunar dates.
  • Nationality-based segments. The "Expat-Western" and "Expat-Asian" segments encode origin. Using them to vary prices, offers or service for real customers could be discriminatory. Review this before any real use.
  • Evaluation. The test split is a random row split, not a time-based split. Only accuracy is reported for the classifiers.

License

MIT

Citation

@misc{agenthink2026gccsupermarket,
  author       = {AgenThink},
  title        = {GCC Supermarket AI},
  year         = {2026},
  publisher    = {Hugging Face},
  howpublished = {\url{https://huggingface.co/agenthinkmesh/gcc_supermarket_ai}}
}
Downloads last month
-
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support