Competition: Flipkart Grid 6.0 · Track: Traffic Demand Forecasting
Submitted Score:73.03 / 100
Actual Model Capability:~93.4 / 100(see Known Issue below)
TL;DR: The score submitted to the platform (73.03) does NOT reflect the model's true capability (~93.4). The wrong output file was uploaded due to a feature mismatch bug between the CLI and the notebook.
Two separate codepaths existed to generate predictions:
| Codepath | Features Used | Local R² | File |
|---|---|---|---|
Notebook (traffic_demand_baseline.ipynb) |
10 numeric + 8 categorical (full set) | 0.934 | submission_best.csv ✅ |
CLI (traffic_demand_cli.py) |
6 numeric + 5 categorical (degraded) | 0.730 | submission.csv ❌ uploaded |
The CLI was auto-generated by the notebook's "export" cell and accidentally omitted:
sin_time_of_day,cos_time_of_day— cyclic time encodinggeo_lat,geo_lon— decoded geohash coordinatesgeohash_2,geohash_3,geohash_4— spatial prefix clusters
These 5 features are among the most predictive. Without them, R² drops from 0.934 → 0.730.
The CLI's local holdout (train on day != 49, validate on day == 49) produced R² = 0.730, which matched the platform score exactly — so it looked consistent. The notebook's higher R² (0.934) used a different split (random 20% of day-49 rows), making the two numbers not directly comparable.
submission_best.csv (the correct output) was sitting in the repo but submission.csv was uploaded instead.
- Root cause identified
-
features.pymodule created to unify feature engineering across both codepaths - CLI updated to import from
features.py(pending) - Re-run and upload
submission_best.csv
Predict per-location, per-time-slot traffic demand values (continuous, clamped to [0, 1]) for a grid of geohash cells across multiple days. The evaluation metric is R² × 100.
- Train: 77,299 rows across days 1–49
- Test: 41,778 rows (no target)
- Key columns:
geohash,day,timestamp,RoadType,NumberofLanes,LargeVehicles,Landmarks,Temperature,Weather
Flipkart_Gridlock/
│
├── README.md # This file
├── .gitignore
├── requirements.txt # Python dependencies
├── pytest.ini # Test runner config (testpaths + pythonpath)
│
├── src/ # Library source code
│ ├── __init__.py
│ ├── features.py # Feature engineering (time, geo, cyclic, peak-hour)
│ ├── evaluate.py # Metrics: R², MAE, RMSE, competition score
│ ├── model_config.py # Hyperparameter dataclass with JSON I/O
│ └── traffic_demand_baseline_support.py # Timestamp parser utility
│
├── scripts/
│ └── predict.py # CLI runner — train + predict in one command
│
├── notebooks/
│ └── traffic_demand_baseline.ipynb # Full tuning notebook (local R²: 0.934)
│
├── submissions/ # Generated CSV outputs (gitignored content)
│ └── .gitkeep
│
├── artifacts/
│ ├── best_params.json # Best hyperparameters from tuning loop
│ └── metrics.json # Validation metrics (CLI holdout, R²=0.730)
│
├── tests/ # Unit tests (pytest)
│ ├── test_features.py # 30+ tests for src/features.py
│ ├── test_evaluate.py # Tests for src/evaluate.py
│ └── test_model_config.py # Tests for ModelConfig dataclass
│
└── docs/
└── approach.txt # Full competition approach write-up
pip install -r upload_package/requirements.txtOr with a virtual environment:
python -m venv .venv
.venv\Scripts\activate # Linux/Mac: source .venv/bin/activate
pip install -r upload_package/requirements.txtDownload the competition CSVs and place them under dataset/:
dataset/train.csv
dataset/test.csv
dataset/sample_submission.csv
⚠️ Use the notebook for now until the CLI is updated to usesrc/features.py.
# Preferred (full features, ~0.93 R²):
jupyter nbconvert --to notebook --execute notebooks/traffic_demand_baseline.ipynb
# CLI (scripts/predict.py):
python scripts/predict.py --out submissions/submission.csvFeatures below are what the notebook uses (the full set). The CLI was missing the last 5.
| Feature | Codepath | Description |
|---|---|---|
hour, minute |
Both | Parsed from timestamp column (H:M format) |
time_of_day |
Both | hour * 60 + minute (linear minutes since midnight) |
sin_time_of_day, cos_time_of_day |
Notebook only |
Cyclic time encoding — captures daily periodicity |
geohash_2/3/4 |
Notebook only |
Prefix-level clusters (coarse → fine spatial hierarchy) |
geo_lat, geo_lon |
Notebook only |
Decoded geohash center coordinates via pygeohash |
day_of_week |
features.py (new) |
day % 7 — weekly demand rhythm proxy |
is_peak_hour |
features.py (new) |
Binary: 1 if hour ∈ {7,8,9,17,18,19} |
ColumnTransformer
├── numeric → SimpleImputer(median)
└── categorical → SimpleImputer("__MISSING__") → OrdinalEncoder(unknown=-1)
│
└── HistGradientBoostingRegressor
HistGradientBoostingRegressor (scikit-learn) — chosen for:
- Native support for missing values (no separate imputer needed for the model itself)
- Fast training on large tabular datasets
- Strong out-of-the-box performance with minimal tuning
Best hyperparameters (from notebook tuning loop over 5 candidates):
| Param | Value |
|---|---|
learning_rate |
0.08 |
max_iter |
600 |
max_leaf_nodes |
128 |
min_samples_leaf |
20 |
Two different validation schemes were used — this is part of why the discrepancy wasn't caught:
| Scheme | Description | R² |
|---|---|---|
| Notebook | Random 80/20 split within day-49 rows | 0.934 |
| CLI holdout | Train on day != 49, validate on all day == 49 |
0.730 |
The CLI holdout is the more conservative and realistic estimate because it treats an entire day as unseen. The 0.730 is the correct credible score for that scheme.
| Submission | Features | Validation R² | Platform Score |
|---|---|---|---|
submission.csv (CLI — uploaded) |
6 numeric + 5 cat | 0.730 | 73.03 ❌ |
submission_best.csv (notebook) |
10 numeric + 8 cat | 0.934 | ~93.4 (not uploaded) ✅ |
pytest tests/ -vTests cover: timestamp parsing, all feature builders, cyclic encoding bounds, peak-hour flag, geo prefix lengths, evaluation metrics, and ModelConfig serialization.
See upload_package/requirements.txt.
Key dependencies:
scikit-learn >= 1.4pandasnumpypygeohashpytest(dev)