Skip to content

James-Muguro/CustomerSegmentation

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

28 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Bank Customer Segmentation

An end-to-end data engineering pipeline for segmenting banking customers in the Kenyan market. The project covers data ingestion and validation, geographic enrichment, feature engineering, unsupervised clustering, segment profiling, and an interactive Streamlit dashboard — built to production-grade code standards with a reproducible, modular pipeline.


The Problem

Banks in Kenya manage thousands of customers with different financial profiles, relationship tenures, and product needs. Treating them as a single group leads to poorly targeted products, weak retention, and missed revenue. The goal was to build a structured segmentation pipeline that identifies distinct customer groups from raw banking data — and produces something the business can act on, not just a model that outputs cluster labels.


The Data

Two datasets were used:

Customer data (customer_data.xlsx) — 2,812 records, 32 columns:

  • Demographics: age, sex, nationality, occupation
  • Banking metadata: relationship type, fee structure, loyalty classification, investment advisor, banking contact
  • Financial balances: estimated income, bank deposits, bank loans, saving accounts, checking accounts, credit card balance, superannuation savings, foreign currency account, business lending
  • Relationship history: date joined, last contact, last meeting, days with bank, relationship time frame

Location data (location.xlsx) — 10,013 records, 8 columns:

  • Location ID, street, suburb, postcode, longitude, latitude, full address, city

The two datasets were merged on Location ID using a left join with referential integrity validation (validate="m:1"). Zero customers were left unmatched. The resulting dataset had 39 columns. The only missing values were 183 records in the Street column, imputed using Suburb as a proxy.


Approach

Pipeline Design

The pipeline was built in five distinct stages, each with validation at entry and exit:

  1. Ingestion — schema validation, duplicate detection, and missing value checks on both datasets before any processing
  2. Integration — left merge on Location ID with post-merge row count validation and unmatched record logging
  3. EDA — statistical profiling, distribution analysis, correlation matrix, and categorical breakdowns before feature engineering
  4. Feature engineering — engineered features on top of the clean, merged dataset
  5. Clustering — standardised features, silhouette-based cluster selection, K-Means, and segment profiling

The interim merged dataset was saved to Parquet (merged_customer_data.parquet) for reproducibility between stages.

Feature Engineering

Raw balances don't capture financial behaviour. The following features were engineered to represent what customers actually do with their money:

Feature Formula Business Logic
Total Assets Bank Deposits + Saving Accounts + Superannuation Savings Full picture of liquid and semi-liquid wealth
Total Debt Bank Loans + Credit Card Balance Consolidated liability position
Net Wealth Total Assets − Total Debt True financial position
Debt-to-Income Ratio Total Debt / Estimated Income Leverage relative to earnings
Savings Ratio Saving Accounts / Estimated Income Propensity to save
Deposit-to-Loan Ratio Bank Deposits / Bank Loans Asset coverage of debt
Credit Utilization Ratio Credit Card Balance / Number of Credit Cards Proxy for financial stress
Product Diversity Score Count of non-zero product balances Cross-sell depth
Income Category Binned: 0–20K / 20–50K / 50–100K / 100K+ Interpretable income segment
Date features Year, month, quarter, days-since for Joined Bank / Last Contact / Last Meeting Recency and relationship tenure

All division operations used np.where guards to handle zero denominators cleanly.

Outliers were treated using IQR clipping (1.5× IQR) applied to all numeric columns before clustering.

Preprocessing

A scikit-learn ColumnTransformer pipeline was built with separate branches for numeric and categorical features:

  • Numeric: mean imputation → StandardScaler
  • Categorical: most-frequent imputation → OneHotEncoder (handle_unknown="ignore")

The transformed output was converted from sparse to dense matrix before being passed to the clustering step.

Clustering

Features used for segmentation:

  • Age
  • Estimated Income
  • Total Assets
  • Credit Utilization Ratio
  • Total Debt
  • Saving Accounts

Three algorithms were implemented:

Algorithm Rationale
K-Means Primary model — interpretable, scalable, deterministic with fixed random seed
Gaussian Mixture Models Probabilistic alternative for soft cluster boundaries
DBSCAN Density-based, useful for detecting noise and non-spherical clusters

Optimal cluster count was selected using silhouette score analysis across k = 2 to 10, plotted alongside WCSS (elbow method) for visual confirmation. The best silhouette score determined the final k used in production.

Fitted models and scalers were persisted to disk using joblib for reuse without retraining.


Results

Segment Profiles (K-Means, optimal k from silhouette analysis)

Cluster 0 Cluster 1
Size 797 customers (28.3%) 2,015 customers (71.7%)
Avg Age 50.9 years 51.2 years
Avg Income KES 155,295 KES 173,448
Avg Total Assets KES 2,255,881 KES 905,124
Debt-to-Income Ratio 8.49 3.25
Savings Ratio 14.53 4.22
Credit Utilization Higher Lower

Cluster 0 — High-Asset, High-Leverage customers (28.3%): Despite lower average income than Cluster 1, this group holds more than double the total assets and carries proportionally more debt. Savings and deposit-to-loan ratios are significantly elevated. Retail is the dominant banking relationship (385 customers), followed by Investments Only (162) and Private Bank (114). Top cities: Nairobi (83), Kiambu (46), Nakuru (44). Business recommendation: premium wealth management, dedicated relationship manager.

Cluster 1 — Mainstream Retail customers (71.7%): The broader customer base. Moderate income, lower assets, conservative debt levels. Retail dominates (964 customers), followed by Investments Only (412) and Commercial (299). Top cities: Nairobi (234), Kiambu (106), Nyeri (98). Business recommendation: investment product promotion, financial advisory services.

Key EDA Findings

Strong correlations identified (|r| ≥ 0.70):

  • Bank Deposits ↔ Checking Accounts: r = 0.84
  • Bank Deposits ↔ Saving Accounts: r = 0.75
  • Location ID ↔ Location ID_1: r = 1.00 (exact duplicate — one column should be dropped before modelling)

Distribution characteristics:

  • Age is symmetrically distributed (skewness ≈ 0.00), ranging 17–85, median 51
  • Estimated Income is right-skewed (skewness = 0.88), median KES 141,620 vs mean KES 171,118
  • Bank Deposits, Saving Accounts, and Business Lending are all positively skewed — a small number of high-balance customers pull the mean upward

Customer breakdown:

  • 51.2% have been with the bank for over 20 years
  • Retail is the largest relationship type at 48.0% of customers
  • European nationality accounts for 44.0% of the base (5 nationalities total)
  • Nairobi accounts for 11.3% of customers; Kiambu (5.4%), Mombasa (4.1%), Meru (4.0%), and Nakuru (4.8%) are the next largest markets

Technical Stack

Layer Tools
Data ingestion pandas, openpyxl, pathlib
Validation Custom schema and referential integrity checks
Feature engineering pandas, numpy, scikit-learn
Preprocessing scikit-learn Pipeline, ColumnTransformer
Clustering scikit-learn KMeans, GaussianMixture, DBSCAN
Visualisation matplotlib, seaborn, plotly
Dashboard Streamlit
Persistence joblib, Parquet
Logging Python logging module
Environment Python 3.8+, virtual environment

Project Structure

CustomerSegmentation/
├── bank_customer_segmentation.ipynb    # End-to-end analysis notebook
├── app.py                              # Streamlit dashboard
├── requirements.txt                    # Dependencies
├── data/
│   ├── raw/
│   │   ├── customer_data.xlsx
│   │   └── location.xlsx
│   ├── interim/
│   │   └── merged_customer_data.parquet
│   └── processed/
├── models/
│   ├── customer_segmentation_kmeans.joblib
│   └── feature_scaler.joblib
├── reports/
│   └── segment_analysis.json
└── README.md

Running the Project

1. Clone and set up

git clone https://github.com/James-Muguro/CustomerSegmentation.git
cd CustomerSegmentation
python -m venv .venv
source .venv/Scripts/activate   # Windows: .venv\Scripts\activate
pip install -r requirements.txt

2. Run the notebook

Open bank_customer_segmentation.ipynb in Jupyter and run all cells top to bottom. The pipeline auto-detects Excel files in data/raw/ — no hardcoded paths.

3. Launch the dashboard

streamlit run app.py

The app loads data/customer_data.xlsx automatically. If the file is missing it falls back to a synthetic dataset so the UI is always explorable. Use the sidebar to select clustering features, adjust k, and run the model interactively.


What I Would Improve

Drop redundant and identifier columns before the preprocessing pipeline. Location ID and Location ID_1 are identical (r = 1.0). Columns like Name, Customer ID, Banking Contact, and Full Address add no signal to clustering and inflate the one-hot encoded feature matrix significantly. A deliberate feature selection step before the ColumnTransformer would produce cleaner clusters.

Validate the Credit Utilization Ratio formula. The current formula divides Credit Card Balance by Number of Credit Cards, which produces a per-card balance rather than a true utilization rate. A proper credit utilization ratio requires a credit limit denominator. The notebook acknowledges this in a comment — fixing it upstream would make the ratio more meaningful as a risk proxy.

Restrict scaler fitting to training data. The current pipeline fits StandardScaler on the full dataset. For a production model with a train/test split, fitting on training data only is necessary to prevent leakage. The CustomerSegmentation class fits in __init__, which means refitting is also triggered on every call — worth separating into a dedicated fit() method.

Test higher values of k with stakeholder input. The silhouette score selected k = 2, which is interpretable but likely too coarse for product-level targeting. In a real deployment, k = 4 or 5 would be worth validating with business stakeholders to check whether the additional segments are actionable rather than just statistically distinct.

Automate reporting. Segment profiles and recommendations are currently printed to the notebook. Templated HTML or PDF reports generated on each run would make the output shareable with non-technical stakeholders without requiring them to open a Jupyter notebook.


Acknowledgements

Built with scikit-learn, pandas, plotly, matplotlib, and Streamlit. Dataset is a simulated Kenyan banking dataset used for analytical and portfolio purposes.

About

This repository contains data analysis and customer segmentation of Kenyan banks. It aims to understand customer behaviors and patterns. Explore how Kenyan banks segment their customers using demographics, behavior, and needs. Gain data-driven strategies to target the right audiences with the most relevant products and services

Resources

License

Stars

2 stars

Watchers

1 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors