diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e2a7d61
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/Anomaly Detection/dataset
\ No newline at end of file
diff --git a/Anomaly Detection/README.md b/Anomaly Detection/README.md
new file mode 100644
index 0000000..dc4f2d9
--- /dev/null
+++ b/Anomaly Detection/README.md
@@ -0,0 +1,79 @@
+# Anomaly Detection in Credit Card Fraud Transactions
+
+This project demonstrates a simple but strong anomaly detection workflow using the Credit Card Fraud Detection dataset.
+
+## Overview
+
+Credit card fraud detection is a highly imbalanced anomaly detection problem. Most transactions are normal, while fraudulent transactions are rare. This notebook compares three popular unsupervised anomaly detection algorithms:
+
+- Isolation Forest
+- Local Outlier Factor
+- One-Class SVM
+
+The notebook includes:
+
+- Data loading
+- Class distribution analysis
+- Feature scaling
+- Model training
+- Prediction conversion from anomaly labels to binary labels
+- Evaluation using classification report, confusion matrix, ROC-AUC, and PR-AUC
+- A summary table comparing all models
+
+## Dataset
+
+Use the Credit Card Fraud Detection dataset from Kaggle:
+
+https://www.kaggle.com/datasets/mlg-ulb/creditcardfraud
+
+Download `creditcard.csv` and place it in this project folder before running the notebook.
+
+Expected structure:
+
+```text
+Anomaly Detection/
+├── README.md
+├── anomaly_detection.ipynb
+|── dataset/
+└──── creditcard.csv
+```
+
+## How to Run
+
+Install the required packages:
+
+```bash
+pip install pandas numpy matplotlib scikit-learn
+```
+
+Then open the notebook:
+
+```bash
+jupyter notebook anomaly_detection.ipynb
+```
+
+## Models Used
+
+### Isolation Forest
+Isolation Forest detects anomalies by randomly isolating observations. Anomalies are expected to require fewer random splits.
+
+### Local Outlier Factor
+Local Outlier Factor detects anomalies by comparing the local density of a sample with the density of nearby samples.
+
+### One-Class SVM
+One-Class SVM learns a boundary around normal samples and treats points outside the boundary as anomalies.
+
+## Evaluation Metrics
+
+Because the dataset is highly imbalanced, accuracy alone is not reliable. This project reports:
+
+- Precision
+- Recall
+- F1-score
+- ROC-AUC
+- PR-AUC
+- Confusion matrix
+
+## Notes
+
+The models are trained in an unsupervised way by removing the label column during training. The labels are only used for evaluation.
diff --git a/Anomaly Detection/anomaly_detection.ipynb b/Anomaly Detection/anomaly_detection.ipynb
new file mode 100644
index 0000000..0f03026
--- /dev/null
+++ b/Anomaly Detection/anomaly_detection.ipynb
@@ -0,0 +1,911 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "7488e70f",
+ "metadata": {},
+ "source": [
+ "# Anomaly Detection in Credit Card Fraud Transactions\n",
+ "\n",
+ "This notebook presents a simple but strong anomaly detection workflow for credit card fraud detection.\n",
+ "\n",
+ "We compare three unsupervised anomaly detection methods:\n",
+ "\n",
+ "- Isolation Forest\n",
+ "- Local Outlier Factor\n",
+ "- One-Class SVM\n",
+ "\n",
+ "The dataset is highly imbalanced, so we evaluate models using precision, recall, F1-score, ROC-AUC, PR-AUC, and confusion matrices instead of relying only on accuracy.\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b272eb73",
+ "metadata": {},
+ "source": [
+ "## 1. Import Libraries"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "1ef5ceea",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "import warnings\n",
+ "warnings.filterwarnings(\"ignore\")\n",
+ "\n",
+ "import numpy as np\n",
+ "import pandas as pd\n",
+ "import matplotlib.pyplot as plt\n",
+ "\n",
+ "from sklearn.model_selection import train_test_split\n",
+ "from sklearn.preprocessing import StandardScaler\n",
+ "from sklearn.ensemble import IsolationForest\n",
+ "from sklearn.neighbors import LocalOutlierFactor\n",
+ "from sklearn.svm import OneClassSVM\n",
+ "from sklearn.metrics import (\n",
+ " classification_report,\n",
+ " confusion_matrix,\n",
+ " roc_auc_score,\n",
+ " average_precision_score,\n",
+ " precision_score,\n",
+ " recall_score,\n",
+ " f1_score,\n",
+ ")\n",
+ "\n",
+ "RANDOM_STATE = 42\n",
+ "np.random.seed(RANDOM_STATE)\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "60fccad2",
+ "metadata": {},
+ "source": [
+ "## 2. Load Dataset\n",
+ "\n",
+ "Download the dataset from Kaggle and place `creditcard.csv` in the same folder as this notebook:\n",
+ "\n",
+ "https://www.kaggle.com/datasets/mlg-ulb/creditcardfraud\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "82b0966b",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Dataset shape: (284807, 31)\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " Time | \n",
+ " V1 | \n",
+ " V2 | \n",
+ " V3 | \n",
+ " V4 | \n",
+ " V5 | \n",
+ " V6 | \n",
+ " V7 | \n",
+ " V8 | \n",
+ " V9 | \n",
+ " ... | \n",
+ " V21 | \n",
+ " V22 | \n",
+ " V23 | \n",
+ " V24 | \n",
+ " V25 | \n",
+ " V26 | \n",
+ " V27 | \n",
+ " V28 | \n",
+ " Amount | \n",
+ " Class | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " 0.0 | \n",
+ " -1.359807 | \n",
+ " -0.072781 | \n",
+ " 2.536347 | \n",
+ " 1.378155 | \n",
+ " -0.338321 | \n",
+ " 0.462388 | \n",
+ " 0.239599 | \n",
+ " 0.098698 | \n",
+ " 0.363787 | \n",
+ " ... | \n",
+ " -0.018307 | \n",
+ " 0.277838 | \n",
+ " -0.110474 | \n",
+ " 0.066928 | \n",
+ " 0.128539 | \n",
+ " -0.189115 | \n",
+ " 0.133558 | \n",
+ " -0.021053 | \n",
+ " 149.62 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " 0.0 | \n",
+ " 1.191857 | \n",
+ " 0.266151 | \n",
+ " 0.166480 | \n",
+ " 0.448154 | \n",
+ " 0.060018 | \n",
+ " -0.082361 | \n",
+ " -0.078803 | \n",
+ " 0.085102 | \n",
+ " -0.255425 | \n",
+ " ... | \n",
+ " -0.225775 | \n",
+ " -0.638672 | \n",
+ " 0.101288 | \n",
+ " -0.339846 | \n",
+ " 0.167170 | \n",
+ " 0.125895 | \n",
+ " -0.008983 | \n",
+ " 0.014724 | \n",
+ " 2.69 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 1.0 | \n",
+ " -1.358354 | \n",
+ " -1.340163 | \n",
+ " 1.773209 | \n",
+ " 0.379780 | \n",
+ " -0.503198 | \n",
+ " 1.800499 | \n",
+ " 0.791461 | \n",
+ " 0.247676 | \n",
+ " -1.514654 | \n",
+ " ... | \n",
+ " 0.247998 | \n",
+ " 0.771679 | \n",
+ " 0.909412 | \n",
+ " -0.689281 | \n",
+ " -0.327642 | \n",
+ " -0.139097 | \n",
+ " -0.055353 | \n",
+ " -0.059752 | \n",
+ " 378.66 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 1.0 | \n",
+ " -0.966272 | \n",
+ " -0.185226 | \n",
+ " 1.792993 | \n",
+ " -0.863291 | \n",
+ " -0.010309 | \n",
+ " 1.247203 | \n",
+ " 0.237609 | \n",
+ " 0.377436 | \n",
+ " -1.387024 | \n",
+ " ... | \n",
+ " -0.108300 | \n",
+ " 0.005274 | \n",
+ " -0.190321 | \n",
+ " -1.175575 | \n",
+ " 0.647376 | \n",
+ " -0.221929 | \n",
+ " 0.062723 | \n",
+ " 0.061458 | \n",
+ " 123.50 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 2.0 | \n",
+ " -1.158233 | \n",
+ " 0.877737 | \n",
+ " 1.548718 | \n",
+ " 0.403034 | \n",
+ " -0.407193 | \n",
+ " 0.095921 | \n",
+ " 0.592941 | \n",
+ " -0.270533 | \n",
+ " 0.817739 | \n",
+ " ... | \n",
+ " -0.009431 | \n",
+ " 0.798278 | \n",
+ " -0.137458 | \n",
+ " 0.141267 | \n",
+ " -0.206010 | \n",
+ " 0.502292 | \n",
+ " 0.219422 | \n",
+ " 0.215153 | \n",
+ " 69.99 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
5 rows × 31 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " Time V1 V2 V3 V4 V5 V6 V7 \\\n",
+ "0 0.0 -1.359807 -0.072781 2.536347 1.378155 -0.338321 0.462388 0.239599 \n",
+ "1 0.0 1.191857 0.266151 0.166480 0.448154 0.060018 -0.082361 -0.078803 \n",
+ "2 1.0 -1.358354 -1.340163 1.773209 0.379780 -0.503198 1.800499 0.791461 \n",
+ "3 1.0 -0.966272 -0.185226 1.792993 -0.863291 -0.010309 1.247203 0.237609 \n",
+ "4 2.0 -1.158233 0.877737 1.548718 0.403034 -0.407193 0.095921 0.592941 \n",
+ "\n",
+ " V8 V9 ... V21 V22 V23 V24 V25 \\\n",
+ "0 0.098698 0.363787 ... -0.018307 0.277838 -0.110474 0.066928 0.128539 \n",
+ "1 0.085102 -0.255425 ... -0.225775 -0.638672 0.101288 -0.339846 0.167170 \n",
+ "2 0.247676 -1.514654 ... 0.247998 0.771679 0.909412 -0.689281 -0.327642 \n",
+ "3 0.377436 -1.387024 ... -0.108300 0.005274 -0.190321 -1.175575 0.647376 \n",
+ "4 -0.270533 0.817739 ... -0.009431 0.798278 -0.137458 0.141267 -0.206010 \n",
+ "\n",
+ " V26 V27 V28 Amount Class \n",
+ "0 -0.189115 0.133558 -0.021053 149.62 0 \n",
+ "1 0.125895 -0.008983 0.014724 2.69 0 \n",
+ "2 -0.139097 -0.055353 -0.059752 378.66 0 \n",
+ "3 -0.221929 0.062723 0.061458 123.50 0 \n",
+ "4 0.502292 0.219422 0.215153 69.99 0 \n",
+ "\n",
+ "[5 rows x 31 columns]"
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "DATA_PATH = \"dataset/creditcard.csv\"\n",
+ "\n",
+ "if not os.path.exists(DATA_PATH):\n",
+ " raise FileNotFoundError(\n",
+ " \"creditcard.csv was not found. Download it from Kaggle and place it in this folder.\"\n",
+ " )\n",
+ "\n",
+ "df = pd.read_csv(DATA_PATH)\n",
+ "print(\"Dataset shape:\", df.shape)\n",
+ "df.head()\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "24e01823",
+ "metadata": {},
+ "source": [
+ "## 3. Basic Data Analysis"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "577b6f83",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Missing values: 0\n",
+ "Duplicate rows: 1081\n",
+ "Columns: ['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']\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(\"Missing values:\", df.isnull().sum().sum())\n",
+ "print(\"Duplicate rows:\", df.duplicated().sum())\n",
+ "print(\"Columns:\", list(df.columns))\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "f52b83fa",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " Class | \n",
+ " Count | \n",
+ " Percentage | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " Normal | \n",
+ " 284315 | \n",
+ " 99.827251 | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " Fraud | \n",
+ " 492 | \n",
+ " 0.172749 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " Class Count Percentage\n",
+ "0 Normal 284315 99.827251\n",
+ "1 Fraud 492 0.172749"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "class_counts = df[\"Class\"].value_counts().sort_index()\n",
+ "class_ratio = df[\"Class\"].value_counts(normalize=True).sort_index() * 100\n",
+ "\n",
+ "summary = pd.DataFrame({\n",
+ " \"Class\": [\"Normal\", \"Fraud\"],\n",
+ " \"Count\": class_counts.values,\n",
+ " \"Percentage\": class_ratio.values,\n",
+ "})\n",
+ "summary\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "8d34cd81",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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9vXXX8f6OjOmAABAqhpzo72etA3CkiVL3BYFs2bNsgkTJrgVi7WzNwAAQKpqudGMKG10eeONN1qaNGmsWLFiVq9ePbcMsqaDN27cOHFqCgAAkBgtN8eOHbN8+fK5x9qBW91Uol24f/755/heDgAAIHnDjda3+e2339zjihUr2ujRo+3vv/+2t99+2woWLBjZ2gEAACR2t5TG3OzYscM97tOnjzVo0MAmTZpkGTJksPHjx8f3cgAAAMkbbu6///7g4xtuuMG2bNniFvArWrSo5cmTJ7K1AwAASOxuqegyZszoBhanTZs2oZcCAABInqngY8aMCa5po32mKlWqZEWKFLFvv/024TUCAABIynDz6aefWoUKFdzj6dOnB7ulFHqef/75hNQFAAAg6cPN3r17rUCBAu7xzJkz7Z577rGSJUtahw4dbM2aNQmvEQAAQFKGm/z589u6detcl5RWJ65bt25wrynG3QAAgFQ3W0r7SrVq1cqtaRMVFeVWJ5alS5faddddlxh1BAAASLxw07dvXytbtqxt27bNdUlptpSo1aZXr17xvRwAAEDyhhu5++67Y5xr165dJOoDAACQ9OFm3rx57ti9e7edO3cu7LWxY8cmrEYAAABJGW769etn/fv3typVqgTH3QAAAKTacKMNMrWHVNu2bROnRgAAAEk5Ffz06dNWvXr1hNwTAAAg5YSbRx55xCZPnpw4tQEAAEjqbqmTJ0/aO++8Y998842VL1/e0qdPH/b68OHDE1onAACApAs3q1evtooVK7rHa9euDXuNwcUAACDVhZsFCxYkTk0AAACSY8wNAACA7xbxW758uX3yySe2detWN3sq1GeffRapugEAACR+y82UKVOsRo0abmfwadOm2ZkzZ9zj+fPnW/bs2eNfAwAAgOQMNwMHDrQRI0bYV199ZRkyZLD//e9/tn79erdTeNGiRSNZNwAAgMQPN3/++ac1btzYPdaO4MeOHXOzpJ566ik3RRwAACBVhZtcuXLZkSNH3OPChQsHp4MfPHjQjh8/HvkaAgAAJOaA4ltvvdXmzp1r5cqVc11RTz75pBtvo3N16tSJ7+UAAACSN9y88cYbbpVi6d27t1uheOHChdayZUt74YUXIls7AACAxAw3//77r02fPt0aNGjgnqdJk8Z69OjhDgAAgFQ35iZdunT23//+106dOpV4NQIA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rbcurRYAAADJFW6aN28e9jxNmjSWN29eq1Wrll133XWRqhcAAEDShJs+ffpc2p0AAACSAIv4AQCAy7PlRt1PF1q8T/T6v//+G4l6AQAAJG64mTZt2nlfW7Rokb3++utucDEAAECqCDfNmjWLcW7Dhg3Wu3dvmz59ut1///320ksvRbp+AAAAiT/m5p9//rGOHTta+fLlXTfUqlWrbMKECVa0aNFLuRwAAEDyhJtDhw5Zz5497ZprrrFff/3V5s2b51ptypYtG7kaAQAAJEW31JAhQ+yVV16xAgUK2IcffhhrNxUAAEByiwrEcRSwZktlypTJ6tata2nTpj1vuc8++yyS9fOdw4cPW/bs2V0rWLZs2SJ23eK9ZkTsWkBKt2Vw4+SuAoAU/Bka55abBx988KJTwQEAAJJbnMPN+PHjE7cmAAAAEcAKxQAAwFcINwAAwFcINwAAwFcINwAAwFcINwAAwFcINwAAwFcINwAAwFcINwAAwFcINwAAwFcINwAAwFcINwAAwFcINwAAwFcINwAAwFcINwAAwFdSdLjp27evRUVFhR0FChQIvh4IBFyZQoUKWaZMmaxWrVr266+/hl3j1KlT1qVLF8uTJ49lyZLFmjZtatu3bw8rc+DAAWvbtq1lz57dHXp88ODBsDJbt261Jk2auGvoWl27drXTp08n8ncAAAD4KtzI9ddfbzt27Agea9asCb42ZMgQGz58uL3xxhu2fPlyF3zq1atnR44cCZbp1q2bTZs2zaZMmWILFy60o0eP2p133mlnz54NlmnTpo2tWrXKZs2a5Q49VsDxqGzjxo3t2LFj7hq61tSpU6179+5J+J0AAABxkc5SuHTp0oW11oS22owcOdKef/55a9mypTs3YcIEy58/v02ePNkeffRRO3TokI0ZM8YmTpxodevWdWU++OADK1KkiH3zzTfWoEEDW79+vQs0S5YssZtvvtmVeffdd61atWr222+/WalSpWzOnDm2bt0627Ztm2slkldffdXat29vL7/8smXLli1JvycAACAVt9z8/vvvLlCUKFHC7r33Xtu0aZM7v3nzZtu5c6fVr18/WDZjxoxWs2ZNW7RokXu+YsUKO3PmTFgZXats2bLBMosXL3ZdUV6wkapVq7pzoWX0Hi/YiIKRurx0jwtRmcOHD4cdAADgMg03Chzvv/++zZ4927WmKMxU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CxQokKj3WL58uZ09eza4C/1dd93lvp/ZsmVz57TBKoBLQ8sNgDjRh713ZM+e3bUueM9PnjxpOXLkcBuQqsXhiiuusA8++MD27dtn9913n1111VWWOXNmtyv7hx9+GHZdle/atav16NHDcuXK5a7Xt2/fsDJ6rh3VM2bMaIUKFXLlPbpPlSpV3AaHem+bNm1s9+7dYe//9ddfrXHjxi44qNytt97qdoXWdbUL/BdffBFsMfn2229j7Zb67rvv7KabbnJ1KFiwoPXq1cvtQh2fryP6Jqre90/lJV++fMGvQeExlL6Xuvf8+fODLT/aBFZlr7zySvd9Cd3tXg4dOmSdOnVy19XXfvvtt9svv/wSp583kJoRbgBETM+ePd0H/Pr1661BgwYu9FSuXNm++uorW7t2rfugbdu2rS1dujTsfQoYWbJkceeHDBnidmWfO3eue+3TTz+1ESNG2OjRo+3333+3zz//3IUkz+nTp92HvD609drmzZvDusj+/vtvu+2221zgUjBYsWKFPfzwwy6YaEfoVq1aWcOGDV3LlI7q1avH+Lp0jTvuuMNuvPFGd59Ro0bZmDFjbMCAAXH+OuLjkUcescmTJ9upU6eC57TLtQJM7dq1g+eGDh1q5cuXt59//tl69+5tTz31VPB+2hNZgW7nzp02c+ZM93VXqlTJ6tSpY/v37493nYBURbuCA0B8jBs3LpA9e/bg882bNwf0n5ORI0de9L133HFHoHv37sHnNWvWDNxyyy1hZW688cZAz5493eNXX301ULJkycDp06fjVLdly5a5uhw5csQ97927d6BEiRLnfX+7du0CzZo1CzvnfT0rV650z5977rlAqVKlAufOnQuWefPNNwNXXnll4OzZs3H6Oi5kwYIF7n4HDhxwz0+ePBnIlStX4KOPPgqWqVixYqBv377B58WKFQs0bNgw7DqtW7cONGrUyD2eN29eIFu2bO5aoa6++urA6NGjL1onIDWj5QZAxKh7KJTGlLz88suudSF37tyu+2TOnDm2devWsHJ6PZS6fbyupXvuucdOnDhh//nPf6xjx442bdq0sO6glStXWrNmzaxYsWKuy8kbiOvdQ11L6oZKyPgftURVq1bNdVV5atSoYUePHrXt27fH6euID3U/PfDAAzZ27Njg16AWo+iDtlWn6M9VV1FLjernfd+9Qy1b6pID/IwBxQAiRl0yoV599VXXpTRy5EjXlaTXu3Xr5rqSQkUPHgoR586dC85c0kBbdbd888031rlzZ9cdozEwuk79+vXdobE3GseiUKMuMe8e0Qc6Xwp18YQGG++cV9e4fB2X0jVVsWJFF54UctSdpAB3MV59dF+FK40hik7jowA/I9wASDQ//PCDa1VRK4T3gatxM6VLl47XdRRQmjZt6o7HH3/crrvuOluzZo0LGHv37rXBgwe7ECQ//fRT2HvVmqKxMOebvaVZSd6spfMpU6aMTZ06NSzkaIaTWooKFy5siUFhUC1h7777rht/E32wsCxZsiTGc31vRONrNN4mXbp0bvAxcDmhWwpAornmmmtci4uCgLpLHn30UfeBGx/jx493g3c1IHnTpk02ceJEF3bUiqEZVAon+uDXa19++aUbXBzqiSeesMOHD9u9997rgo/Cla7hTWPXB//q1avdcwUlhaDo1Fq0bds269Kli23YsMHNrurTp489/fTTliZN4v1nVK03Cm4KXy1atIjx+o8//ugGLm/cuNHefPNN++STT+zJJ590r9WtW9d1U2kNn9mzZ7sZYPo5/N///V+MAAj4DeEGQKJ54YUXXAuCuok0FkbTnKMvmHcx6kJR64XGuKgVZt68eTZ9+nQ3lkTdUAo/+lBX64qCwLBhw8Ler3KaJaXxJzVr1nSzt3Q9rxVH43hKlSrlWkl0PQWG6NQ6oxlHy5YtswoVKthjjz1mHTp0cEEhMWkavVpeNN1bs72i6969uxtbc8MNN7hQp25Afa9FLUyqs2aKaXZYyZIlXcBTyMmfP3+i1htIblEaVZzclQAAxKTWIrUsacE/hcRQOq/xSzoAhGPMDQCkMOoa05o7WiiwatWqMYINgAujWwoAUhh1jWlMkbqc3n777eSuDpDq0C0FAAB8hZYbAADgK4QbAADgK4QbAADgK4QbAADgK4QbAADgK4QbAADgK4QbAADgK4QbAABgfvL/AeK+tUYmBhC8AAAAAElFTkSuQmCC",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "plt.figure(figsize=(6, 4))\n",
+ "plt.bar([\"Normal\", \"Fraud\"], class_counts.values)\n",
+ "plt.title(\"Class Distribution\")\n",
+ "plt.xlabel(\"Transaction Type\")\n",
+ "plt.ylabel(\"Number of Transactions\")\n",
+ "plt.show()\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d4fe0f01",
+ "metadata": {},
+ "source": [
+ "## 4. Prepare Features and Labels\n",
+ "\n",
+ "The models are trained without using the `Class` label. The label is used only for evaluation.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "1e5eb677",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Feature matrix: (284807, 30)\n",
+ "Labels: (284807,)\n"
+ ]
+ }
+ ],
+ "source": [
+ "X = df.drop(columns=[\"Class\"])\n",
+ "y = df[\"Class\"].astype(int)\n",
+ "\n",
+ "print(\"Feature matrix:\", X.shape)\n",
+ "print(\"Labels:\", y.shape)\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "04c66b7a",
+ "metadata": {},
+ "source": [
+ "## 5. Train-Test Split and Scaling\n",
+ "\n",
+ "We use stratified splitting to preserve the fraud ratio in both train and test sets.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "dabf3d56",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Train shape: (199364, 30)\n",
+ "Test shape: (85443, 30)\n",
+ "Fraud ratio in train set: 0.0017254870488152324\n"
+ ]
+ }
+ ],
+ "source": [
+ "X_train, X_test, y_train, y_test = train_test_split(\n",
+ " X,\n",
+ " y,\n",
+ " test_size=0.30,\n",
+ " random_state=RANDOM_STATE,\n",
+ " stratify=y,\n",
+ ")\n",
+ "\n",
+ "scaler = StandardScaler()\n",
+ "X_train_scaled = scaler.fit_transform(X_train)\n",
+ "X_test_scaled = scaler.transform(X_test)\n",
+ "\n",
+ "fraud_ratio = y_train.mean()\n",
+ "print(\"Train shape:\", X_train_scaled.shape)\n",
+ "print(\"Test shape:\", X_test_scaled.shape)\n",
+ "print(\"Fraud ratio in train set:\", fraud_ratio)\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "fc12f29f",
+ "metadata": {},
+ "source": [
+ "## 6. Helper Function for Evaluation\n",
+ "\n",
+ "Scikit-learn anomaly detectors usually return:\n",
+ "\n",
+ "- `1` for normal samples\n",
+ "- `-1` for anomalies\n",
+ "\n",
+ "We convert this format into binary labels:\n",
+ "\n",
+ "- `0` = normal\n",
+ "- `1` = fraud/anomaly\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "b80b563c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def convert_anomaly_labels(predictions):\n",
+ " \"\"\"Convert sklearn anomaly labels from {1, -1} to {0, 1}.\"\"\"\n",
+ " return np.where(predictions == -1, 1, 0)\n",
+ "\n",
+ "\n",
+ "def evaluate_model(model_name, y_true, y_pred, anomaly_score=None):\n",
+ " \"\"\"Evaluate an anomaly detection model and return key metrics.\"\"\"\n",
+ " print(f\"================ {model_name} ================\")\n",
+ " print(\"Confusion Matrix:\")\n",
+ " print(confusion_matrix(y_true, y_pred))\n",
+ " print(\"Classification Report:\")\n",
+ " print(classification_report(y_true, y_pred, digits=4))\n",
+ "\n",
+ " result = {\n",
+ " \"Model\": model_name,\n",
+ " \"Precision\": precision_score(y_true, y_pred, zero_division=0),\n",
+ " \"Recall\": recall_score(y_true, y_pred, zero_division=0),\n",
+ " \"F1-score\": f1_score(y_true, y_pred, zero_division=0),\n",
+ " }\n",
+ "\n",
+ " if anomaly_score is not None:\n",
+ " result[\"ROC-AUC\"] = roc_auc_score(y_true, anomaly_score)\n",
+ " result[\"PR-AUC\"] = average_precision_score(y_true, anomaly_score)\n",
+ " else:\n",
+ " result[\"ROC-AUC\"] = np.nan\n",
+ " result[\"PR-AUC\"] = np.nan\n",
+ "\n",
+ " return result\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d80add0c",
+ "metadata": {},
+ "source": [
+ "## 7. Model 1: Isolation Forest\n",
+ "\n",
+ "Isolation Forest is a strong baseline for anomaly detection. It isolates anomalies using random partitions.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "id": "c582ca3c",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "================ Isolation Forest ================\n",
+ "Confusion Matrix:\n",
+ "[[85188 107]\n",
+ " [ 113 35]]\n",
+ "Classification Report:\n",
+ " precision recall f1-score support\n",
+ "\n",
+ " 0 0.9987 0.9987 0.9987 85295\n",
+ " 1 0.2465 0.2365 0.2414 148\n",
+ "\n",
+ " accuracy 0.9974 85443\n",
+ " macro avg 0.6226 0.6176 0.6200 85443\n",
+ "weighted avg 0.9974 0.9974 0.9974 85443\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "iso_forest = IsolationForest(\n",
+ " n_estimators=200,\n",
+ " contamination=fraud_ratio,\n",
+ " random_state=RANDOM_STATE,\n",
+ " n_jobs=-1,\n",
+ ")\n",
+ "\n",
+ "iso_forest.fit(X_train_scaled)\n",
+ "\n",
+ "iso_pred_raw = iso_forest.predict(X_test_scaled)\n",
+ "iso_pred = convert_anomaly_labels(iso_pred_raw)\n",
+ "\n",
+ "# Higher score means more anomalous.\n",
+ "iso_score = -iso_forest.decision_function(X_test_scaled)\n",
+ "\n",
+ "results = []\n",
+ "results.append(evaluate_model(\"Isolation Forest\", y_test, iso_pred, iso_score))\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ff979e59",
+ "metadata": {},
+ "source": [
+ "## 8. Model 2: Local Outlier Factor\n",
+ "\n",
+ "Local Outlier Factor compares the local density of each point with the density of its neighbors.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "id": "552adfcb",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "================ Local Outlier Factor ================\n",
+ "Confusion Matrix:\n",
+ "[[85158 137]\n",
+ " [ 148 0]]\n",
+ "Classification Report:\n",
+ " precision recall f1-score support\n",
+ "\n",
+ " 0 0.9983 0.9984 0.9983 85295\n",
+ " 1 0.0000 0.0000 0.0000 148\n",
+ "\n",
+ " accuracy 0.9967 85443\n",
+ " macro avg 0.4991 0.4992 0.4992 85443\n",
+ "weighted avg 0.9965 0.9967 0.9966 85443\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "lof = LocalOutlierFactor(\n",
+ " n_neighbors=20,\n",
+ " contamination=fraud_ratio,\n",
+ " novelty=True,\n",
+ " n_jobs=-1,\n",
+ ")\n",
+ "\n",
+ "lof.fit(X_train_scaled)\n",
+ "\n",
+ "lof_pred_raw = lof.predict(X_test_scaled)\n",
+ "lof_pred = convert_anomaly_labels(lof_pred_raw)\n",
+ "\n",
+ "# Higher score means more anomalous.\n",
+ "lof_score = -lof.decision_function(X_test_scaled)\n",
+ "\n",
+ "results.append(evaluate_model(\"Local Outlier Factor\", y_test, lof_pred, lof_score))\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9691ad5a",
+ "metadata": {},
+ "source": [
+ "## 9. Model 3: One-Class SVM\n",
+ "\n",
+ "One-Class SVM can be useful, but it is computationally expensive on large datasets. To keep the notebook beginner-friendly and fast, we train it on a subset of normal training samples.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "id": "dbc0c3dd",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "================ One-Class SVM ================\n",
+ "Confusion Matrix:\n",
+ "[[82728 2567]\n",
+ " [ 28 120]]\n",
+ "Classification Report:\n",
+ " precision recall f1-score support\n",
+ "\n",
+ " 0 0.9997 0.9699 0.9846 85295\n",
+ " 1 0.0447 0.8108 0.0847 148\n",
+ "\n",
+ " accuracy 0.9696 85443\n",
+ " macro avg 0.5222 0.8904 0.5346 85443\n",
+ "weighted avg 0.9980 0.9696 0.9830 85443\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "normal_train_indices = np.where(y_train.values == 0)[0]\n",
+ "\n",
+ "# Limit training samples for faster execution.\n",
+ "max_ocsvm_samples = min(20000, len(normal_train_indices))\n",
+ "selected_indices = np.random.choice(\n",
+ " normal_train_indices,\n",
+ " size=max_ocsvm_samples,\n",
+ " replace=False,\n",
+ ")\n",
+ "\n",
+ "X_train_ocsvm = X_train_scaled[selected_indices]\n",
+ "\n",
+ "ocsvm = OneClassSVM(\n",
+ " kernel=\"rbf\",\n",
+ " gamma=\"scale\",\n",
+ " nu=float(fraud_ratio),\n",
+ ")\n",
+ "\n",
+ "ocsvm.fit(X_train_ocsvm)\n",
+ "\n",
+ "ocsvm_pred_raw = ocsvm.predict(X_test_scaled)\n",
+ "ocsvm_pred = convert_anomaly_labels(ocsvm_pred_raw)\n",
+ "\n",
+ "# Higher score means more anomalous.\n",
+ "ocsvm_score = -ocsvm.decision_function(X_test_scaled)\n",
+ "\n",
+ "results.append(evaluate_model(\"One-Class SVM\", y_test, ocsvm_pred, ocsvm_score))\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "adec8721",
+ "metadata": {},
+ "source": [
+ "## 10. Compare All Models"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "id": "66b1aa71",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " Model | \n",
+ " Precision | \n",
+ " Recall | \n",
+ " F1-score | \n",
+ " ROC-AUC | \n",
+ " PR-AUC | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 2 | \n",
+ " One-Class SVM | \n",
+ " 0.044659 | \n",
+ " 0.810811 | \n",
+ " 0.084656 | \n",
+ " 0.936266 | \n",
+ " 0.252087 | \n",
+ "
\n",
+ " \n",
+ " | 0 | \n",
+ " Isolation Forest | \n",
+ " 0.246479 | \n",
+ " 0.236486 | \n",
+ " 0.241379 | \n",
+ " 0.946039 | \n",
+ " 0.137427 | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " Local Outlier Factor | \n",
+ " 0.000000 | \n",
+ " 0.000000 | \n",
+ " 0.000000 | \n",
+ " 0.537689 | \n",
+ " 0.002871 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " Model Precision Recall F1-score ROC-AUC PR-AUC\n",
+ "2 One-Class SVM 0.044659 0.810811 0.084656 0.936266 0.252087\n",
+ "0 Isolation Forest 0.246479 0.236486 0.241379 0.946039 0.137427\n",
+ "1 Local Outlier Factor 0.000000 0.000000 0.000000 0.537689 0.002871"
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "results_df = pd.DataFrame(results)\n",
+ "results_df = results_df.sort_values(by=\"PR-AUC\", ascending=False)\n",
+ "results_df\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "id": "05e9f2a3",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAArwAAAG0CAYAAADQAfSzAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAUblJREFUeJzt3Qm4TeX7//H7mDWYMpYxZUpkzBClQlJfRaWEFJUopDKUEt8yNEgKpW9IZagQigwNJJqEFJVEZMiUsczrf32e/2/ta+999uHIcfY567xf17Ude+211x7XXvd6nvu5nwTP8zwDAAAAAipTvJ8AAAAAcDoR8AIAACDQCHgBAAAQaAS8AAAACDQCXgAAAAQaAS8AAAACjYAXAAAAgUbACwAAgEAj4AUAAECgEfACATN27FhLSEhwl88++yzR7Zpc8YILLnC3X3HFFSn62Nrmk08+edL3W7dunbuvnnty/Pnnn9arVy+7+OKL7ayzzrIcOXLYhRdeaF27drXVq1dbRvmM9b4Fnb6jKf09PZnH9vclXXLmzGmVK1e2oUOH2rFjx0LrtWvXLmK9bNmyWenSpe3hhx+2PXv2nNRjHj582AoXLuy2895778VcR4+n731SdJvWifbbb7/Z/fffb2XKlHGv5YwzzrCLLrrI+vTpYxs3bjyp5wmkN1ni/QQAnB5nn322vf7664mChfnz59uaNWvc7enR119/bdddd50L3HXwrl27tgswfv75Z3vrrbesZs2a9tdff1mQNW3a1BYvXmxFihSxoBsxYkRcH//888+3t99+2/1/69at9sorr9iDDz5omzdvtsGDB4fWUwD5ySefuP/v2rXLBavPP/+8ff/99zZnzpxkP94HH3zgTuhE++9NN92UIq9D27311lstf/78br+pUqWKC6pXrFhho0ePtg8//NCWLl2aIo8FpEUEvEBAtWzZ0h2ohw8fbrly5Qot10FUQeLJtjylBXrOzZo1cy26ixYtsqJFi4ZuU2B/7733JtkqFgT//POPe+0FChRwl4ygQoUKcX18BbK1atUKXW/SpImVK1fOXn75ZXvqqacsa9asbnmmTJki1rvmmmtci+rcuXNt7dq1VqpUqWQ9nvZPncBdfvnlLlD+448/Ir7n/4YeX8GuWnY//fRTy507d+i2K6+80rp06WJTp049pccA0jpSGoCAuu2229zf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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "metric_to_plot = \"PR-AUC\"\n",
+ "\n",
+ "plt.figure(figsize=(8, 4))\n",
+ "plt.bar(results_df[\"Model\"], results_df[metric_to_plot])\n",
+ "plt.title(f\"Model Comparison using {metric_to_plot}\")\n",
+ "plt.xlabel(\"Model\")\n",
+ "plt.ylabel(metric_to_plot)\n",
+ "plt.xticks(rotation=20)\n",
+ "plt.show()\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "5deca4fb",
+ "metadata": {},
+ "source": [
+ "## 11. Conclusion\n",
+ "\n",
+ "This notebook introduced a complete anomaly detection workflow for a highly imbalanced fraud detection problem.\n",
+ "\n",
+ "Key points:\n",
+ "\n",
+ "- Accuracy is not enough for fraud detection because the dataset is highly imbalanced.\n",
+ "- Precision, recall, F1-score, ROC-AUC, and PR-AUC provide a better evaluation.\n",
+ "- Isolation Forest is usually a strong and efficient first baseline.\n",
+ "- Local Outlier Factor is useful for density-based anomaly detection.\n",
+ "- One-Class SVM can work well but may be slower on large datasets.\n",
+ "\n",
+ "Possible improvements:\n",
+ "\n",
+ "- Add threshold tuning using validation data.\n",
+ "- Compare with supervised models such as Random Forest or XGBoost.\n",
+ "- Add explainability methods to understand why a transaction is flagged as anomalous.\n",
+ "- Test deep learning methods such as Autoencoders or VAE-based anomaly detection.\n"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.15"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}