BACK

GitHub repository
Kaggle

Customer Segmentation from Credit Card Transactions

Customer segmentation divides costumers into groups based on common characteristics. This is useful for companies to be able to strategize their marketing estrategies in a more effective way. One of the main goals is to identify ways to improve products or new product or service opportunities.

Clustering is an Unsupervised Learning methodology, useful to develop customer segmentation analysis. One of the most known algorithms is KMeans. KMeans requires the number of clusters to be specified and clusters data by trying to separate samples in n groups of equal variance, minimizing inertia. KMeans scales well to large number of samples.

Objective

In this playground project, a customer segmentation using KMeans is developed. The dataset sumarizes the behaviour of 8950 clients and includes information such as balance, purchases, cash advance, among others.

In this notebook I am practicing my skills in:

  • Pandas to handling the data
  • Clustering (KMeans --> Sklearn)

Resources

Graphical Summary of Results

  • 7 Clusters were used for customer segmentation Results for Topics

Contents

  1. Exploratory Data Analysis (EDA)
  2. Clustering: K-Means
  3. Principal Component Analysis (PCA)
  4. Conclusions

Initial Setup

  • Import the packages needed for the notebook
In [136]:
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler, normalize
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA

Load Data

In [137]:
creditcard = pd.read_csv('CC GENERAL.csv')
creditcard.head()
Out[137]:
CUST_ID BALANCE BALANCE_FREQUENCY PURCHASES ONEOFF_PURCHASES INSTALLMENTS_PURCHASES CASH_ADVANCE PURCHASES_FREQUENCY ONEOFF_PURCHASES_FREQUENCY PURCHASES_INSTALLMENTS_FREQUENCY CASH_ADVANCE_FREQUENCY CASH_ADVANCE_TRX PURCHASES_TRX CREDIT_LIMIT PAYMENTS MINIMUM_PAYMENTS PRC_FULL_PAYMENT TENURE
0 C10001 40.900749 0.818182 95.40 0.00 95.4 0.000000 0.166667 0.000000 0.083333 0.000000 0 2 1000.0 201.802084 139.509787 0.000000 12
1 C10002 3202.467416 0.909091 0.00 0.00 0.0 6442.945483 0.000000 0.000000 0.000000 0.250000 4 0 7000.0 4103.032597 1072.340217 0.222222 12
2 C10003 2495.148862 1.000000 773.17 773.17 0.0 0.000000 1.000000 1.000000 0.000000 0.000000 0 12 7500.0 622.066742 627.284787 0.000000 12
3 C10004 1666.670542 0.636364 1499.00 1499.00 0.0 205.788017 0.083333 0.083333 0.000000 0.083333 1 1 7500.0 0.000000 NaN 0.000000 12
4 C10005 817.714335 1.000000 16.00 16.00 0.0 0.000000 0.083333 0.083333 0.000000 0.000000 0 1 1200.0 678.334763 244.791237 0.000000 12

Exploratory Data Analysis (EDA)

To better understand the data it is important to explore the dataset. First a statistical description of the dataset is obtained. Then null values are replaced with the mean of the column. Next, histograms and correlation plots are done.

  • Describe - Main statistical information is obtained: minimum, maximum values as well as standard deviations and quarters.
In [204]:
creditcard.describe()
Out[204]:
BALANCE BALANCE_FREQUENCY PURCHASES ONEOFF_PURCHASES INSTALLMENTS_PURCHASES CASH_ADVANCE PURCHASES_FREQUENCY ONEOFF_PURCHASES_FREQUENCY PURCHASES_INSTALLMENTS_FREQUENCY CASH_ADVANCE_FREQUENCY CASH_ADVANCE_TRX PURCHASES_TRX CREDIT_LIMIT PAYMENTS MINIMUM_PAYMENTS PRC_FULL_PAYMENT TENURE
count 8950.000000 8950.000000 8950.000000 8950.000000 8950.000000 8950.000000 8950.000000 8950.000000 8950.000000 8950.000000 8950.000000 8950.000000 8950.000000 8950.000000 8950.000000 8950.000000 8950.000000
mean 1564.474828 0.877271 1003.204834 592.437371 411.067645 978.871112 0.490351 0.202458 0.364437 0.135144 3.248827 14.709832 4494.449450 1733.143852 864.206542 0.153715 11.517318
std 2081.531879 0.236904 2136.634782 1659.887917 904.338115 2097.163877 0.401371 0.298336 0.397448 0.200121 6.824647 24.857649 3638.612411 2895.063757 2330.588021 0.292499 1.338331
min 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 50.000000 0.000000 0.019163 0.000000 6.000000
25% 128.281915 0.888889 39.635000 0.000000 0.000000 0.000000 0.083333 0.000000 0.000000 0.000000 0.000000 1.000000 1600.000000 383.276166 170.857654 0.000000 12.000000
50% 873.385231 1.000000 361.280000 38.000000 89.000000 0.000000 0.500000 0.083333 0.166667 0.000000 0.000000 7.000000 3000.000000 856.901546 335.628312 0.000000 12.000000
75% 2054.140036 1.000000 1110.130000 577.405000 468.637500 1113.821139 0.916667 0.300000 0.750000 0.222222 4.000000 17.000000 6500.000000 1901.134317 864.206542 0.142857 12.000000
max 19043.138560 1.000000 49039.570000 40761.250000 22500.000000 47137.211760 1.000000 1.000000 1.000000 1.500000 123.000000 358.000000 30000.000000 50721.483360 76406.207520 1.000000 12.000000
  • Checking for null values --> MINIMUM_PAYMENTS and CREDIT_LIMIT COLUMNS present null values
In [139]:
creditcard.isnull().sum()
Out[139]:
CUST_ID                               0
BALANCE                               0
BALANCE_FREQUENCY                     0
PURCHASES                             0
ONEOFF_PURCHASES                      0
INSTALLMENTS_PURCHASES                0
CASH_ADVANCE                          0
PURCHASES_FREQUENCY                   0
ONEOFF_PURCHASES_FREQUENCY            0
PURCHASES_INSTALLMENTS_FREQUENCY      0
CASH_ADVANCE_FREQUENCY                0
CASH_ADVANCE_TRX                      0
PURCHASES_TRX                         0
CREDIT_LIMIT                          1
PAYMENTS                              0
MINIMUM_PAYMENTS                    313
PRC_FULL_PAYMENT                      0
TENURE                                0
dtype: int64
  • There are different options to work with null values. One would be to drop them, but in this case I am filling them with the mean value of the column
In [144]:
creditcard['CREDIT_LIMIT'].fillna(creditcard['CREDIT_LIMIT'].mean(), inplace=True)
creditcard['MINIMUM_PAYMENTS'].fillna(creditcard['MINIMUM_PAYMENTS'].mean(), inplace=True)

creditcard.isnull().sum()
Out[144]:
CUST_ID                             0
BALANCE                             0
BALANCE_FREQUENCY                   0
PURCHASES                           0
ONEOFF_PURCHASES                    0
INSTALLMENTS_PURCHASES              0
CASH_ADVANCE                        0
PURCHASES_FREQUENCY                 0
ONEOFF_PURCHASES_FREQUENCY          0
PURCHASES_INSTALLMENTS_FREQUENCY    0
CASH_ADVANCE_FREQUENCY              0
CASH_ADVANCE_TRX                    0
PURCHASES_TRX                       0
CREDIT_LIMIT                        0
PAYMENTS                            0
MINIMUM_PAYMENTS                    0
PRC_FULL_PAYMENT                    0
TENURE                              0
dtype: int64
  • Check for duplicates
In [202]:
duplicatesN = creditcard[creditcard.duplicated()]['CUST_ID'].count()
print(f"Number of dupplicated entries: {duplicatesN}")
Number of dupplicated entries: 0
  • Histogram Plots - help to understand how the values on the different parameters are distributed
In [146]:
columnsName = creditcard.columns[1:]  # CUST_ID column is not considered
plt.figure(figsize=(12,55))
for ii, columnName in enumerate(columnsName): 
    plt.subplot(len(columnsName), 1, ii+1)
    plt.hist(creditcard[columnName], alpha=.4, bins=30)
    plt.title(columnName)
    
plt.tight_layout()
  • Correlation matrix - helps visualizing how the different variables are correlated.
In [205]:
plt.figure(figsize=(12,12))
sns.heatmap(creditcard.corr(), annot=True)
Out[205]:
<AxesSubplot:>
In [207]:
# As customer ID is not important for this analysis, I will drop this from the dataframe
creditcard_df = creditcard.copy()
creditcard_df = creditcard_df.drop(columns='CUST_ID')

Clustering: k-Means

KMeans clustering was originated for signal processing. It splits the data into k clusters in which each observation belongs to the cluster with the nearest mean. More information about K-Means here.

Scale/standardize

The first step to apply k-Means is to scale/standarize the features. It is not convinient to have features at different scale. Here I am using a standar scaler.

In [149]:
scaler = StandardScaler()
creditcard_scaled = scaler.fit_transform(creditcard_df)

Elbow method

