Skip to content

About

Delivery time prediction and late-delivery classification with PySpark ML — Linear & Logistic Regression, plus a dependency-free Flask API for real-time scoring.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

6 Commits

Folders and files

Repository files navigation

Smart Dispatch

Delivery Time Prediction and Late Delivery Classification using PySpark ML.

Demo

Smart Dispatch live prediction demo

Models

  • Linear Regression
  • Logistic Regression

Dataset

Synthetic Delivery Dataset

Technologies

  • PySpark
  • Spark ML
  • Hadoop HDFS
  • Jupyter Notebook

Running the notebooks

Run in order — each notebook reads artifacts the previous one saved. All notebooks expect to be run with the project root (smart_dispatch/) as the working directory (VS Code: set via .vscode/settings.json's jupyter.notebookFileRoot), using the venv (3.12.13.final.0) / smart_dispatch kernel, with SPARK_HOME set (see ~/.bashrc).

  1. scripts/generate_dataset.py — run once first (python scripts/generate_dataset.py) to create data/raw/delivery_data.csv (10,000 synthetic rows). Regenerate any time you want a fresh dataset — it's deterministic (seed=42).
  2. 01_Data_Preparation.ipynb — loads the raw CSV, validates it (nulls, duplicates, feature ranges, label balance), builds and fits the preprocessing pipeline (categorical encoding + scaling), and saves:
    • data/processed/prepared_data.parquet
    • models/preprocessing_pipeline/
  3. 02_Model_Training.ipynb — loads prepared_data.parquet, splits train/test (80/20, seed=42), trains a LinearRegression (target: actual_time_mins) and a LogisticRegression (target: is_late), and saves:
    • data/processed/train_data.parquet, data/processed/test_data.parquet
    • models/regression_model/, models/classification_model/
  4. 03_Model_Evaluation.ipynb — loads both models and the test set, evaluates them (RMSE/R²/MAE, AUC/accuracy/precision/recall/F1, confusion matrix, feature importance, error analysis), and saves output/metrics/metrics_<timestamp>.json.
  5. 04_Model_Inference.ipynb — loads all three saved models, defines single-record and batch prediction functions, runs deployment-readiness checks (edge cases, latency, memory), and saves:
    • scripts/predict.py — a dependency-free (no Spark/JVM) scoring function for low-latency serving
    • output/predictions/predictions_<timestamp>.csv

Each notebook can also be run non-interactively, e.g.:

jupyter nbconvert --to notebook --execute --inplace \
  --ExecutePreprocessor.kernel_name=smart_dispatch \
  notebooks/01_Data_Preparation.ipynb

run from the project root (so relative paths and config.py imports resolve correctly).

Real-time prediction API

api/app.py is a Flask app built on top of scripts/predict.py (the dependency-free scorer from 04_Model_Inference.ipynb — no Spark/JVM per request, so it's actually suited to real-time traffic; the Spark models themselves take ~850ms/call and shouldn't be called directly per-request). Regenerate scripts/predict.py by re-running that notebook's Section 5 if the models are retrained.

Route Method Purpose
/ GET Browser demo form (see below)
/predict POST JSON prediction endpoint

1. Run the app

From the project root:

# dev (foreground)
python api/app.py

# production (foreground)
gunicorn -w 4 -b 0.0.0.0:8000 api.app:app

# production, backgrounded
nohup gunicorn -w 4 -b 0.0.0.0:8000 api.app:app > /tmp/smart_dispatch_api.log 2>&1 &
disown

2. View in browser

/predict only accepts POST requests with a JSON body, so you can't view a prediction by just navigating to it — but GET / serves a small HTML form for exactly that:

  1. With the server running (step 1), open http://127.0.0.1:8000/ in a browser. On WSL2 this is reachable directly from Windows (localhost auto-forwards); VS Code may also pop up a "port 8000 available" notification you can click instead.
  2. The form is pre-filled with sample values (distance, packages, traffic index, weather, driver experience, time of day). Click Predict.
  3. The JSON result renders below the form — green background on success (delivery_time, is_late, probability_late), red on error (e.g. a missing/invalid field).

Each submission calls POST /predict under the hood via fetch(), so it's the same response you'd get from curl — just rendered instead of printed:

curl -X POST http://127.0.0.1:8000/predict \
  -H "Content-Type: application/json" \
  -d '{"distance_km": 18.5, "num_packages": 6, "traffic_index": 6.2, "weather_condition": "Rain", "driver_experience_yrs": 4.0, "time_of_day": "Rush_Hour"}'
{"delivery_time": 71.04, "is_late": 1, "probability_late": 0.989}

Returns 400 with an {"error": ...} body for missing fields or invalid value types; unknown weather_condition/time_of_day values are accepted (mapped to the pipeline's reserved "unseen category" index) rather than rejected.

3. Stop the server

  • Foreground (python api/app.py or a plain gunicorn ... command in a terminal): press Ctrl+C in that terminal.

  • Backgrounded (started with nohup ... &, or you lost track of the terminal): find and stop the gunicorn master process —

    pkill -f "gunicorn.*api.app:app"

    or find its PID first and send it a graceful shutdown signal:

    ps aux | grep "gunicorn.*api.app:app"
    kill <PID>          # SIGTERM: graceful, finishes in-flight requests

    Avoid kill -9 unless a plain kill doesn't stop it — that skips gunicorn's graceful worker shutdown.

License

This project is licensed under the MIT License — you are free to use, modify, and distribute it. See the LICENSE file for details.

About

Delivery time prediction and late-delivery classification with PySpark ML — Linear & Logistic Regression, plus a dependency-free Flask API for real-time scoring.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages