Time Series Forecasting with LightGBM | Machine Learning Project
This project focuses on forecasting the total number of daily transactions for 7 merchants over a 3-month horizon (October–December 2020) using machine learning-based time series techniques. Rather than classical statistical models (ARIMA, Exponential Smoothing), the project adopts a feature engineering-heavy approach where temporal patterns are encoded as input features for a LightGBM regression model.
The core challenge is to construct features that are available at prediction time — respecting the forecast horizon — and to validate the model on the exact period being forecasted.
Source: Miuul Time Series Course
The dataset contains daily transaction records per merchant:
| Variable | Description |
|---|---|
transaction_date |
Date of transactions (2018-01-01 to 2020-12-31) |
merchant_id |
Unique merchant identifier (7 merchants) |
Total_Transaction |
Daily total transaction count (target variable) |
Total_Paid |
Daily total payment amount (excluded from modelling) |
.
├── main.py # Full pipeline: EDA → Feature Engineering → Modelling
├── data/
│ └── iyzico_data.csv
└── README.md
- Dataset shape, data types, and null value inspection
- Descriptive statistics per merchant
- Per-merchant transaction count time series visualisation
- Autocorrelation Function (ACF) analysis to identify meaningful lag periods
All features are constructed to be available at prediction time. A 90-day minimum shift is enforced across all lag and rolling features to prevent data leakage given the 3-month forecast horizon.
month,day_of_month,day_of_year,week_of_year,day_of_week,yearis_weekend,is_month_start,is_month_end
Per-merchant lagged values of Total_Transaction at:
91, 98, 105, 112, 120, 180, 365, 540, 720 days.
Per-merchant rolling mean with a 90-day shift applied before the window:
91, 120, 180, 365, 455, 540-day windows.
Per-merchant EWM combining multiple decay rates and starting points, giving more weight to recent observations:
- Alphas:
0.95, 0.7, 0.5 - Lags:
91, 180, 270, 365days
- One-Hot Encoding applied to
monthandday_of_week merchant_idpassed as a native categorical feature to LightGBMtransaction_dateandTotal_Paidexcluded from the feature set
LightGBM (gradient boosting) is used with a Poisson regression objective, which is appropriate for count data. Early stopping is applied on the validation set to prevent overfitting.
| Split | Date Range |
|---|---|
| Train | 2018-01-01 → 2020-09-30 |
| Validation | 2020-10-01 → 2020-12-31 |
| Final Model | Retrained on all data using best_iteration from validation |
A special MAPE function independently evaluates model performance for each vendor and then calculates the average across vendors. This prevents large vendors from dominating the metric and ensures each vendor is evaluated on their own scale. The results are as follows:
| merchant_id | MAPE |
|---|---|
535 |
12.09% |
42616 |
25.75% |
46774 |
26.71% |
57192 |
11.50% |
86302 |
14.01% |
124381 |
42.28% |
129316 |
18.24% |
Average MAPE: 21.51%
1. Clone the repository
git clone https://github.com/BahriDogru/iyzico_transaction_volume_forecasting.git
cd iyzico_transaction_volume_forecasting2. Install required libraries
pip install -r requirements.txt3. Add the dataset
Place iyzico_data.csv inside a folder named data/ in the project root:
.
├── main.py
├── data/
│ └── iyzico_data.csv
└── README.md
4. Run
python main.py| Decision | Rationale |
|---|---|
| Forecast horizon enforcement | All lag and rolling features use a minimum 90-day shift — no future information leaks into training |
| Per-merchant feature computation | groupby('merchant_id') applied before every shift/rolling/ewm so each merchant's history is used independently |
Total_Paid excluded |
Highly correlated with the target and unavailable at prediction time |
| Poisson objective | Count data with non-negative constraints; more appropriate than standard MSE regression |
| Custom MAPE metric | Merchant-level evaluation prevents large merchants from masking poor performance on smaller ones |