To decide how many clusters are needed, elbow method#:~:text=In%20cluster%20analysis%2C%20the%20elbow,number%20of%20clusters%20to%20use.) can be used. The idea is to find the proper number of clusters accounting for both, inertia and computer time. Here we selected 7 as the optimum value.

In [208]:
inertiaValue = []

for ii in range(1, 25):
    model = KMeans( n_clusters = ii )
    model.fit(creditcard_scaled)
    inertiaValue.append(model.inertia_)
    
plt.plot(inertiaValue,'bs-')
Out[208]:
[<matplotlib.lines.Line2D at 0x1d025f30108>]

Applying k-Means

In [209]:
kmeans = KMeans(7)
kmeans.fit(creditcard_scaled)
labels = kmeans.labels_
y_kmeans = kmeans.fit_predict(creditcard_scaled)
In [210]:
# New column including the cluster for each sample
creditcard_df_cluster = pd.concat([creditcard_df, pd.DataFrame({'cluster':labels})], axis = 1)
creditcard_df_cluster.head()
Out[210]:
BALANCE BALANCE_FREQUENCY PURCHASES ONEOFF_PURCHASES INSTALLMENTS_PURCHASES CASH_ADVANCE PURCHASES_FREQUENCY ONEOFF_PURCHASES_FREQUENCY PURCHASES_INSTALLMENTS_FREQUENCY CASH_ADVANCE_FREQUENCY CASH_ADVANCE_TRX PURCHASES_TRX CREDIT_LIMIT PAYMENTS MINIMUM_PAYMENTS PRC_FULL_PAYMENT TENURE cluster
0 40.900749 0.818182 95.40 0.00 95.4 0.000000 0.166667 0.000000 0.083333 0.000000 0 2 1000.0 201.802084 139.509787 0.000000 12 6
1 3202.467416 0.909091 0.00 0.00 0.0 6442.945483 0.000000 0.000000 0.000000 0.250000 4 0 7000.0 4103.032597 1072.340217 0.222222 12 0
2 2495.148862 1.000000 773.17 773.17 0.0 0.000000 1.000000 1.000000 0.000000 0.000000 0 12 7500.0 622.066742 627.284787 0.000000 12 2
3 1666.670542 0.636364 1499.00 1499.00 0.0 205.788017 0.083333 0.083333 0.000000 0.083333 1 1 7500.0 0.000000 864.206542 0.000000 12 6
4 817.714335 1.000000 16.00 16.00 0.0 0.000000 0.083333 0.083333 0.000000 0.000000 0 1 1200.0 678.334763 244.791237 0.000000 12 6

Visualization

All the varialbes are plote according to the cluster assigned by k-Means. However, it is not possible to visualize all the 17 features. In this case a Principal Component Analysis is needed.

In [196]:
columnsName = creditcard_df_cluster.columns
for ii, columnName in enumerate(columnsName): 
    plt.figure(figsize=(35,5))
    
    for jj in range(7):
        plt.subplot(1, 7, jj+1)    
        plot_df = creditcard_df_cluster[creditcard_df_cluster['cluster'] == jj]    
        plt.hist(plot_df[columnName], alpha=.4, bins=30)
        plt.title('{}    \nCluster {} '.format(columnName, jj))
    
plt.tight_layout()

Principal Component Analysis (PCA)

PCA is a dimensional reduction technique. The idea is to be able to visualize the different clusters by reducing to 2 dimensions.

In [211]:
pca = PCA(n_components=2)
principal_comp = pca.fit_transform(creditcard_scaled)

# Create a dataframe with the two components
pca_df = pd.DataFrame(data = principal_comp, columns =['PCA 1','PCA 2'])
pca_df['Cluster'] = labels  
pca_df.head()
Out[211]:
PCA 1 PCA 2 Cluster
0 -1.682221 -1.076450 6
1 -1.138292 2.506460 0
2 0.969684 -0.383522 2
3 -0.873628 0.043165 6
4 -1.599434 -0.688579 6
In [214]:
plt.figure(figsize=(10,10))
sns.scatterplot(x="PCA 1", y="PCA 2", hue = "Cluster", data = pca_df, palette =['red','green','blue','pink','yellow','gray','purple'])
plt.xlabel('PCA 1')
plt.xlabel('PCA 2')
plt.title('Principal Component Analysis (PCA) by Cluster')
Out[214]:
Text(0.5, 1.0, 'Principal Component Analysis (PCA) by Cluster')

Conclusions

  • It was possible to use k-Means to perform customer segmentation of 8950 credit card users.
  • 7 Clusters can explain the customers behaviors
  • PCA can be used to visualize the clustering
In [ ]: