← All work

Bank Customer Churn Prediction

AUC 0.868. But the insight is cheaper than the model — the EDA alone tells a branch manager who to call.

Python · JupyterEDA · Feature engineering · Gradient boostingCase study
Bank customer churn analysis
Churn drivers: tenure, balance, and activity patterns

The question

A retail bank loses customers silently — by the time they close the account, the relationship is already gone. Which customers are about to leave, and what would actually change their mind?

The pipeline

End to end, in one notebook anyone can rerun:

  1. EDA before anything. Distributions, correlations, and segment profiles first — because a model trained on misunderstood data is confidently wrong.
  2. Feature engineering. Balance-to-salary ratios, tenure bands, product-count and activity flags. The raw columns hide the behavior; the features surface it.
  3. Gradient boosting, validated properly — stratified splits, no leakage, AUC 0.868 on held-out data.

Findings

1

Inactivity is the loudest signal

Customers with dormant accounts churn at multiples of the base rate. The model leans on it; so should retention teams.

2

Tenure has a danger zone

Early-tenure customers with high balances are the most at-risk, most valuable segment — new enough to leave, invested enough to matter.

3

The model confirms what the EDA shows

Feature importance and segment analysis agree — which is exactly what you want. A model that surprises its own EDA is a model nobody trusts.

The insight is cheaper than the model

The punchline of this project isn't AUC 0.868 — it's that the top churn drivers are actionable without any model in production. A branch manager can call dormant, high-balance, early-tenure customers this week using a spreadsheet. The model's job is to rank and refine that list, not to replace the judgment.

What I'd do differently

Calibrate the probabilities. AUC ranks well, but retention budgets need "how likely, really" — next iteration: Platt scaling or isotonic calibration.

Cost-sensitive thresholds. A false negative (lost customer) costs more than a false positive (a phone call). The threshold should reflect that, not default to 0.5.


Tech stack: Python, pandas, scikit-learn, gradient boosting, matplotlib/seaborn. Full notebook in the repo.