From d4cab736498eea153b591d5a561e5df56b181d57 Mon Sep 17 00:00:00 2001 From: Ayana Samuel Date: Tue, 17 Jun 2025 18:15:45 +0300 Subject: [PATCH 1/3] Update: task-4 - model training and interpretability --- ...odel_training_ and _interpretability.ipynb | 1524 +++++++++++++++++ requirements.txt | 1 + src/task_4/__init__.py | 0 src/task_4/data_processing.py | 99 ++ src/task_4/feature_engineering.py | 20 + src/task_4/interpretability.py | 57 + src/task_4/model_training.py | 45 + tests/test_task_4/__init__.py | 0 tests/test_task_4/test_feature_engineering.py | 31 + tests/test_task_4/test_model_training.py | 46 + 10 files changed, 1823 insertions(+) create mode 100644 notebooks/task_4/06_model_training_ and _interpretability.ipynb create mode 100644 src/task_4/__init__.py create mode 100644 src/task_4/data_processing.py create mode 100644 src/task_4/feature_engineering.py create mode 100644 src/task_4/interpretability.py create mode 100644 src/task_4/model_training.py create mode 100644 tests/test_task_4/__init__.py create mode 100644 tests/test_task_4/test_feature_engineering.py create mode 100644 tests/test_task_4/test_model_training.py diff --git a/notebooks/task_4/06_model_training_ and _interpretability.ipynb b/notebooks/task_4/06_model_training_ and _interpretability.ipynb new file mode 100644 index 0000000..e16e580 --- /dev/null +++ b/notebooks/task_4/06_model_training_ and _interpretability.ipynb @@ -0,0 +1,1524 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "44e25710", + "metadata": {}, + "source": [ + "# Task 4 · Model Training & Interpretability \n", + "**Dataset:** `data/cleaned/cleaned_data.csv` \n", + "**Focus:** \n", + "1. Claim-Severity Regression (predict `TotalClaims` where claims > 0). \n", + "2. Model comparison: Linear Regression, Random Forest, XGBoost. \n", + "3. Model interpretability with **LIME**. \n", + "4. Business take-aways for risk-based premium setting. \n", + "*All preprocessing / feature-engineering code comes from `src/task_4`.* " + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "ebb937b5", + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "sys.path.append(\"../../\")" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "acd639ad", + "metadata": {}, + "outputs": [], + "source": [ + "# Core\n", + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "# ML & metrics\n", + "from sklearn.metrics import mean_squared_error, r2_score\n", + "\n", + "# Local modules\n", + "from src.task_4.data_processing import prepare_claim_severity_data, prepare_claim_probability_data\n", + "\n", + "from src.task_4.model_training import train_and_compare_models, evaluate_model\n", + "from src.task_4.interpretability import explain_model_with_lime, show_lime_explanation\n", + "from src.task_4.feature_engineering import add_features\n", + "\n", + "DATA_PATH = \"../../data/cleaned/cleaned_data.csv\"\n", + "RANDOM_STATE = 42" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "c42ce9db", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape: (569760, 48)\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
UnderwrittenCoverIDPolicyIDTransactionMonthIsVATRegisteredCitizenshipLegalTypeTitleLanguageBankAccountType...CalculatedPremiumPerTermExcessSelectedCoverCategoryCoverTypeProductStatutoryClassStatutoryRiskTypeTotalPremiumTotalClaimsclaim_indicator
0145249.0128272015-03-01 00:00:00TrueNaNclose corporationmrenglishfirst national bankcurrent account...25.0000mobility - windscreenwindscreenwindscreenmobility metered taxis: monthlycommercialifrs constant21.9298250.000000False
1145249.0128272015-05-01 00:00:00TrueNaNclose corporationmrenglishfirst national bankcurrent account...25.0000mobility - windscreenwindscreenwindscreenmobility metered taxis: monthlycommercialifrs constant21.9298256140.350877True
2145249.0128272015-07-01 00:00:00TrueNaNclose corporationmrenglishfirst national bankcurrent account...25.0000mobility - windscreenwindscreenwindscreenmobility metered taxis: monthlycommercialifrs constant0.0000000.000000False
3145255.0128272015-05-01 00:00:00TrueNaNclose corporationmrenglishfirst national bankcurrent account...584.6468mobility - metered taxis - r2000own damageown damagemobility metered taxis: monthlycommercialifrs constant512.8480700.000000False
4145255.0128272015-07-01 00:00:00TrueNaNclose corporationmrenglishfirst national bankcurrent account...584.6468mobility - metered taxis - r2000own damageown damagemobility metered taxis: monthlycommercialifrs constant0.0000006140.350877True
\n", + "

5 rows × 48 columns

\n", + "
" + ], + "text/plain": [ + " UnderwrittenCoverID PolicyID TransactionMonth IsVATRegistered \\\n", + "0 145249.0 12827 2015-03-01 00:00:00 True \n", + "1 145249.0 12827 2015-05-01 00:00:00 True \n", + "2 145249.0 12827 2015-07-01 00:00:00 True \n", + "3 145255.0 12827 2015-05-01 00:00:00 True \n", + "4 145255.0 12827 2015-07-01 00:00:00 True \n", + "\n", + " Citizenship LegalType Title Language Bank \\\n", + "0 NaN close corporation mr english first national bank \n", + "1 NaN close corporation mr english first national bank \n", + "2 NaN close corporation mr english first national bank \n", + "3 NaN close corporation mr english first national bank \n", + "4 NaN close corporation mr english first national bank \n", + "\n", + " AccountType ... CalculatedPremiumPerTerm \\\n", + "0 current account ... 25.0000 \n", + "1 current account ... 25.0000 \n", + "2 current account ... 25.0000 \n", + "3 current account ... 584.6468 \n", + "4 current account ... 584.6468 \n", + "\n", + " ExcessSelected CoverCategory CoverType \\\n", + "0 mobility - windscreen windscreen windscreen \n", + "1 mobility - windscreen windscreen windscreen \n", + "2 mobility - windscreen windscreen windscreen \n", + "3 mobility - metered taxis - r2000 own damage own damage \n", + "4 mobility - metered taxis - r2000 own damage own damage \n", + "\n", + " Product StatutoryClass StatutoryRiskType \\\n", + "0 mobility metered taxis: monthly commercial ifrs constant \n", + "1 mobility metered taxis: monthly commercial ifrs constant \n", + "2 mobility metered taxis: monthly commercial ifrs constant \n", + "3 mobility metered taxis: monthly commercial ifrs constant \n", + "4 mobility metered taxis: monthly commercial ifrs constant \n", + "\n", + " TotalPremium TotalClaims claim_indicator \n", + "0 21.929825 0.000000 False \n", + "1 21.929825 6140.350877 True \n", + "2 0.000000 0.000000 False \n", + "3 512.848070 0.000000 False \n", + "4 0.000000 6140.350877 True \n", + "\n", + "[5 rows x 48 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
countuniquetopfreqmeanstdmin25%50%75%max
UnderwrittenCoverID569760.0NaNNaNNaN114768.44043359657.96150513797.074389.0112079.0145019.0262572.41
PolicyID569760.0NaNNaNNaN8869.0840995075.150739369.05570.07703.012032.022193.0
TransactionMonth569760222015-08-01 00:00:0067553NaNNaNNaNNaNNaNNaNNaN
IsVATRegistered5697602False568959NaNNaNNaNNaNNaNNaNNaN
Citizenship0.0NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
LegalType5697602individual568299NaNNaNNaNNaNNaNNaNNaN
Title5697603mr562668NaNNaNNaNNaNNaNNaNNaN
Language5697601english569760NaNNaNNaNNaNNaNNaNNaN
Bank5697608first national bank257306NaNNaNNaNNaNNaNNaNNaN
AccountType5697603current account323270NaNNaNNaNNaNNaNNaNNaN
\n", + "
" + ], + "text/plain": [ + " count unique top freq \\\n", + "UnderwrittenCoverID 569760.0 NaN NaN NaN \n", + "PolicyID 569760.0 NaN NaN NaN \n", + "TransactionMonth 569760 22 2015-08-01 00:00:00 67553 \n", + "IsVATRegistered 569760 2 False 568959 \n", + "Citizenship 0.0 NaN NaN NaN \n", + "LegalType 569760 2 individual 568299 \n", + "Title 569760 3 mr 562668 \n", + "Language 569760 1 english 569760 \n", + "Bank 569760 8 first national bank 257306 \n", + "AccountType 569760 3 current account 323270 \n", + "\n", + " mean std min 25% 50% \\\n", + "UnderwrittenCoverID 114768.440433 59657.961505 13797.0 74389.0 112079.0 \n", + "PolicyID 8869.084099 5075.150739 369.0 5570.0 7703.0 \n", + "TransactionMonth NaN NaN NaN NaN NaN \n", + "IsVATRegistered NaN NaN NaN NaN NaN \n", + "Citizenship NaN NaN NaN NaN NaN \n", + "LegalType NaN NaN NaN NaN NaN \n", + "Title NaN NaN NaN NaN NaN \n", + "Language NaN NaN NaN NaN NaN \n", + "Bank NaN NaN NaN NaN NaN \n", + "AccountType NaN NaN NaN NaN NaN \n", + "\n", + " 75% max \n", + "UnderwrittenCoverID 145019.0 262572.41 \n", + "PolicyID 12032.0 22193.0 \n", + "TransactionMonth NaN NaN \n", + "IsVATRegistered NaN NaN \n", + "Citizenship NaN NaN \n", + "LegalType NaN NaN \n", + "Title NaN NaN \n", + "Language NaN NaN \n", + "Bank NaN NaN \n", + "AccountType NaN NaN " + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df_clean = pd.read_csv(DATA_PATH)\n", + "print(\"Shape:\", df_clean.shape)\n", + "display(df_clean.head())\n", + "df_clean.describe(include=\"all\").T.head(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "d42acfbd", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape after feature engineering: (569760, 54)\n" + ] + } + ], + "source": [ + "df = add_features(df_clean)\n", + "print(\"Shape after feature engineering:\", df.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "8d0d28d7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Rebuilt 0\n", + "Converted 0\n", + "SumInsured 0\n", + "TermFrequency 0\n", + "CalculatedPremiumPerTerm 0\n", + "ExcessSelected 0\n", + "CoverCategory 0\n", + "CoverType 0\n", + "Product 0\n", + "StatutoryClass 0\n", + "StatutoryRiskType 0\n", + "TotalPremium 0\n", + "TotalClaims 0\n", + "claim_indicator 0\n", + "IsHighValue 0\n", + "VehicleAge 0\n", + "IsNew 0\n", + "PowerPerCylinder 0\n", + "MonthlyPremium 0\n", + "Citizenship 569760\n", + "dtype: int64" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df.isna().sum().sort_values(ascending=True).tail(20)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "35d4efd4", + "metadata": {}, + "outputs": [], + "source": [ + "df = df.drop(columns = ['Citizenship', 'ClaimRatio'])" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "d8168e8e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.int64(0)" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df.isna().sum().sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "b4e9f994", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:30: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " parsed = pd.to_datetime(series, errors='coerce')\n", + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:30: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " parsed = pd.to_datetime(series, errors='coerce')\n", + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:30: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " parsed = pd.to_datetime(series, errors='coerce')\n", + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:30: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " parsed = pd.to_datetime(series, errors='coerce')\n", + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:30: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " parsed = pd.to_datetime(series, errors='coerce')\n", + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:30: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " parsed = pd.to_datetime(series, errors='coerce')\n", + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:30: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " parsed = pd.to_datetime(series, errors='coerce')\n", + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:30: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " parsed = pd.to_datetime(series, errors='coerce')\n", + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:30: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " parsed = pd.to_datetime(series, errors='coerce')\n", + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:30: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " parsed = pd.to_datetime(series, errors='coerce')\n", + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:30: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " parsed = pd.to_datetime(series, errors='coerce')\n", + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:30: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " parsed = pd.to_datetime(series, errors='coerce')\n", + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:30: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " parsed = pd.to_datetime(series, errors='coerce')\n", + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:30: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " parsed = pd.to_datetime(series, errors='coerce')\n", + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:30: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " parsed = pd.to_datetime(series, errors='coerce')\n", + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:30: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " parsed = pd.to_datetime(series, errors='coerce')\n", + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:30: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " parsed = pd.to_datetime(series, errors='coerce')\n", + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:30: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " parsed = pd.to_datetime(series, errors='coerce')\n", + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:30: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " parsed = pd.to_datetime(series, errors='coerce')\n", + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:30: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " parsed = pd.to_datetime(series, errors='coerce')\n", + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:30: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " parsed = pd.to_datetime(series, errors='coerce')\n", + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:30: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " parsed = pd.to_datetime(series, errors='coerce')\n", + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:30: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " parsed = pd.to_datetime(series, errors='coerce')\n", + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_4\\../..\\src\\task_4\\data_processing.py:48: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " df[col] = pd.to_datetime(df[col], errors='coerce')\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train set: (92238, 50) | Test set: (23060, 50)\n" + ] + } + ], + "source": [ + "X_train_reg, X_test_reg, y_train_reg, y_test_reg = prepare_claim_severity_data(df)\n", + "print(f\"Train set: {X_train_reg.shape} | Test set: {X_test_reg.shape}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "e6c92c86", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "UnderwrittenCoverID 0\n", + "PolicyID 0\n", + "IsVATRegistered 0\n", + "LegalType 0\n", + "Title 0\n", + "Language 0\n", + "Bank 0\n", + "AccountType 0\n", + "MaritalStatus 0\n", + "Gender 0\n", + "Country 0\n", + "Province 0\n", + "PostalCode 0\n", + "SubCrestaZone 0\n", + "ItemType 0\n", + "mmcode 0\n", + "VehicleType 0\n", + "RegistrationYear 0\n", + "make 0\n", + "Model 0\n", + "dtype: int64" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df.isna().sum().sort_values(ascending=False).head(20)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "9aeef76f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.int64(0)" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "X_train_reg.isna().sum().sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "9284aa8f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
UnderwrittenCoverIDPolicyIDIsVATRegisteredLegalTypeTitleLanguageBankAccountTypeMaritalStatusGender...claim_indicatorVehicleAgeIsNewPowerPerCylinderIsHighValueMonthlyPremiumTransactionMonth_yearTransactionMonth_monthVehicleIntroDate_yearVehicleIntroDate_month
count569760.000000569760.000000569760569760569760569760569760569760569760569760...569760569760.000000569760.0569760.000000569760.000000569760.000000569760.000000569760.000000569760.000000569760.000000
uniqueNaNNaN22318322...2NaNNaNNaNNaNNaNNaNNaNNaNNaN
topNaNNaNFalseindividualmrenglishfirst national bankcurrent accountnot specifiedFemale...FalseNaNNaNNaNNaNNaNNaNNaNNaNNaN
freqNaNNaN568959568299562668569760257306323270569584563581...454462NaNNaNNaNNaNNaNNaNNaNNaNNaN
mean114768.4404338869.084099NaNNaNNaNNaNNaNNaNNaNNaN...NaN14.8548530.024.2312960.119454114.4260052014.8060625.8272732007.5481177.374884
std59657.9615055075.150739NaNNaNNaNNaNNaNNaNNaNNaN...NaN3.2870420.04.3901390.324322212.7149290.3963123.0026115.9571313.279792
min13797.000000369.000000NaNNaNNaNNaNNaNNaNNaNNaN...NaN10.0000000.015.3333330.0000000.9292002013.0000001.0000001991.0000001.000000
25%74389.0000005570.000000NaNNaNNaNNaNNaNNaNNaNNaN...NaN12.0000000.018.7500000.0000003.2417002015.0000003.0000002007.0000004.000000
50%112079.0000007703.000000NaNNaNNaNNaNNaNNaNNaNNaN...NaN14.0000000.027.7500000.0000008.3632502015.0000006.0000002010.0000008.000000
75%145019.00000012032.000000NaNNaNNaNNaNNaNNaNNaNNaN...NaN17.0000000.027.7500000.00000090.0000002015.0000008.0000002012.00000011.000000
max262572.41000022193.000000NaNNaNNaNNaNNaNNaNNaNNaN...NaN24.0000000.033.7500001.000000984.3228002015.00000012.0000002014.00000012.000000
\n", + "

11 rows × 54 columns

\n", + "
" + ], + "text/plain": [ + " UnderwrittenCoverID PolicyID IsVATRegistered LegalType \\\n", + "count 569760.000000 569760.000000 569760 569760 \n", + "unique NaN NaN 2 2 \n", + "top NaN NaN False individual \n", + "freq NaN NaN 568959 568299 \n", + "mean 114768.440433 8869.084099 NaN NaN \n", + "std 59657.961505 5075.150739 NaN NaN \n", + "min 13797.000000 369.000000 NaN NaN \n", + "25% 74389.000000 5570.000000 NaN NaN \n", + "50% 112079.000000 7703.000000 NaN NaN \n", + "75% 145019.000000 12032.000000 NaN NaN \n", + "max 262572.410000 22193.000000 NaN NaN \n", + "\n", + " Title Language Bank AccountType MaritalStatus \\\n", + "count 569760 569760 569760 569760 569760 \n", + "unique 3 1 8 3 2 \n", + "top mr english first national bank current account not specified \n", + "freq 562668 569760 257306 323270 569584 \n", + "mean NaN NaN NaN NaN NaN \n", + "std NaN NaN NaN NaN NaN \n", + "min NaN NaN NaN NaN NaN \n", + "25% NaN NaN NaN NaN NaN \n", + "50% NaN NaN NaN NaN NaN \n", + "75% NaN NaN NaN NaN NaN \n", + "max NaN NaN NaN NaN NaN \n", + "\n", + " Gender ... claim_indicator VehicleAge IsNew PowerPerCylinder \\\n", + "count 569760 ... 569760 569760.000000 569760.0 569760.000000 \n", + "unique 2 ... 2 NaN NaN NaN \n", + "top Female ... False NaN NaN NaN \n", + "freq 563581 ... 454462 NaN NaN NaN \n", + "mean NaN ... NaN 14.854853 0.0 24.231296 \n", + "std NaN ... NaN 3.287042 0.0 4.390139 \n", + "min NaN ... NaN 10.000000 0.0 15.333333 \n", + "25% NaN ... NaN 12.000000 0.0 18.750000 \n", + "50% NaN ... NaN 14.000000 0.0 27.750000 \n", + "75% NaN ... NaN 17.000000 0.0 27.750000 \n", + "max NaN ... NaN 24.000000 0.0 33.750000 \n", + "\n", + " IsHighValue MonthlyPremium TransactionMonth_year \\\n", + "count 569760.000000 569760.000000 569760.000000 \n", + "unique NaN NaN NaN \n", + "top NaN NaN NaN \n", + "freq NaN NaN NaN \n", + "mean 0.119454 114.426005 2014.806062 \n", + "std 0.324322 212.714929 0.396312 \n", + "min 0.000000 0.929200 2013.000000 \n", + "25% 0.000000 3.241700 2015.000000 \n", + "50% 0.000000 8.363250 2015.000000 \n", + "75% 0.000000 90.000000 2015.000000 \n", + "max 1.000000 984.322800 2015.000000 \n", + "\n", + " TransactionMonth_month VehicleIntroDate_year VehicleIntroDate_month \n", + "count 569760.000000 569760.000000 569760.000000 \n", + "unique NaN NaN NaN \n", + "top NaN NaN NaN \n", + "freq NaN NaN NaN \n", + "mean 5.827273 2007.548117 7.374884 \n", + "std 3.002611 5.957131 3.279792 \n", + "min 1.000000 1991.000000 1.000000 \n", + "25% 3.000000 2007.000000 4.000000 \n", + "50% 6.000000 2010.000000 8.000000 \n", + "75% 8.000000 2012.000000 11.000000 \n", + "max 12.000000 2014.000000 12.000000 \n", + "\n", + "[11 rows x 54 columns]" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df.describe(include=\"all\")" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "37a91cbe", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "92238 92238 23060 23060\n" + ] + } + ], + "source": [ + "assert len(X_train_reg) == len(y_train_reg) , \"X_train and y_train length mismatch\"\n", + "assert len(X_test_reg) == len(y_test_reg), \"X_test and y_test length mismatch\"\n", + "print(len(X_train_reg), len(y_train_reg), len(X_test_reg), len(y_test_reg))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "461604d6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Linear Regression Results → RMSE: 4347.74, R²: 0.0438\n", + "Random Forest Results → RMSE: 4837.29, R²: -0.1837\n", + "XGBoost Results → RMSE: 4757.96, R²: -0.1452\n", + "\n", + "All model scores: {'LinearRegression': (np.float64(4347.739916554806), 0.04377097808964758), 'RandomForest': (np.float64(4837.287837817768), -0.18369187971065437), 'XGBoost': (np.float64(4757.959883084042), -0.14518686832165772)}\n", + "\n", + "🟢 Best model = LinearRegression\n" + ] + } + ], + "source": [ + "best_model, best_name, all_scores = train_and_compare_models(X_train_reg, y_train_reg, X_test_reg, y_test_reg)\n", + "print(\"\\nAll model scores:\", all_scores)\n", + "print(f\"\\n🟢 Best model = {best_name}\")" + ] + }, + { + "cell_type": "markdown", + "id": "d71f28a4", + "metadata": {}, + "source": [ + "# Model Interpretability" + ] + }, + { + "cell_type": "markdown", + "id": "17bf5f4a", + "metadata": {}, + "source": [ + "## importance\n" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "2bfe5219", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Coefficient
Cylinders-902.206323
IsVATRegistered-659.133239
LegalType-336.257620
AlarmImmobiliser274.009953
Converted-269.202710
NewVehicle219.571274
TransactionMonth_year-183.414234
PowerPerCylinder-156.805334
Title150.068979
VehicleType-137.916064
\n", + "
" + ], + "text/plain": [ + " Coefficient\n", + "Cylinders -902.206323\n", + "IsVATRegistered -659.133239\n", + "LegalType -336.257620\n", + "AlarmImmobiliser 274.009953\n", + "Converted -269.202710\n", + "NewVehicle 219.571274\n", + "TransactionMonth_year -183.414234\n", + "PowerPerCylinder -156.805334\n", + "Title 150.068979\n", + "VehicleType -137.916064" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "if hasattr(best_model, \"feature_importances_\"):\n", + " importances = pd.Series(best_model.feature_importances_,\n", + " index=X_train_reg.columns).sort_values(ascending=False)\n", + " display(importances.head(10).to_frame(\"Importance\"))\n", + "elif best_name == \"LinearRegression\":\n", + " coefs = pd.Series(best_model.coef_, index=X_train_reg.columns)\n", + " display(coefs.sort_values(key=abs, ascending=False).head(10).to_frame(\"Coefficient\"))\n", + "else:\n", + " print(\"Model has no built-in feature_importances_.\")" + ] + }, + { + "cell_type": "markdown", + "id": "5f6f6420", + "metadata": {}, + "source": [ + "## LIME" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "67e2db55", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Feature contributions to prediction:\n", + "Cylinders <= 4.00: 4785.4689\n", + "IsVATRegistered <= 0.00: 1206.5559\n", + "18.75 < PowerPerCylinder <= 27.75: -985.4877\n", + "75.00 < kilowatts <= 111.00: 768.3439\n", + "Converted <= 0.00: -576.0644\n", + "0.00 < TotalPremium <= 2.36: -397.2949\n", + "make <= 31.00: -385.9235\n", + "mmcode > 60058418.00: -377.3096\n", + "14.00 < CoverCategory <= 18.00: -323.6194\n", + "13.00 < CoverType <= 16.00: 271.2910\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\venv\\Lib\\site-packages\\sklearn\\utils\\validation.py:2749: UserWarning: X does not have valid feature names, but LinearRegression was fitted with feature names\n", + " warnings.warn(\n" + ] + } + ], + "source": [ + "# Explain the first row of X_test_reg\n", + "lime_exp = explain_model_with_lime(\n", + " best_model,\n", + " X_train_reg,\n", + " X_test_reg,\n", + " feature_names=list(X_train_reg.columns),\n", + " instance_idx=0,\n", + " mode=\"regression\"\n", + ")\n", + "show_lime_explanation(lime_exp)" + ] + }, + { + "cell_type": "markdown", + "id": "b8674924", + "metadata": {}, + "source": [ + "## classification" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "9847c2d8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Classifier train / test shapes: (455808, 53) (113952, 53)\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
AccPrecRecF1
LogReg1.0000001.01.0000001.000000
RF1.0000001.01.0000001.000000
XGB0.9987281.00.9937120.996846
\n", + "
" + ], + "text/plain": [ + " Acc Prec Rec F1\n", + "LogReg 1.000000 1.0 1.000000 1.000000\n", + "RF 1.000000 1.0 1.000000 1.000000\n", + "XGB 0.998728 1.0 0.993712 0.996846" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "X_train_clf, X_test_clf, y_train_clf, y_test_clf = prepare_claim_probability_data(df)\n", + "print(\"Classifier train / test shapes:\", X_train_clf.shape, X_test_clf.shape)\n", + "\n", + "from sklearn.ensemble import RandomForestClassifier\n", + "from sklearn.linear_model import LogisticRegression\n", + "from xgboost import XGBClassifier\n", + "from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score\n", + "\n", + "def eval_clf(name, model, Xt, yt):\n", + " pred = model.predict(Xt)\n", + " return {\n", + " \"Acc\": accuracy_score(yt, pred),\n", + " \"Prec\": precision_score(yt, pred),\n", + " \"Rec\": recall_score(yt, pred),\n", + " \"F1\": f1_score(yt, pred)\n", + " }\n", + "\n", + "clf_models = {\n", + " \"LogReg\": LogisticRegression(max_iter=1000),\n", + " \"RF\": RandomForestClassifier(n_estimators=400, random_state=RANDOM_STATE, n_jobs=-1),\n", + " \"XGB\": XGBClassifier(n_estimators=500, learning_rate=0.05,\n", + " max_depth=6, random_state=RANDOM_STATE, n_jobs=-1)\n", + "}\n", + "clf_scores = {}\n", + "for name, mdl in clf_models.items():\n", + " mdl.fit(X_train_clf, y_train_clf)\n", + " clf_scores[name] = eval_clf(name, mdl, X_test_clf, y_test_clf)\n", + "\n", + "pd.DataFrame(clf_scores).T" + ] + }, + { + "cell_type": "markdown", + "id": "54667b63", + "metadata": {}, + "source": [ + "## 🔍 Key Pricing Insights & Actions\n", + "\n", + "| Insight | Evidence (LIME / Importance) | Action |\n", + "|----------------------------------------|----------------------------------|------------------------------------------------|\n", + "| ≤4 Cylinders → Higher Severity | +4785 / -902 | Apply loading to ≤4-cylinder vehicles |\n", + "| Not VAT Registered → Higher Risk | +1206 / -659 | Increase base rate for non-VAT customers |\n", + "| Converted Vehicles → Higher Risk | -576 / -269 | Raise premiums for modified/converted cars |\n", + "| Alarm/Immobiliser → Lower Risk | N/A / +274 | Offer discounts for vehicles with alarms |\n", + "| New Vehicles → Lower Risk | N/A / +220 | Discount premiums for new cars |\n", + "| Moderate Power/Cylinder → Lower Risk | -985 / -157 | Consider targeted discounts |\n", + "| Low-Premium Segment → Possibly Underpriced | -397 | Review pricing in low-premium segments |\n", + "\n", + "> Deploying **{best_name}** reduces RMSE by **Δ%** vs. linear model, enhancing pricing precision. \n", + "> Pairing with **{best_clf_name}** enables dynamic pricing:\n", + "\n", + "\\[\n", + "\\text{Premium} = \\left[\\Pr(\\text{Claim}) \\times \\widehat{\\text{Severity}}\\right] + \\text{Expenses} + \\text{Margin}\n", + "\\]\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "venv", + "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.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/requirements.txt b/requirements.txt index 08c77ae..a546f71 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,6 +6,7 @@ seaborn scikit-learn scipy statsmodels +xgboost # EDA & Plotting plotly diff --git a/src/task_4/__init__.py b/src/task_4/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/task_4/data_processing.py b/src/task_4/data_processing.py new file mode 100644 index 0000000..ce7302e --- /dev/null +++ b/src/task_4/data_processing.py @@ -0,0 +1,99 @@ +import pandas as pd +from sklearn.model_selection import train_test_split +from sklearn.preprocessing import LabelEncoder +# from src.preprocessing.cleaning import clean_data +from src.task_4.feature_engineering import add_features # you defined this earlier + +def encode_categoricals(df: pd.DataFrame) -> pd.DataFrame: + """ + Label encode all categorical columns, safely handling NaNs and type issues. + """ + df_encoded = df.copy() + cat_cols = df_encoded.select_dtypes(include=['object', 'category']).columns + + for col in cat_cols: + # If NaNs exist, temporarily fill with placeholder + if df_encoded[col].isnull().any(): + df_encoded[col] = df_encoded[col].fillna('___missing___') + + try: + le = LabelEncoder() + df_encoded[col] = le.fit_transform(df_encoded[col].astype(str)) + except (ValueError, TypeError): + # Fallback for very dirty columns + df_encoded[col] = df_encoded[col].astype('category').cat.codes + + return df_encoded + +def is_date_column(series: pd.Series) -> bool: + try: + parsed = pd.to_datetime(series, errors='coerce') + non_null_ratio = parsed.notna().mean() + return non_null_ratio > 0.9 # 90%+ of values must be valid dates + except: + return False + + + + +def prepare_claim_severity_data(df): + target = 'TotalClaims' + drop_cols = ['UnderwrittenCoverID', 'PolicyID', 'claim_indicator'] + + # Filter only likely date columns + date_cols = [col for col in df.columns if df[col].dtype == 'object' and is_date_column(df[col])] + + # Convert and extract + for col in date_cols: + df[col] = pd.to_datetime(df[col], errors='coerce') + df[f'{col}_year'] = df[col].dt.year + df[f'{col}_month'] = df[col].dt.month + df.drop(columns=[col], inplace=True) + + for col in date_cols: + if col in df.columns: + df[col] = pd.to_datetime(df[col], errors='coerce') + df[f'{col}_year'] = df[col].dt.year + df[f'{col}_month'] = df[col].dt.month + df.drop(columns=[col], inplace=True) + + # Keep only positive-claim rows + df = df[df[target] > 0] + + # Encode categoricals + df = encode_categoricals(df) + + # Drop unnecessary columns + df = df.drop(columns=drop_cols) + + + + X = df.drop(columns=[target]) + y = df[target] + + return train_test_split(X, y, test_size=0.2, random_state=42) + + + + + +def prepare_claim_probability_data(df: pd.DataFrame) -> tuple: + """ + Load, clean, engineer, encode, and split the data for claim probability classification. + Assumes a `claim_indicator` column exists (boolean or binary 0/1). + """ + + # Ensure claim_indicator is binary (0/1) + if df['claim_indicator'].dtype == 'bool': + df['claim_indicator'] = df['claim_indicator'].astype(int) + elif df['claim_indicator'].nunique() == 2 and sorted(df['claim_indicator'].unique()) == [False, True]: + df['claim_indicator'] = df['claim_indicator'].map({False: 0, True: 1}) + + df = encode_categoricals(df) + df = df.dropna() + + X = df.drop(columns=['claim_indicator']) + y = df['claim_indicator'] + + return train_test_split(X, y, test_size=0.2, stratify=y, random_state=42) + diff --git a/src/task_4/feature_engineering.py b/src/task_4/feature_engineering.py new file mode 100644 index 0000000..30d5ca6 --- /dev/null +++ b/src/task_4/feature_engineering.py @@ -0,0 +1,20 @@ +import pandas as pd +def add_features(df: pd.DataFrame) -> pd.DataFrame: + df['ClaimRatio'] = df['TotalClaims'] / df['TotalPremium'].replace(0, 1) + df['VehicleAge'] = 2025 - df['RegistrationYear'] + df['IsNew'] = (df['VehicleAge'] <= 1).astype(int) + df['PowerPerCylinder'] = df['kilowatts'] / df['Cylinders'].replace(0, 1) + df['IsHighValue'] = (df['CustomValueEstimate'] > df['CustomValueEstimate'].median()).astype(int) + + # 🔁 Map TermFrequency from string to numeric multiplier + term_map = { + 'Monthly': 12, + 'Quarterly': 4, + 'Semi-Annual': 2, + 'Annual': 1 + } + df['TermFrequency'] = df['TermFrequency'].map(term_map).fillna(1) + + df['MonthlyPremium'] = df['CalculatedPremiumPerTerm'] / df['TermFrequency'].replace(0, 1) + + return df diff --git a/src/task_4/interpretability.py b/src/task_4/interpretability.py new file mode 100644 index 0000000..5406194 --- /dev/null +++ b/src/task_4/interpretability.py @@ -0,0 +1,57 @@ +import lime +import lime.lime_tabular +import numpy as np +import pandas as pd + +def explain_model_with_lime(model, X_train, X_test, feature_names, instance_idx=0, mode='regression'): + """ + Explain the prediction of a single instance using LIME. + + Parameters: + - model: trained model (regressor or classifier) + - X_train: training features as DataFrame or numpy array (for LIME background distribution) + - X_test: test features as DataFrame or numpy array + - feature_names: list of feature names (columns) + - instance_idx: index of the test instance to explain + - mode: 'regression' or 'classification' + + Returns: + - explanation object from LIME (can be used to display or save explanations) + """ + if isinstance(X_train, pd.DataFrame): + training_data = X_train.values + else: + training_data = X_train + + if isinstance(X_test, pd.DataFrame): + test_data = X_test.values + else: + test_data = X_test + + # Setup the LIME explainer for tabular data + explainer = lime.lime_tabular.LimeTabularExplainer( + training_data, + feature_names=feature_names, + mode=mode, + discretize_continuous=True + ) + + # Select one instance to explain + instance = test_data[instance_idx] + + # Get explanation + explanation = explainer.explain_instance( + instance, + model.predict, + num_features=10 # top 10 features in explanation + ) + + return explanation + +def show_lime_explanation(explanation): + """ + Display the LIME explanation in a readable format. + """ + print("Feature contributions to prediction:") + for feature, weight in explanation.as_list(): + print(f"{feature}: {weight:.4f}") diff --git a/src/task_4/model_training.py b/src/task_4/model_training.py new file mode 100644 index 0000000..72628e4 --- /dev/null +++ b/src/task_4/model_training.py @@ -0,0 +1,45 @@ +import pandas as pd +import numpy as np +from sklearn.linear_model import LinearRegression +from sklearn.ensemble import RandomForestRegressor +from xgboost import XGBRegressor +from sklearn.metrics import mean_squared_error, r2_score +from joblib import dump +from src.task_4.data_processing import prepare_claim_severity_data + +def evaluate_model(name, model, X_test, y_test): + preds = model.predict(X_test) + rmse = np.sqrt(mean_squared_error(y_test, preds)) + r2 = r2_score(y_test, preds) + print(f"{name} Results → RMSE: {rmse:.2f}, R²: {r2:.4f}") + return rmse, r2 + +def train_and_compare_models(X_train,y_train, X_test, y_test): + + results = {} + models = {} + + # 1. Linear Regression + lr = LinearRegression() + lr.fit(X_train, y_train) + results['LinearRegression'] = evaluate_model("Linear Regression", lr, X_test, y_test) + models['LinearRegression'] = lr + + # 2. Random Forest + rf = RandomForestRegressor(n_estimators=100, random_state=42) + rf.fit(X_train, y_train) + results['RandomForest'] = evaluate_model("Random Forest", rf, X_test, y_test) + models['RandomForest'] = rf + + # 3. XGBoost + xgb = XGBRegressor(n_estimators=100, learning_rate=0.1, random_state=42) + xgb.fit(X_train, y_train) + results['XGBoost'] = evaluate_model("XGBoost", xgb, X_test, y_test) + models['XGBoost'] = xgb + + # Identify best model by lowest RMSE + best_model_name = min(results, key=lambda k: results[k][0]) + best_model = models[best_model_name] + + + return best_model, best_model_name, results diff --git a/tests/test_task_4/__init__.py b/tests/test_task_4/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_task_4/test_feature_engineering.py b/tests/test_task_4/test_feature_engineering.py new file mode 100644 index 0000000..4b460f8 --- /dev/null +++ b/tests/test_task_4/test_feature_engineering.py @@ -0,0 +1,31 @@ +import pandas as pd +from src.task_4.feature_engineering import add_features + +def test_add_features(): + df = pd.DataFrame({ + 'TotalClaims': [1000, 2000, 0, 5000], + 'TotalPremium': [5000, 4000, 0, 1000], + 'RegistrationYear': [2024, 2023, 2025, 2010], + 'kilowatts': [100, 200, 150, 300], + 'Cylinders': [4, 0, 3, 6], + 'CustomValueEstimate': [10000, 20000, 15000, 30000], + 'CalculatedPremiumPerTerm': [1200, 800, 0, 400], + 'TermFrequency': [12, 4, 0, 1] + }) + + df_new = add_features(df) + + expected_claim_ratio = df['TotalClaims'] / df['TotalPremium'].replace(0, 1) + expected_vehicle_age = 2025 - df['RegistrationYear'] + expected_is_new = (expected_vehicle_age <= 1).astype(int) + expected_power_per_cylinder = df['kilowatts'] / df['Cylinders'].replace(0, 1) + median_value = df['CustomValueEstimate'].median() + expected_is_high_value = (df['CustomValueEstimate'] > median_value).astype(int) + expected_monthly_premium = df['CalculatedPremiumPerTerm'] / df['TermFrequency'].replace(0, 1) + + assert all(df_new['ClaimRatio'] == expected_claim_ratio) + assert all(df_new['VehicleAge'] == expected_vehicle_age) + assert all(df_new['IsNew'] == expected_is_new) + assert all(df_new['PowerPerCylinder'] == expected_power_per_cylinder) + assert all(df_new['IsHighValue'] == expected_is_high_value) + assert all(df_new['MonthlyPremium'] == expected_monthly_premium) diff --git a/tests/test_task_4/test_model_training.py b/tests/test_task_4/test_model_training.py new file mode 100644 index 0000000..6732851 --- /dev/null +++ b/tests/test_task_4/test_model_training.py @@ -0,0 +1,46 @@ +import os +import pandas as pd +import pytest +from joblib import load +from src.task_4.model_training import train_and_compare_models + +# Create a small dummy CSV for testing +dummy_csv_path = 'test_dummy_data.csv' + +@pytest.fixture(scope="module", autouse=True) +def setup_dummy_data(): + # Create a dummy dataset for claim severity with minimal rows and columns + data = { + 'TotalClaims': [100, 200, 150, 300], + 'TotalPremium': [1000, 2000, 1500, 3000], + 'RegistrationYear': [2020, 2019, 2021, 2018], + 'kilowatts': [100, 150, 120, 130], + 'Cylinders': [4, 6, 4, 4], + 'CustomValueEstimate': [5000, 7000, 6000, 8000], + 'CalculatedPremiumPerTerm': [100, 150, 120, 130], + 'TermFrequency': [12, 12, 12, 12], + # Add categorical columns as strings (with small number of categories) + 'Gender': ['male', 'female', 'male', 'female'], + 'claim_indicator': [1, 0, 1, 0] # Just to mimic full data format + } + df = pd.DataFrame(data) + df.to_csv(dummy_csv_path, index=False) + yield + os.remove(dummy_csv_path) + +def test_train_and_compare_models_runs(): + best_model, best_name, results = train_and_compare_models(dummy_csv_path) + + # Check return types + assert best_name in results + assert hasattr(best_model, 'predict') # model should have predict method + + # Check results contain expected keys + assert 'LinearRegression' in results + assert 'RandomForest' in results + assert 'XGBoost' in results + + # Check RMSE and R2 are floats + for key, (rmse, r2) in results.items(): + assert isinstance(rmse, float) + assert isinstance(r2, float) From ac5b95c105d7e9444b74bc5be3ffcc1b8adbb24a Mon Sep 17 00:00:00 2001 From: Ayana Samuel Date: Tue, 17 Jun 2025 18:22:01 +0300 Subject: [PATCH 2/3] Fix: test updated --- tests/test_task_4/test_model_training.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/test_task_4/test_model_training.py b/tests/test_task_4/test_model_training.py index 6732851..69082e7 100644 --- a/tests/test_task_4/test_model_training.py +++ b/tests/test_task_4/test_model_training.py @@ -3,6 +3,7 @@ import pytest from joblib import load from src.task_4.model_training import train_and_compare_models +from src.task_4.data_processing import prepare_claim_severity_data # Create a small dummy CSV for testing dummy_csv_path = 'test_dummy_data.csv' @@ -11,6 +12,8 @@ def setup_dummy_data(): # Create a dummy dataset for claim severity with minimal rows and columns data = { + 'UnderwrittenCoverID': [1, 2, 3, 4], + 'PolicyID': [101, 102, 103, 104], 'TotalClaims': [100, 200, 150, 300], 'TotalPremium': [1000, 2000, 1500, 3000], 'RegistrationYear': [2020, 2019, 2021, 2018], @@ -20,7 +23,7 @@ def setup_dummy_data(): 'CalculatedPremiumPerTerm': [100, 150, 120, 130], 'TermFrequency': [12, 12, 12, 12], # Add categorical columns as strings (with small number of categories) - 'Gender': ['male', 'female', 'male', 'female'], + 'Gender': ['Male', 'Female', 'Male', 'Female'], 'claim_indicator': [1, 0, 1, 0] # Just to mimic full data format } df = pd.DataFrame(data) @@ -29,7 +32,16 @@ def setup_dummy_data(): os.remove(dummy_csv_path) def test_train_and_compare_models_runs(): - best_model, best_name, results = train_and_compare_models(dummy_csv_path) + # Read the dummy data + df = pd.read_csv(dummy_csv_path) + # Assume 'TotalClaims' is the target + # X = df.drop(columns=['TotalClaims']) + # y = df['TotalClaims'] + + # Simple train-test split (2 train, 2 test) + X_train, X_test, y_train, y_test = prepare_claim_severity_data(df) + + best_model, best_name, results = train_and_compare_models(X_train, y_train, X_test, y_test) # Check return types assert best_name in results From 5baf43b407644a9cfd5fa7c8389c1c57d88e6058 Mon Sep 17 00:00:00 2001 From: Ayana Samuel Date: Tue, 17 Jun 2025 20:27:10 +0300 Subject: [PATCH 3/3] update: readme updated --- README.md | 163 +++++++++++++++++++++-- notebooks/README.md | 79 ++++++++++- notebooks/task_1/04_visualizations.ipynb | 23 +--- src/README.md | 86 +++++++++++- tests/README.md | 82 +++++++++++- 5 files changed, 390 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index df814c4..4e02abf 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,161 @@ -# Insurance Risk Analytics Project +# 🚗 Insurance Risk Modeling & Dynamic Pricing System -This project analyzes insurance data to extract actionable insights on risk, profitability, and optimal pricing strategies. +This project develops a robust, explainable, and risk-aware pricing model for auto insurance policies. It incorporates statistical analysis, machine learning, and reproducible data practices to predict insurance claim severity and optimize premium pricing. -## 📂 Structure -- `data/`: Raw and processed data tracked via DVC -- `notebooks/`: EDA and modeling notebooks -- `src/`: Reusable Python scripts -- `tests/`: Unit tests for core logic -- `.github/workflows/`: CI/CD pipelines +> ✅ Completed as part of the Week 3 Challenge at **10Academy**. + +--- + +## 🧭 Project Goals + +- Understand and explore insurance data to uncover actionable insights. +- Establish a reproducible data pipeline using Git, GitHub, and DVC. +- Statistically validate hypotheses related to insurance risk. +- Build predictive models to estimate: + - 💰 **Claim Severity** — How much we might pay. + - 📈 **Claim Probability** — How likely a customer is to claim. +- Construct a **dynamic pricing formula** that incorporates business margins. + +--- + +## 🔧 Technologies & Tools + +| Area | Tools Used | +|--------------------|----------------------------------------| +| Programming | Python, Jupyter | +| Data Handling | Pandas, NumPy, DVC | +| Visualization | Matplotlib, Seaborn, Plotly | +| Modeling | Scikit-learn, XGBoost, SHAP, LIME | +| Version Control | Git, GitHub, GitHub Actions | +| CI/CD | GitHub Actions | +| Environment | `venv` + `requirements.txt` | + +--- + +## 📂 Repository Structure +```text +. +├── data/ # Raw and processed data (tracked via DVC) +├── models/ # Saved models +├── notebooks/ # Jupyter notebooks for EDA, testing, modeling +├── src/ # Core source code +│ ├── preprocessing/ # Cleaning, transformation, encoding +│ ├── task_3/ # Hypothesis testing modules +│ └── task_4/ # Modeling pipeline and interpretation +├── tests/ # Unit tests +├── .dvc/ # DVC metadata +├── .github/workflows/ # GitHub Actions CI pipeline +├── dvc.yaml # DVC pipeline definition +├── requirements.txt # Python dependencies +└── README.md # Project overview (this file) +``` + +--- + +## 📊 Task Breakdown + +### 🔍 Task 1: EDA & Git Setup + +- Configured Git and GitHub, created `task-1` branch +- Performed EDA on claims, premiums, and customer demographics +- Visualized insights across provinces, genders, and vehicle types +- Identified key drivers of loss ratio and risk + +### 💾 Task 2: Data Version Control (DVC) + +- Installed DVC and initialized version control +- Added data files to DVC tracking +- Set up a **local remote storage** and pushed data +- Ensured reproducibility and auditability of datasets + +### 📊 Task 3: Hypothesis Testing + +- Formulated and tested statistical hypotheses: + - 📍 Risk varies across **provinces** and **zip codes** + - 👥 Gender differences in **claim frequency** and **severity** + - 💸 Profitability margins vary by region +- Used t-tests, z-tests, chi-squared where applicable +- Business interpretations provided for each result + +### 🧠 Task 4: Predictive Modeling + +- Built severity regression models: Linear, Random Forest, XGBoost +- Evaluated using RMSE, R² +- Used **SHAP** and **LIME** for feature importance +- Modeled claim probability (classification) for pricing +- Final pricing formula: + +--- + +## 📈 Key Insights & Recommendations + +| Insight | Impact on Pricing Strategy | +|-------------------------------------------|---------------------------------------------------| +| ≤4 Cylinder vehicles → ↑ Severity Risk | Apply loading to small-engine vehicles | +| Non-VAT Registered → ↑ Risk | Raise base rate for unregistered customers | +| Converted/Modified Vehicles = ↑ Risk | Apply higher risk surcharge | +| Alarm/Immobilizer → ↓ Risk | Provide discount for security features | +| New Vehicles → ↓ Risk | Discount for newer vehicles | + +--- + +## 📦 Setup Instructions + +1. **Clone the repository:** + ```bash + git clone https://github.com/ayanasamuel8/End-to-End-Insurance-Risk-Analytics-and-Predictive-Modeling.git + cd insurance-risk-model +Install dependencies: -## 📦 Setup ```bash python -m venv venv -source venv/bin/activate # or venv\Scripts\activate on Windows +source venv/bin/activate # Windows: venv\Scripts\activate pip install -r requirements.txt ``` +Run notebooks: + +```bash +jupyter notebook +``` +Run tests: -## ▶️ Run Tests ```bash pytest ``` -## 📊 Tools -- Python, DVC, GitHub Actions, SHAP, XGBoost \ No newline at end of file +🧪 Data Versioning with DVC +bash +dvc init +dvc add data/raw/insurance_data.csv +dvc remote add -d localstorage /path/to/your/storage +dvc push +To reproduce the data pipeline: + +bash +dvc pull +✅ CI/CD +GitHub Actions is configured for: + +Code linting + +Unit tests + +Model validation (optional step) + +Workflow defined in .github/workflows/deploy.yml. + +📌 Results Summary +🧮 Best Severity Model: XGBoost +RMSE improvement: +Δ% vs. baseline +Top features: Engine Size, Vehicle Age, Province, Conversion Status + +🧠 Classification Accuracy: ~X% +Enables dynamic, fair, and risk-adjusted premium pricing + +👥 Contributors +👤 Ayana Samuel +Role: Full Data Science Workflow +Skills: EDA, DVC, Statistical Testing, Machine Learning, GitOps +GitHub: https://github.com/ayanasamuel8/End-to-End-Insurance-Risk-Analytics-and-Predictive-Modeling.git + +📜 License +This project is licensed for academic and demonstration use. Contact the author for commercial usage rights. \ No newline at end of file diff --git a/notebooks/README.md b/notebooks/README.md index 663ce96..522ee54 100644 --- a/notebooks/README.md +++ b/notebooks/README.md @@ -1,7 +1,78 @@ -# Notebooks +# 📒 Notebooks Overview — Insurance Pricing Project -This folder contains Jupyter notebooks for exploratory data analysis (EDA), feature engineering, and modeling. +This folder contains Jupyter Notebooks used to explore, analyze, and model insurance claim data as part of the **10Academy Week 3 Challenge**. -- `task_1/`: Notebooks related to Task 1 (e.g., data understanding, preprocessing). +Each notebook is modular and corresponds to a specific task in the data science workflow — from EDA and hypothesis testing to modeling and interpretation. -Notebooks are organized by task or experiment for reproducibility. \ No newline at end of file +--- + +## 📁 Folder Purpose + +The `notebooks/` directory serves as the primary space for: + +- Experimenting with data pipelines +- Visualizing insights +- Testing hypotheses +- Training and evaluating models +- Documenting key results + +--- + +## 🧭 Execution Order + +| Notebook Path | Description | +|-------------------------------------------------------------|--------------------------------------------------------------------------------------| +| `task_1/01_Data_understanding.ipynb` | 🔍 **Initial Data Exploration** — Overview of dataset structure, basic distributions, and types of variables. | +| `task_1/02.eda_univariate.ipynb` | 📊 **Univariate Analysis** — Examines single-variable distributions and statistics, including missing data handling. | +| `task_1/03_eda_bivariate.ipynb` | 🔗 **Bivariate Analysis** — Explores relationships between key variables (e.g., claims vs. gender, province). | +| `task_1/04_visualization.ipynb` | 📈 **Visual Summary** — Aggregated plots and advanced visuals to communicate key trends and risk factors. | +| `task_3/05_hypthesis_testing.ipynb` | 📐 **Statistical Hypothesis Testing** — Validates assumptions across provinces, genders, and customer segments. | +| `task_4/06_model_training_and_interpretability.ipynb` | 🧠 **Modeling & Interpretability** — Trains severity and claim probability models; interprets them using SHAP/LIME. | + + +> **Note:** Run these notebooks in order for best results. Dependencies between notebooks are minimal but intentional (e.g., modeling uses cleaned data from Notebook 5). + +--- + +## 🔍 Highlights by Notebook + +### 📁 `task_1/01_Data_understanding.ipynb` +- Overview of dataset structure and key variables +- Initial univariate statistics and class distributions +- Identified outliers and null-value patterns + +### 📁 `task_1/02.eda_univariate.ipynb` +- Dealt with missing values and variable types +- Performed univariate analysis: claims, premiums, risk flags +- Created new features: vehicle age, risk class, etc. + +### 📁 `task_1/03_eda_bivariate.ipynb` +- Bivariate relationships: claim vs gender, province, zip, cylinders +- Used cross-tabulations and grouped summaries +- Inferred possible risk drivers from visual trends + +### 📁 `task_1/04_visualization.ipynb` +- Visual storytelling using bar plots, heatmaps, and boxplots +- Focused on loss ratio patterns by region and vehicle attributes +- Illustrated skewness, imbalance, and outliers effectively + +### 📁 `task_3/05_hypthesis_testing.ipynb` +- Statistically tested hypotheses on claim risk factors +- Methods: z-test, t-test, chi-squared +- Provided business insights on regional and demographic effects + +### 📁 `task_4/06_model_training_and_interpretability.ipynb` +- Modeled both claim severity (regression) and probability (classification) +- Trained Linear, RF, XGBoost models with performance metrics +- Used SHAP and LIME to interpret model decisions and explain pricing + + +--- + +## 📦 How to Use + +To run the notebooks: + +```bash +cd notebooks/ +jupyter notebook diff --git a/notebooks/task_1/04_visualizations.ipynb b/notebooks/task_1/04_visualizations.ipynb index 96132c8..83f4237 100644 --- a/notebooks/task_1/04_visualizations.ipynb +++ b/notebooks/task_1/04_visualizations.ipynb @@ -18,7 +18,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "id": "6215fd69", "metadata": {}, "outputs": [], @@ -31,19 +31,10 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "id": "58c1cf24", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "c:\\Users\\user\\OneDrive\\Documents\\Data Science\\insurance-risk-modeling\\notebooks\\task_1\\../..\\src\\data_loader.py:10: DtypeWarning: Columns (32) have mixed types. Specify dtype option on import or set low_memory=False.\n", - " return pd.read_csv(path)\n" - ] - } - ], + "outputs": [], "source": [ "df = load_raw_data(RAW_DATA_PATH)" ] @@ -77,7 +68,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "C:\\Users\\user\\AppData\\Local\\Temp\\ipykernel_11980\\3038241407.py:5: FutureWarning: \n", + "C:\\Users\\user\\AppData\\Local\\Temp\\ipykernel_19576\\3038241407.py:5: FutureWarning: \n", "\n", "The `ci` parameter is deprecated. Use `errorbar=None` for the same effect.\n", "\n", @@ -117,7 +108,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "C:\\Users\\user\\AppData\\Local\\Temp\\ipykernel_11980\\227640980.py:5: FutureWarning: \n", + "C:\\Users\\user\\AppData\\Local\\Temp\\ipykernel_19576\\1575506053.py:5: FutureWarning: \n", "\n", "The `ci` parameter is deprecated. Use `errorbar=None` for the same effect.\n", "\n", @@ -126,7 +117,7 @@ }, { "data": { - "image/png": "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8w74+AEDBliZMmUsZ86cVrJkSXvggQds165dx+x4AQAAAABIiKB7zJgx1qlTJ+vZs6d99dVXVrFiRatfv76tX78+xcePHj3aHnnkEX/8999/b8OGDfPXePTRR4/5sQMAAAAAENdB93PPPWft2rWz1q1bW/ny5W3w4MGWK1cuGz58eIqPnzNnjtWqVctuuukmz45fccUV1qJFi4NmxwEAAAAAyFRB9549e2zhwoVWr169/zuYrFn987lz56b4nAsvvNCfEwTZP//8s02cONEaNWp0zI4bAAAAAIBDdbylk40bN9revXutSJEiSe7X58uWLUvxOcpw63kXXXSRRSIR+/fff+2uu+46YHn57t27/RbYsmVLGv4UAAAAAADEcSO1wzFjxgx74okn7KWXXvI14OPGjbMJEyZY3759U31Ov379LG/evNGbmq8BAAAAAJChM90FCxa04447ztatW5fkfn1etGjRFJ/TvXt3u/XWW+3222/3z88991zbvn273XHHHda1a1cvT0+uS5cu3qwtNtNN4A0AAAAAyNCZ7mzZslmVKlVs+vTp0fv27dvnn9esWTPF5+zYsWO/wFqBu6jcPCXZs2e3PHnyJLkBAAAAAJChM92iDHSrVq2satWqVq1aNd+DW5lrdTOXli1bWvHixb1EXJo0aeIdzytXrux7eq9YscKz37o/CL4BAAAAAIgX6Rp0N2/e3DZs2GA9evSwtWvXWqVKlWzSpEnR5morV65Mktnu1q2bZcmSxf//+++/W6FChTzgfvzxx9PxpwAAAAAAIGVZIqnVZWdQWtOthmqbN2+m1BwAMqkqnUel9yEgg1j4TMv0PgQAQJzHlgnVvRwAAAAAgExTXq7S8OXLl/vHZcqU8XJvAAAAAABwFJluNTtr06aNFStWzC655BK/6eO2bdt6h3EAAAAAAHCEQbe6jn/22Wf24Ycf2t9//+23Dz74wO/7z3/+k/ZHCQAAAABAZikvf++99+zdd9+1OnXqRO9r1KiR5cyZ02644QZ7+eWX0/IYAQAAAADIPJlulZAH23rFKly4MOXlAAAAAAAcTdBds2ZN69mzp+3atSt6386dO613797+NQAAAAAAcITl5QMHDrT69etbiRIlrGLFin7fkiVLLEeOHDZ58uS0PkYAAAAAADJP0F2hQgX78ccf7c0337Rly5b5fS1atLCbb77Z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wU9gtnf3zjR5HSCtxmRaYWwjo45tYXwjrTC+kZHF4/gG4vFvJBKJxG/QfSz069fPevfuvd/9JUuWTJfjAVKT94W70vsQgFAwtpGRMb6RkTG+gUOzdetWy5s3b3wG3QULFrTjjjvO1q1bl+R+fV60aNEUn6P7D+fxXbp08UZtgX379tmmTZusQIECliVLljT5OXDg2R9NcKxatcry5MmT3ocDpCnGNzIyxjcyKsY2MjLG97GlDLcC7mLFih3wcekadGfLls2qVKli06dP9w7kQVCsz9u3b5/ic2rWrOlf79ixY/S+qVOn+v0pyZ49u99i5cuXL01/Dhyc/uj5w0dGxfhGRsb4RkbF2EZGxvg+dg6U4Y6b8nJloVu1amVVq1a1atWq2YABA2z79u3ezVxatmxpxYsX9zJx6dChg9WuXdv69+9vjRs3trffftsWLFhgQ4YMSeefBAAAAACAOAu6mzdvbhs2bLAePXp4M7RKlSrZpEmTos3SVq5c6R3NAxdeeKGNHj3aunXrZo8++qiVLl3axo8fbxUqVEjHnwIAAAAAgDgMukWl5KmVk8+YMWO/+66//nq/If6ptL9nz577lfgDGQHjGxkZ4xsZFWMbGRnjOz5liRysvzkAAAAAADgi/1e3DQAAAAAA0hRBNwAAAAAAISHoBgAAAAAgJATdAJBJaGvF999/P70PA0gTzzzzjP3000/pfRgAABwUQTcAZALaknHixIn2yCOP2P/+97/0PhzgqPz666/28MMP+3j+7bff0vtwgKPWpk0bu+iii9L7MACEhKAbmVLQtD+2eT+N/JGRFS1a1Lp27WoXXnihde7c2QNwIBHt27fPSpUqZUuWLLEpU6ZYp06dPAgHEtmNN95oP//8szVt2jS9DwVACAi6kSkv2LJkyeIfb9682bZv3x69j8AbGVEwri+44AK799577fzzz7eHHnqIwBsJKWvWrLZ3714799xzbdasWR54/+c//yHwRkK74oor7K233rIvvvjCrr766vQ+HGSw9/8tW7bYv//+m96Hk6kRdCNTUXCtC7ZgPeB1111nl112mTVo0MBWrVoVDcaBjETjWkGKVK1a1dq3b0/gjYR23HHH+QXkeeedZ59//jmBNzKE2rVr25gxY+zLL78k8EaaBNx6/58wYYJ17NjR5syZE70WwLFH0I1MJQi4VWaroLtly5b29NNP27Jly+zKK6+0TZs2pfchAqHMcitICVSrVs3uueceAm8k3KRprOOPP97/r8B79uzZNnnyZAJvJPxSt1q1ahF4I00o4B43bpw1b97cTjvtNCtevHiSawGqO4+t//+OBWSS2T5ZuXKlZ0XeeOMNL+f6+OOPveymS5culj9//hSfAySiYAwrINE4V9BStmxZb9hTo0YN//rLL7/sgbce17Bhw/Q+ZOCgVUojR470wHrjxo3WoUMHK1asmFWsWNHHuRpRaSw/++yzvu4bSIQxresSBUP58uWzk046ycexAu8bbrjBA+8PPvjAH8d1CQ7Hd9995xnuF154wVq3bh29/8cff/Q+L7lz504yDhEufsvI0PSGpVm+2PXaymbrDU4Bt7o4t2jRwp566im7++67bdu2bfbiiy962SJvbMgos9xNmjTxIEXbK/Xv39/uv/9+/3rNmjV93Gutd9u2bW3q1KnpfchAioKLQnUrV9fyH374webNm2dXXXWVvffee/bXX39ZpUqVvNR82rRpfoGpjv1APIoNdB577DE/R9erV8+qVKliX3/9tQfgKjUfO3asj/NrrrnGH8t1CQ6HrmkLFy7sY2nnzp320ksvWZ06dXyCXQ371qxZQ8B9DPGbRoa1Y8cOy5Ytm910000eXAdvVmeffbZVrlzZyxAVlD/33HN25513+te09cxHH31kc+fOTeejBw7fP//8k+RzXayps/OTTz5pb7/9tvXt29fWr19vgwcPtltvvTUaeCvzrWzKmWeemU5HDhzcK6+84o2mVEY+evRoXyK0fPlye+KJJ3z/eTXGVMZbk0cKWnSxCcQbJQCCQKdHjx4+0a//jx8/3goUKOBL3TTGY9d4K9OtCSfgQILkUnAtoATSunXr7PHHH/dlOKry1Ht+7969bfXq1fbpp5+m8xFnMhEgA/vrr78i7du3j5xwwgmRCRMm+H1bt26N3HrrrZHs2bP71wI7duyINGrUKNK4cePI3r170/GogcPXqVOnyGuvvZbkvldffTVy5513+se//fZb5PTTT4/cdtttkUGDBkWyZcsWue+++6KP3blz5zE/ZiA1LVq0iEyZMiX6+fbt2yNPP/105KWXXvLP33vvvUjevHkjQ4cOjVx33XWRwoULR4YNGxbZuHFjktfhXI54MXPmzCSfz507N1KjRo3I9OnT/fMPP/wwki9f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PDBW9viVV16JPl5Bd+3atT3bnSNHDm+kpkz166+/7l/XW7oyzQpclTVWpltZYgW8yjI/+uij9vbbb3tQfsIJJ+x3PMo+//333zZ+/HjbvXu35c+f36ZNm+YTAIHbb7/ds+ujR49O8lwdS8eOHf0mjRo18vteeukl/1xZ+m+++cZ/1uB7ffTRR7Zq1SrLlSuX36fgunPnzrZ582b7559/Duv7AwAQr8h0AwCQTsqWLWsXXnihDR8+3D9fsWKFN1FTafmSJUs8C64y7uBWv359L7n+5Zdfoq8Ru+5bpeQKutevX5/i91OZuYL0lALu5HQsCm4vv/zyJMegzHNq5eix2rVr59nrXbt2ecm4gmRlwGOpXDwIuEXBtcrrFYgf7fcHACBepH1nFQAAcMgUYN933302aNAgb6B25plnejZbweedd97pGeLktHY7kDyAVuCtwDwlWhd9qPT9ZcKECVa8ePEkX1MW/lC6s+tx77//vmXLls0z182aNTtm3x8AgHhB0A0AQDq64YYbrEOHDp4JVhZXa7IVOJ9//vn23Xff2VlnnZVm30tZ8ddee80D4INlu8uXL+/B7cqVK30S4EjWrLdq1conEhR033jjjfsF/crm79y5M3r/F1984dlsNY1TafnRfH8AAOIFQTcAAOlIQWbz5s2tS5cutmXLFl/rLA8//LDVqFHDG6dpHfOJJ57oQbi2GXvxxReP6HvptV544QUPgPX98ubN64FutWrVrEyZMkkeq07k2hZMzcuUOb/ooot8rfXnn3/uncYVUB+Mjltr1kXPS05l58r0d+vWzdef9+zZ048xa9asafL9AQCIBwTdAACkMwWew4YN8+ZjxYoVi2al1aW7a9euvg5bTdJUeq4A/UgVKFDAu5arWZmyx2rUpgZrtWrVSvHxffv29e281EX8559/tnz58nkGXg3ZDkXp0qV9zfqmTZusevXq+329bt26/hg1j1PjthYtWlivXr3S7PsDABAP6F4OAABCoUsMBdXayqxTp06pdkoHACAjI9MNAADS3IYNG3x7srVr1ybZmxsAgMyGoBsAAKS5woULW8GCBW3IkCF28sknp/fhAACQbigvBwAAAAAgJFnDemEAAAAAADI7gm4AAAAAAEJC0A0AAAAAQEgIugEAAAAACAlBNwAAAAAAISHoBgAAAAAgJATdAAAAAACEhKAbAAAAAICQEHQDAAAAAGDh+H/nm8TTtj2tdAAAAABJRU5ErkJggg==", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA90AAAHqCAYAAAAZLi26AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQAAcedJREFUeJzt3QmclfP7//ErSxst2rWQJS2iUlpRFG0ipGQplRKi9BUSLUKkqC+R0iJEIVnq20oqRYsKUWSraBXtC3X+j/f1f9znd2aa9rmbc2Zez8fjMHPmnDP3TJ+5z319rutzfTJFIpGIAQAAAACAVHdC6r8kAAAAAAAQgm4AAAAAAEJC0A0AAAAAQEgIugEAAAAACAlBNwAAAAAAISHoBgAAAAAgJATdAAAAAACEhKAbAAAAAICQEHQDAAAAABASgm4AAJDEr7/+apkyZbKRI0emyuvNmDHDX+/dd9+1RHIkx3377bdb8eLFj/h76Dl6LgAg/SLoBoAM4KWXXvLgoUqVKml9KHFHQc/VV19t8U7/frG3nDlzWs2aNW3ChAlH/ZqjR4+2AQMGWHpw4YUX2hlnnGGRSOSAj6lRo4YVLFjQ/v33X0tvNEGSfIykdDuaiQEAwLE56RifDwBIAG+++aZfbM+bN89WrFhh5557blofEo7ClVdeaS1atPDA8rfffrOXX37ZGjVqZP/73/+sbt26RxV0f/vtt9apU6ck95955pm2c+dOO/nkky1R3HLLLfbwww/brFmz7LLLLksxez937lzr0KGDnXRS6l/+DB061Pbt22dpRT/z66+/nuS+O+64wypXrmzt2rWL3nfqqaemwdEBQMZG0A0A6dwvv/xic+bMsXHjxtmdd97pAXiPHj2O6zEoGNmzZ49lzZr1uH7f9Oa8886zW2+9Nfr5DTfcYGXKlLGBAwceVdB9IMqIJtq/1c0332xdu3b1iYSUgu633nrLJysUnIchrScozj77bL/Fat++vd8XO2YAAMcf5eUAkM4pyD7ttNOsYcOG1qRJE/888M8//1iePHmsVatW+z1vy5YtHng98MAD0ft2797tAbsy5VmyZLFixYrZgw8+6PcnD9qUUdT3Ov/88/2xkyZN8q/169fPqlevbnnz5rVs2bJZxYoVU1wzq0zrfffdZ/ny5bMcOXLYNddcY7///ru/ds+ePZM8Vve3bt3aS4f1vfQ9hw8fbqlF5ci9e/e2c845x19fVQOPPPLIfj/3ggULPPjVMetnO+uss/y4Yr399tv+M+tnUon4BRdc4EHz0ShdurR/r59++inJ/R988IH/excuXNiPV8et49+7d2/0MbVq1fLSdGXMk5ceH2hN9yeffGKXXnqpnXLKKZY7d2679tpr7fvvvz/s49X31++tUKFC/hr6N121alX06xpbCl43bNiw33OVrdX33LVrV4qvrbGoYFtjSeM6OQXj+j0ESyyOZMxo0ujJJ5+0okWL+t9E7dq1vWLkUGu69Tz92+rfWM/Lnz+/1atXz8fJwfz9999efaCfScemv7dnnnnmmDLp27Zt8995x44d9/va6tWr7cQTT7Q+ffokKVWfOXOmT9Tpb1VjVVUWf/31137PV6VFMC40rjX2li5detTHCgDpDZluAEjnFPhef/31ljlzZmvevLmXJM+fP98uvvhiD3Cuu+46z4K/8sor/pjA+PHjPai86aab/HNd8CtImj17tgdACvi++eYbe/755+2HH37wxycP0MaOHevBtwLDICBREKLXUcZR2W8FoTfeeKN9/PHHfrEeG8To+bfddptVrVrVPvvssyRfD6xbt86/HgT6CmwUBLRp08YnDpKXTh8Nlem+9tprPmnxn//8x7788ksPUBRwvv/++/6Y9evX21VXXeXfX2XOChAVvOp3G5g6dar/GyhoUxAleo3PP/88xWDoUDZv3uxBkILJWAqaVEbcuXNn/7/+Lbp37+6/j2effdYf061bN3++Ai79Gx6q9HjatGlWv359z5xq0kOTIi+88IKvk/7qq68Oa62wAlf9Oz300EP++9J68jp16tjixYt9kkL/1o8//riNGTPG/y0DGicKppXZP1gGXmNKY3Py5MlJ1ulrnKqMXr+DoxkzTz/9tJ1wwgk+AaXfWd++ff17aRwcjF5P/xb6vWkMafJG5e9ffPGFVapUKcXn7Nixw9fqa1JAAa/WqatSRVn8NWvWHPUafP3b6m9dv9vnnnvOg+xDVQHod6NxrH/v5cuX+7lDkzRBgzlRSXvLli19skljWsevx11yySW2aNEi1pADgEQAAOnWggUL1FUqMnXqVP983759kaJFi0Y6duwYfczkyZP9MR999FGS5zZo0CBy9tlnRz9//fXXIyeccEJk1qxZSR43ePBgf/7nn38evU+f67FL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kD59+liuXLmit2LFioV2fAAAAAAAHPWWYQEF28pUK2t82mmnRe9v166dr8k+HPny5bMTTzzR1q1bl+R+fV6oUKEUn6OO5SeffLI/L1C6dGnPjKtcPXPmzPs9p2vXrt6sLaBjJvAGAAAAAMRtpnvnzp1eth0E3L/99psNGDDAu4krE304FCArWz19+vQkmWx9rnXbKVHztBUrVvjjAj/88IMH4ykF3KK15zlz5kxyAwAAAAAgboPua6+91kaNGhXdN1v7a2ttdePGje3ll18+7NdRBlpbfr322mv2/fff21133WXbt2/3bubSokULz1QH9HV1L+/YsaMH2xMmTPBGamqsBgAAAABAugi6taf2pZde6h+rsZmanynbrUD8v//972G/jtZk9+vXz7p3727ly5e3xYsX26RJk6LN1VauXJmkG7rKwidPnmzz58+3Cy+80O677z4PwB9++OGj+TEAAAAAAIi/Nd07duywHDly+MdTpkyx66+/3rf7qlq1qgffR6JDhw5+S8mMGTP2u0+l51988cXRHDYAAAAAAPGf6T733HNt/PjxtmrVKs88X3XVVX7/+vXrWTMNAAAAAMCxBN0qB3/ggQesePHivkVY0PhMWe8KFSoczUsCAAAAAJDuHFV5eZMmTeySSy7x9dbBHt1Su3Ztu+6661Lz+AAAAAAAyFhBt2gvbd1Wr17tnxctWtSz3gAAAAAA4BjKy7VP9uOPP265cuWyM88802+5c+e23r17J9lDGwAAAACAjOyoMt3dunWzYcOG2dNPP201atTw+2bPnm09e/a0Xbt22ZNPPpnaxwkAAAAAQMYIul977TV79dVX7Zprronep32zixQpYnfffTdBNwAAAAAAR1tevmnTJitVqtR+9+s+fQ0AAAAAABxl0K2O5S+++OJ+9+s+ZbwBAAAAAMBRlpf37dvXGjZsaNOmTYvu0T137lxbtWqVTZw4MbWPEQAAAACAjJPprlmzpv3www++J/fff//tt+uvv96WLl1qr7/+euofJQAAAAAACShTJBKJpNaLLVmyxC666CLbu3evxastW7b4VmebN2+2nDlzpvXhAADSQMUuo9L6EJBOLHy2RVofAgAgzmPLo8p0AwAAAACAQyPoBgAAAAAgJATdAAAAAADEQ/dyNUs7GDVUAwAAAAAARxF0a5H4ob7eogUNRQAAAAAAOOKg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1Hzs2LE+zq+77jp/LNclOBK6pi1QoICPpZ07d9pLL71ktWrV8gl2Nexbs2YNAfdxxG8a6daOHTssc+bMdvPNN3twHbxZnXfeeVahQgUvQ1RQ/txzz9mdd97pX9PWMx999JHNnTs3jY8eOHL//PNPks91sabOzk8//bS9/fbb1rt3b1u/fr0NHjzYbrvttmjgrcy3sinnnHNOGh05cGivvPKKN5pSGfno0aN9idDy5cvtqaee8v3n1RhTGW9NHilo0cUmEG+UAAgCne7du/tEv/4/fvx4y5s3ry910xiPXeOtTLcmnICDCZJLwbWAEkjr1q2zJ5980pfhqMpT7/m9evWy1atX26effprGR5zBRIB07K+//op06NAhcvLJJ0cmTJjg923dujVy2223RbJkyeJfC+zYsSPSoEGDSMOGDSN79+5Nw6MGjlznzp0jr732WpL7Xn311cidd97pH//222+Rs846K3L77bdHBg0aFMmcOXPk3nvvjT52586dx/2YgQNp3rx5ZMqUKdHPt2/fHunbt2/kpZde8s/fe++9SK5cuSJDhw6N3HDDDZECBQpEhg0bFtm4cWOS1+Fcjngxc+bMJJ/PnTs3UrVq1cj06dP98w8//DCSO3fuSMWKFSM5cuSITJo0KfrYr776KvLvv/8e92NG4pk9e3akfPnykQ0bNvjnw4cPj9x1112RRx99NLJixYro4zT23nnnnTQ80ownk/6T1oE/EGbp1tatW33N6rBhw7zUVrPImuFTlluP07YzWvunGUCVKC5cuNBOPvlk1rkgoTz22GPejV8ltupOqkyfTu/z58/3hmkNGjTwta9aD6uyW812q7JDZbj62wDixYoVK+zNN9+0Rx55xM/FgcWLF9vpp5/u53RVZrRt29bXKy5dutS78qsnh6o4VKrL2lfEEy3pUdZx0KBB0XGpfeY/+eQTu//++31JxC233OIZ7zvuuMPPz1oKN3DgwCTN1IJzO3Cw86eWKuTPn98rJnRe3L17t2XJkiXJ9cKoUaPss88+o/fFcUTQjXQnNljWNmBFixb1E47KyYcOHerr/3RRpoBDwcaMGTO8pEsnHpUrqiOu3hyDzrhAPEseXEyaNMnH/U033eRNUuTnn3/29VtqmqbOuEEDqrp163rDnrPPPjsNfwLgwDRmTznlFN9pIqDtb7QDxbvvvmvnnnuuXziq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CMQrjd2zzz7bypYta88884wHJddff703B7zhhhvshx9+8A7myoQL2W7EG1UWKXAOuo0rkG7QoIGPX239NW3aNMubN69PkrZp08Y+/fRTbw6ovbnffPNN374RABIJ3cszGAXbw4YN8zV/etPSmikZN26cr+NWI7WzzjrLL9L0Zvj5559H11KR4UYi0rKJN954w9asWeNjX+teRc16VEqu7OBtt91mefLkSetDBQ6L9oxXcKLbvffem2Qv4hdffNGDbk0gXXHFFT6xpOCFwBvxYvPmzdakSRPPaiu4VsWdGgBqkkgJgrfeesubtmrs6nFbtmyxqVOnes8NbVs6ePBgrksAJByC7gxE28i0a9fOA43y5ct7WZea7fz8889WtWpV35dbb2oquS1WrJi/CeoNTaVesRd1QCIIto155JFHPMudPXt2XycYu5Zbgbcy3yphvPPOO+20005L02MGDmcPbY1tLQP6+uuvfUyLzudB1lB0ntdyiQ8//NBq1qx53I8bONTEkSY81Wn/scces61bt1qPHj38a2pmqcatmkBSAK7JpeS4LgGQaAi607Hke1XqzU1rt9UYTW9wyv6pTEtvXrlz5/ZSL3UOjcVMMhKVGksVLlzYP3722Wc986eJpAcffDB6v2giat68eV6+SNCNeB7PBQoUiAYaWrutZRBaz60xndI5X9vi5c+f3zODyQN3IK0nkRR4a/maAmtVGynTHYxhJQEUeL/88sue9dZytwD7cANIRLwLZ4CmabpYU2CtgFpvdrfccotddtllvm770Ucf9f25FYR/9tln+70OATcSkZr/NWzYMLrdjCab1Ghq1qxZ9sILL/jYD+gxWgtLwI14peVA6r1x99132+rVq/38ni9fPu+2ryVAqk6KDUSCuXQ1w1SQToCCeBEE3MGWYJoQ1bptNbpU1YbGqr6WOXNm78GhYDs4jwcYzwASEbU5GaBp2ooVK7xxlJqk9e7d23788Ucvq1XJoQINlZhrPau2BQPSy99AqVKlbPjw4V5yq4u6Xr16+cWcmlBpMkkBTJDxDnobAPFI+8grs61qjAsvvNCrM9Q47fbbb7cKFSp4pYYaTMUGJcuXL/eAXI3WCFIQT9cl/fr184lRVR/pHKxtSjVBpE7806dPt4svvtjP1Tp3q0qDyX8A6QHl5em8adqIESM8s3fppZfa6aefnuTrWgP4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ihUrZmvWrLGyZctaauvZs6cf48FuAACAoBsAgDTRpk0bmzp1qq1evXq/r40YMcIqVapkF1544TF9jxNPPNEKFSpkJ510kqW2Bx54wAP64Fa0aFF7/PHHk9wHAAAIugEASBNXX3215c+f30aOHJnk/m3bttk777zjQfns2bPt0ksvtWzZsnnW+r777rPt27cnefyOHTusdevWliNHDjvjjDNsyJAhBy0vX7p0qX/vnDlz+nP0+j/99FOKx7hv3z7r06ePnXXWWX4M5cqVs3fffde/duqpp3pAH9wU4Ov19LGOoU6dOvu9Xvny5e2xxx5LUsreq1cv/z3oeNq3b2979uw5rO8PAECiIOgGACANKPvcokULD7ojkUj0fgXce/futWrVqnn5+Q033OBl5mPGjPEgvEOHDklep3///p4VX7Rokd19991211132fLly1P8nr///rtddtllliVLFvvkk09s4cKFHrD/+++/KT5eAe+oUaNs8ODBHqzff//9duutt9pnn3120J9Nr/n999/b/Pnzo/fp+PRztGrVKnrf9OnT/XEzZsywt956y8aNG+dB+LF+fwAA4kmmSOw7PQAAOG6WLVtmpUuXtk8//dRq1arl9ykoPvPMMz0wVvb4lVdeiT5eQXfNmjU92501a1ZvpKZM9euvv+5f11u6Ms0KXJU1VqZbWWIFvMoyP/LII/b22297UH7yySfvdzzKPv/99982fvx42717t+XJk8emTZvmEwCBO+64w7Pro0ePTvJcHUunTp38Jg0aNPD7XnrpJf9cWfpvvvnGf9bge3300Ue2atUqy549u9+n4LpLly62efNm++eff47o+wMAEK/IdAMAkEZKlSpl1atXt+HDh/vnK1as8CZqKi1fsmSJZ8FVxh3c6tat6yXXv/zyS/Q1Ytd9q5RcQff69etT/H4qM1eQnlLAnZyORcHtlVdemeQYlHk+UDl6rLZt23r2eteuXV4yriBZGfBYKhcPAm5RcK3yegXix/r9AQCIF6nfWQUAABw2Bdj33nuvDRo0yBuonXPOOZ7NVvB55513eoY4Oa3dDiQPoBV4KzBPidZFHy59f5kwYYIVKVIkydeUhT+c7ux63Pvvv2+ZM2f2zHWTJk2O2/cHACBeEHQDAJCGmjZtah07dvRMsLK4WpOtwPmiiy6y7777zs4999xU+17Kir/22mseAB8q212mTBkPbleuXOmTAEezZr1ly5Y+kaCg+6abbtov6Fc2f+fOndH7v/jiC89mq2mcSsuP5fsDABAvCLoBAEhDCjKbNWtmXbt2tS1btvhaZ3nooYesatWq3jhN65hPOeUUD8K1zdiLL754VN9Lr/XCCy94AKzvlytXLg90K1eubCVLlkzyWHUi17Zgal6mzPkll1zia60///xz7zSugPpQdNxasy56XnIqO1em/9FHH/X15z169PBjPOGEE1Ll+wMAEA8IugEASGMKPIcNG+bNxwoXLhzNSqtLd7du3XwdtpqkqfRcAfrRyps3r3ctV7MyZY/VqE0N1mrUqJHi43v37u3beamL+M8//2y5c+f2DLwash2OEiVK+Jr1TZs2WZUqVfb7eu3atf0xah6nxm3Nmze3nj17ptr3BwAgHtC9HAAAhEKXGAqqtZVZ586dD9gpHQCA9IxMNwAASHUbNmzw7cnWrl2bZG9uAAAyGoJuAACQ6goUKGD58uWzIUOG2GmnnZbWhwMAQJqhvBwAAAAAgJCcENYLAwAAAACQ0RF0AwAAAAAQEoJuAAAAAABCQtANAAAAAEBICLoBAAAAAAgJQTcAAAAAACEh6AYAAAAAICQE3QAAAAAAhISgGwAAAAAAC8f/AwX5bJ99O8h7AAAAAElFTkSuQmCC", "text/plain": [ "
" ] @@ -141,7 +132,7 @@ "# Loss Ratio by Vehicle Type\n", "plt.figure(figsize=(10, 5))\n", "sns.barplot(data=df, x='VehicleType', y='LossRatio', estimator='mean', ci=None)\n", - "plt.title(\"Average Loss Ratio by Province\")\n", + "plt.title(\"Average Loss Ratio by Vehicle Type\")\n", "plt.xticks(rotation=45)\n", "plt.tight_layout()\n", "plt.show()\n" diff --git a/src/README.md b/src/README.md index f3d335f..60935a7 100644 --- a/src/README.md +++ b/src/README.md @@ -1,9 +1,83 @@ -# Source Code +# 📦 `src/` — Source Code Overview -This folder contains reusable Python modules for the project. +This directory contains all core logic and modular components used in data processing, hypothesis testing, modeling, and interpretability for the **Insurance Risk Modeling** project. -- `data_loader.py`: Functions for loading and saving data. -- `preprocessing.py`: Data cleaning and preprocessing utilities. -- `config.py`: Configuration and path management. +--- -Import these modules in notebooks and scripts to avoid code duplication. \ No newline at end of file +## 📁 Top-Level Modules + +| File | Description | +|------|-------------| +| `__init__.py` | Marks the directory as a Python package. | +| `config.py` | Central location for storing global configuration variables (paths, constants, toggles). | +| `data_loader.py` | Contains utilities for loading raw and processed datasets. | +| `preprocessing.py` | High-level preprocessing functions for cleaning and transforming data. | +| `README.md` | This file – documentation of the codebase structure. | + +--- + +## 🔍 `task_1/` — Exploratory Data Analysis & Visualization + +### 📁 `eda/` — EDA Logic +| File | Description | +|------|-------------| +| `bivariate.py` | Analyzes relationships between pairs of variables (e.g., claims vs. gender). | +| `univariate.py` | Summarizes distributions of individual features. | +| `summary_stats.py` | Provides descriptive statistics and summary tables. | +| `outlier_detection.py` | Detects and flags unusual or extreme values using statistical methods. | + +### 📁 `viz/` — Visualization Utilities +| File | Description | +|------|-------------| +| `plot_utils.py` | Helper functions for generating consistent plots (bar charts, heatmaps, boxplots, etc.). | + +--- + +## 📈 `task_3/` — Hypothesis Testing & Risk Segmentation + +| File | Description | +|------|-------------| +| `hypothesis_tests.py` | Implements z-tests, t-tests, chi-squared tests for hypothesis validation. | +| `data_segmentation.py` | Splits the data by demographic and regional segments for focused analysis. | +| `business_analysis.py` | Converts statistical results into actionable business interpretations. | +| `segmentation_utils.py` | Utilities to group and label segmented datasets. | +| `stats_helpers.py` | Reusable statistical functions (p-value calculators, assumptions checks, etc.). | + +--- + +## 🤖 `task_4/` — Modeling & Interpretability + +| File | Description | +|------|-------------| +| `data_processing.py` | Handles encoding, normalization, splitting for modeling. | +| `feature_engineering.py` | Creates new predictive features such as vehicle age, conversion flags, etc. | +| `model_training.py` | Core training logic for regression and classification models (XGBoost, Random Forest, etc.). | +| `interpretability.py` | Generates SHAP & LIME explanations to interpret model decisions. | + +--- + +## ✅ Usage Example + +```python +from src.data_loader import load_clean_data +from src.task_1.eda.univariate import summarize_numerics +from src.task_4.model_training import train_xgboost_model +from src.task_4.interpretability import explain_with_shap + +# Load and analyze data +df = load_clean_data("data/processed/cleaned_data.csv") +summarize_numerics(df) + +# Train and explain model +model, X_test = train_xgboost_model(df) +explain_with_shap(model, X_test) +``` +🧩 Design Philosophy +Modular: Code is separated into logically coherent, reusable units. + +Interpretable: Business-facing logic (e.g., hypothesis results) is separated from statistical code. + +Scalable: Easy to extend for future tasks like time-series modeling or real-time pricing engines. + +📝 Note +Ensure all module imports use relative paths (from .module import ...) if running as a package, or adjust PYTHONPATH accordingly for standalone script runs. diff --git a/tests/README.md b/tests/README.md index af2d0c9..feea2a2 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,8 +1,82 @@ -# Tests +# 🧪 `tests/` — Unit Testing Suite -This folder includes unit tests for core logic in the `src/` folder using `pytest`. +This directory contains all unit tests for verifying the correctness, robustness, and reliability of the codebase modules in the `src/` directory. The tests are organized by task to maintain modularity and clarity. -Run using: +> ✅ To run all tests: +```bash +pytest +``` +```bash +tests/ +│ +├── __init__.py # Marks the test suite as a Python package +├── README.md # Test documentation (this file) +├── test_data_loader.py # Tests for general data loading functionality +├── test_example.py # Sample test (placeholder or CI verification) +│ +├── test_task_3/ # Tests for Task 3 - Hypothesis Testing & Segmentation +│ ├── __init__.py +│ ├── test_business_analysis.py +│ ├── test_data_segmentation.py +│ ├── test_hypothesis_tests.py +│ ├── test_segmentation_utils.py +│ ├── test_stats_helpers.py +│ └── __pycache__/ # Compiled Python files (auto-generated) +│ +└── test_task_4/ # Tests for Task 4 - Modeling & Interpretability + ├── __init__.py + ├── test_feature_engineering.py + ├── test_model_training.py + └── __pycache__/ # Compiled Python files (auto-generated) +``` +## 🧪 Test Descriptions + +### 📁 Top-Level Tests + +| File | Description | +|------------------------|-----------------------------------------------------------------------------| +| `test_data_loader.py` | Validates data loading functions: existence, structure, and content of dataframes. | +| `test_example.py` | Generic test file — often used to test CI pipelines or placeholder logic. | + +--- + +### 🔍 `test_task_3/` — Risk Segmentation & Hypothesis Testing + +| File | Tests | +|-----------------------------|-----------------------------------------------------------------------------| +| `test_business_analysis.py` | Business logic functions that interpret test results (e.g., actionable pricing decisions). | +| `test_data_segmentation.py` | Region/segment-based splits and checks for distribution consistency. | +| `test_hypothesis_tests.py` | Statistical hypothesis testing: validates p-values, group means, assumptions. | +| `test_segmentation_utils.py`| Utility functions for grouping and label mapping. | +| `test_stats_helpers.py` | Helper statistical computations like t-scores, z-values, variance checks. | + +--- + +### 🤖 `test_task_4/` — ML Models & Interpretability + +| File | Tests | +|------------------------------|-----------------------------------------------------------------------------| +| `test_feature_engineering.py`| Tests new feature creation (vehicle age, conversion flags, VAT status). | +| `test_model_training.py` | Ensures models train without errors, output expected performance metrics, and save artifacts. | + +## 🧰 Running Tests + +### 🔃 Run All Tests ```bash pytest -``` \ No newline at end of file +``` +🧪 Run a Specific File +```bash +pytest tests/test_task_4/test_model_training.py +``` +✅ Best Practices +Follow naming conventions: test_.py and test_(). + +Use fixtures for setup/teardown. + +Mock external dependencies or file access where possible. + +Keep test coverage above 80% for critical modules (Task 4, Task 3). + +🔄 CI Integration +These tests are automatically executed via GitHub Actions to ensure all pushes to main maintain code quality and prevent regressions. \ No newline at end of file