Mathew K Analytics

Lesson 40 · Data Science Projects

Building a Credit Card Fraud Detection System with Machine Learning Techniques

Understand what credit card fraud is Learn why machine learning helps spot fraud See how to use Python for fraud detection Work through a real world data…

⬇ Download notebookOpen in Colab ↗

What you'll learn

Data

No separate download needed — the notebook creates or downloads everything it uses.

📓 Full notebook

Download .ipynb

Credit Card Fraud Detection 101#

  • Understand what credit card fraud is
  • Learn why machine learning helps spot fraud
  • See how to use Python for fraud detection
  • Work through a real world data science project
  • No coding experience needed

Why is fraud detection important?#

  • Credit card fraud costs billions each year
  • Banks use data mining to catch stolen cards fast
  • Early detection protects both customers and banks
  • Machine learning can spot hidden patterns in money flows
  • You are about to do what real data scientists do
# Always suppress warnings in Jupyter notebooks
import warnings; warnings.filterwarnings("ignore")
import numpy as np
np.random.seed(42)
 

What you will learn in this notebook#

  • How to load a real credit card transaction dataset
  • What 'imbalanced data' really means and why it matters
  • Simple tricks for cleaning and exploring your data
  • Training your first fraud detector with Python
  • Measuring accuracy: how do you know if it works?
  • Mini challenge: try detecting fraud on new data
  • Support each other in the comments! Like and subscribe for more
# Data setup
import pandas as pd
url = 'https://storage.googleapis.com/download.tensorflow.org/data/creditcard.csv'
df = pd.read_csv(url)
print(df.shape)
print(df.head(3))
 
(284807, 31)
   Time        V1        V2        V3        V4        V5        V6        V7  \
0   0.0 -1.359807 -0.072781  2.536347  1.378155 -0.338321  0.462388  0.239599   
1   0.0  1.191857  0.266151  0.166480  0.448154  0.060018 -0.082361 -0.078803   
2   1.0 -1.358354 -1.340163  1.773209  0.379780 -0.503198  1.800499  0.791461   

         V8        V9  ...       V21       V22       V23       V24       V25  \
0  0.098698  0.363787  ... -0.018307  0.277838 -0.110474  0.066928  0.128539   
1  0.085102 -0.255425  ... -0.225775 -0.638672  0.101288 -0.339846  0.167170   
2  0.247676 -1.514654  ...  0.247998  0.771679  0.909412 -0.689281 -0.327642   

        V26       V27       V28  Amount  Class  
0 -0.189115  0.133558 -0.021053  149.62      0  
1  0.125895 -0.008983  0.014724    2.69      0  
2 -0.139097 -0.055353 -0.059752  378.66      0  

[3 rows x 31 columns]
# Quickly check basic info about each column
df.info()
 
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 284807 entries, 0 to 284806
Data columns (total 31 columns):
 #   Column  Non-Null Count   Dtype  
---  ------  --------------   -----  
 0   Time    284807 non-null  float64
 1   V1      284807 non-null  float64
 2   V2      284807 non-null  float64
 3   V3      284807 non-null  float64
 4   V4      284807 non-null  float64
 5   V5      284807 non-null  float64
 6   V6      284807 non-null  float64
 7   V7      284807 non-null  float64
 8   V8      284807 non-null  float64
 9   V9      284807 non-null  float64
 10  V10     284807 non-null  float64
 11  V11     284807 non-null  float64
 12  V12     284807 non-null  float64
 13  V13     284807 non-null  float64
 14  V14     284807 non-null  float64
 15  V15     284807 non-null  float64
 16  V16     284807 non-null  float64
 17  V17     284807 non-null  float64
 18  V18     284807 non-null  float64
 19  V19     284807 non-null  float64
 20  V20     284807 non-null  float64
 21  V21     284807 non-null  float64
 22  V22     284807 non-null  float64
 23  V23     284807 non-null  float64
 24  V24     284807 non-null  float64
 25  V25     284807 non-null  float64
 26  V26     284807 non-null  float64
 27  V27     284807 non-null  float64
 28  V28     284807 non-null  float64
 29  Amount  284807 non-null  float64
 30  Class   284807 non-null  int64  
dtypes: float64(30), int64(1)
memory usage: 67.4 MB
# How many fraudulent versus normal transactions do we have?
print(df['Class'].value_counts())
 
Class
0    284315
1       492
Name: count, dtype: int64
# See some fraudulent cases
df[df['Class'] == 1].head(3)
 
Time V1 V2 V3 V4 V5 V6 V7 V8 V9 ... V21 V22 V23 V24 V25 V26 V27 V28 Amount Class
541 406.0 -2.312227 1.951992 -1.609851 3.997906 -0.522188 -1.426545 -2.537387 1.391657 -2.770089 ... 0.517232 -0.035049 -0.465211 0.320198 0.044519 0.177840 0.261145 -0.143276 0.00 1
623 472.0 -3.043541 -3.157307 1.088463 2.288644 1.359805 -1.064823 0.325574 -0.067794 -0.270953 ... 0.661696 0.435477 1.375966 -0.293803 0.279798 -0.145362 -0.252773 0.035764 529.00 1
4920 4462.0 -2.303350 1.759247 -0.359745 2.330243 -0.821628 -0.075788 0.562320 -0.399147 -0.238253 ... -0.294166 -0.932391 0.172726 -0.087330 -0.156114 -0.542628 0.039566 -0.153029 239.93 1

3 rows × 31 columns

# Explore column names and the first few more rows
print(df.columns.tolist())
df.head()
 
['Time', 'V1', 'V2', 'V3', 'V4', 'V5', 'V6', 'V7', 'V8', 'V9', 'V10', 'V11', 'V12', 'V13', 'V14', 'V15', 'V16', 'V17', 'V18', 'V19', 'V20', 'V21', 'V22', 'V23', 'V24', 'V25', 'V26', 'V27', 'V28', 'Amount', 'Class']
Time V1 V2 V3 V4 V5 V6 V7 V8 V9 ... V21 V22 V23 V24 V25 V26 V27 V28 Amount Class
0 0.0 -1.359807 -0.072781 2.536347 1.378155 -0.338321 0.462388 0.239599 0.098698 0.363787 ... -0.018307 0.277838 -0.110474 0.066928 0.128539 -0.189115 0.133558 -0.021053 149.62 0
1 0.0 1.191857 0.266151 0.166480 0.448154 0.060018 -0.082361 -0.078803 0.085102 -0.255425 ... -0.225775 -0.638672 0.101288 -0.339846 0.167170 0.125895 -0.008983 0.014724 2.69 0
2 1.0 -1.358354 -1.340163 1.773209 0.379780 -0.503198 1.800499 0.791461 0.247676 -1.514654 ... 0.247998 0.771679 0.909412 -0.689281 -0.327642 -0.139097 -0.055353 -0.059752 378.66 0
3 1.0 -0.966272 -0.185226 1.792993 -0.863291 -0.010309 1.247203 0.237609 0.377436 -1.387024 ... -0.108300 0.005274 -0.190321 -1.175575 0.647376 -0.221929 0.062723 0.061458 123.50 0
4 2.0 -1.158233 0.877737 1.548718 0.403034 -0.407193 0.095921 0.592941 -0.270533 0.817739 ... -0.009431 0.798278 -0.137458 0.141267 -0.206010 0.502292 0.219422 0.215153 69.99 0

5 rows × 31 columns

# Check for missing values: is anything blank?
print(df.isnull().sum().sum())
 
0

Visualizing card transaction amounts#

  • Fraud can be easier to spot by visualizing money flows
  • Let us plot the amount of each transaction for both fraud and normal
  • See if amounts look different for fraud cases
# Simple histogram plot of amounts by class
import seaborn as sns
import matplotlib.pyplot as plt
sns.histplot(df[df['Class'] == 0]['Amount'], bins=50, color='blue', label='Normal', alpha=0.5)
sns.histplot(df[df['Class'] == 1]['Amount'], bins=50, color='red', label='Fraudulent', alpha=0.7)
plt.legend()
plt.title('Transaction Amount By Fraud Status')
plt.xlabel('Amount')
plt.ylabel('Count')
plt.show()
 
No description has been provided for this image
# Preview how data looks over time
df['Hour'] = (df['Time'] // 3600) % 24
sns.countplot(x='Hour', hue='Class', data=df, palette={0:'blue',1:'red'})
plt.title('Fraud Counts By Hour of Day')
plt.ylabel('Transactions')
plt.xlabel('Hour')
plt.legend(['Normal','Fraud'], loc='upper right')
plt.show()
 
No description has been provided for this image

Prepare features for machine learning#

  • Time to split data for training and testing our model
  • Aim: teach the computer what fraud looks like using labeled data
  • Test on data it has never seen before
# Split data into inputs (X) and label (y)
X = df.drop(['Class'], axis=1)
y = df['Class']
 
# Split into train and test sets
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42, stratify=y)
print('Training set:', X_train.shape, y_train.shape)
print('Test set:', X_test.shape, y_test.shape)
 
Training set: (213605, 31) (213605,)
Test set: (71202, 31) (71202,)
# Let us build a simple model: Logistic Regression
from sklearn.linear_model import LogisticRegression
model = LogisticRegression(max_iter=1000, random_state=42, class_weight='balanced')
model.fit(X_train, y_train)
 
LogisticRegression(class_weight='balanced', max_iter=1000, random_state=42)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
# Predict on test set and check accuracy
y_pred = model.predict(X_test)
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
print('Accuracy:', accuracy_score(y_test, y_pred))
print(classification_report(y_test, y_pred, target_names=['Not Fraud', 'Fraud']))
print(confusion_matrix(y_test, y_pred))
 
Accuracy: 0.9691581697143339
              precision    recall  f1-score   support

   Not Fraud       1.00      0.97      0.98     71079
       Fraud       0.05      0.89      0.09       123

    accuracy                           0.97     71202
   macro avg       0.52      0.93      0.54     71202
weighted avg       1.00      0.97      0.98     71202

[[68897  2182]
 [   14   109]]
# Visualize confusion matrix as a heatmap
import seaborn as sns
cm = confusion_matrix(y_test, y_pred)
sns.heatmap(cm, annot=True, fmt='d', cmap='Reds', xticklabels=['Not Fraud','Fraud'], yticklabels=['Not Fraud','Fraud'])
plt.xlabel('Predicted')
plt.ylabel('Actual')
plt.title('Confusion Matrix')
plt.show()
 
No description has been provided for this image
# Try using a decision tree for comparison
from sklearn.tree import DecisionTreeClassifier
tree = DecisionTreeClassifier(max_depth=5, class_weight='balanced', random_state=42)
tree.fit(X_train, y_train)
y_tree_pred = tree.predict(X_test)
print('Accuracy (Decision Tree):', accuracy_score(y_test, y_tree_pred))
print(classification_report(y_test, y_tree_pred, target_names=['Not Fraud', 'Fraud']))
 
Accuracy (Decision Tree): 0.9800988736271453
              precision    recall  f1-score   support

   Not Fraud       1.00      0.98      0.99     71079
       Fraud       0.07      0.85      0.13       123

    accuracy                           0.98     71202
   macro avg       0.53      0.91      0.56     71202
weighted avg       1.00      0.98      0.99     71202

# See which features matter most for our decision tree
importances = tree.feature_importances_
features = X.columns
feat_importances = pd.Series(importances, index=features)
feat_importances.nlargest(10).plot(kind='barh')
plt.xlabel('Importance Score')
plt.title('Top 10 Feature Importances for Fraud Detection')
plt.show()
 
No description has been provided for this image

YouTube Mini Project: Your Turn!#

  • Try changing the max_depth for the tree above
  • What happens to fraud detection accuracy?
  • Can you balance between catching more fraud and causing less false alarms?
  • Share your results and ideas in the comments
  • Like and subscribe for more real-world data science tutorials
# Optional: try your own test amount and get the prediction
test_values = X_test.iloc[0].values.reshape(1, -1)
prediction = model.predict(test_values)[0]
if prediction == 1:
    result = "Fraud!"
else:
    result = "Not Fraud."
print("Prediction:", result)
 
Prediction: Not Fraud.

What you achieved today!#

  • Loaded real world credit card data in Python
  • Learned why fraud detection is hard and important
  • Explored, visualized, and split the data safely
  • Trained both a logistic regression and a decision tree model
  • Measured their ability to detect rare frauds
  • Used feature importances and heatmaps for deep understanding
  • You now have practical data mining experience
  • Keep practicing, and you will spot trickier patterns and build better tools!

Found this useful?

All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.