Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save e-roux/8b59bb6a04404e5903b0598ff25dda5d to your computer and use it in GitHub Desktop.
Save e-roux/8b59bb6a04404e5903b0598ff25dda5d to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"button": false,
"new_sheet": false,
"run_control": {
"read_only": false
}
},
"source": [
"### About dataset"
]
},
{
"cell_type": "markdown",
"metadata": {
"button": false,
"new_sheet": false,
"run_control": {
"read_only": false
}
},
"source": [
"This dataset is about past loans. The __Loan_train.csv__ data set includes details of 346 customers whose loan are already paid off or defaulted. It includes following fields:\n",
"\n",
"| Field | Description |\n",
"|----------------|---------------------------------------------------------------------------------------|\n",
"| Loan_status | Whether a loan is paid off on in collection |\n",
"| Principal | Basic principal loan amount at the |\n",
"| Terms | Origination terms which can be weekly (7 days), biweekly, and monthly payoff schedule |\n",
"| Effective_date | When the loan got originated and took effects |\n",
"| Due_date | Since it’s one-time payoff schedule, each loan has one single due date |\n",
"| Age | Age of applicant |\n",
"| Education | Education of applicant |\n",
"| Gender | The gender of applicant |"
]
},
{
"cell_type": "markdown",
"metadata": {
"button": false,
"new_sheet": false,
"run_control": {
"read_only": false
}
},
"source": [
"Lets download the dataset"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"button": false,
"new_sheet": false,
"run_control": {
"read_only": false
}
},
"outputs": [],
"source": [
"!curl -sLo loan_train.csv https://s3-api.us-geo.objectstorage.softlayer.net/cf-courses-data/CognitiveClass/ML0101ENv3/labs/loan_train.csv\n",
"!curl -sLo loan_test.csv https://s3-api.us-geo.objectstorage.softlayer.net/cf-courses-data/CognitiveClass/ML0101ENv3/labs/loan_test.csv"
]
},
{
"cell_type": "raw",
"metadata": {},
"source": [
"!pip install -U --force sklearn watermark seaborn >/dev/null"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"button": false,
"new_sheet": false,
"run_control": {
"read_only": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2020-07-30 16:07:59 \n",
"\n",
"CPython 3.8.3\n",
"IPython 7.14.0\n",
"\n",
"sklearn 0.0\n",
"numpy 1.19.1\n",
"pandas 1.1.0\n",
"matplotlib 3.3.0\n",
"seaborn 0.10.1\n"
]
}
],
"source": [
"from itertools import product\n",
"\n",
"from pathlib import Path\n",
"from typing import (List, Tuple, Union)\n",
"\n",
"import numpy as np\n",
"import matplotlib as mpl\n",
"import matplotlib.pyplot as plt\n",
"\n",
"import pandas as pd\n",
"from pandas import DataFrame, Series\n",
"from numpy import ndarray\n",
"\n",
"import matplotlib.ticker as ticker\n",
"from sklearn.base import (BaseEstimator, TransformerMixin)\n",
"from sklearn.compose import ColumnTransformer\n",
"from sklearn.pipeline import Pipeline\n",
"from sklearn.preprocessing import (LabelEncoder, StandardScaler)\n",
"from sklearn.model_selection import GridSearchCV, train_test_split\n",
"\n",
"from sklearn.neighbors import KNeighborsClassifier\n",
"from sklearn.tree import DecisionTreeClassifier\n",
"from sklearn.svm import SVC\n",
"from sklearn.linear_model import LogisticRegression\n",
"\n",
"# In binary and multiclass classification, accuracy_score\n",
"# is equal to the jaccard_score function.\n",
"from sklearn.metrics import (\n",
" accuracy_score,\n",
" jaccard_score,\n",
" f1_score,\n",
" log_loss\n",
")\n",
"\n",
"%load_ext watermark\n",
"%watermark -dvtp sklearn,numpy,pandas,matplotlib,seaborn"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"# Ignore DataConversionWarning from sklearn\n",
"import warnings\n",
"from sklearn.exceptions import DataConversionWarning\n",
"warnings.filterwarnings(action='ignore', category=DataConversionWarning)"
]
},
{
"cell_type": "markdown",
"metadata": {
"button": false,
"new_sheet": false,
"run_control": {
"read_only": false
}
},
"source": [
"### Load Data From CSV File "
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"button": false,
"new_sheet": false,
"run_control": {
"read_only": false
},
"scrolled": true
},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>loan_status</th>\n",
" <th>principal</th>\n",
" <th>terms</th>\n",
" <th>effective_date</th>\n",
" <th>due_date</th>\n",
" <th>age</th>\n",
" <th>education</th>\n",
" <th>gender</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>PAIDOFF</td>\n",
" <td>1000</td>\n",
" <td>30</td>\n",
" <td>2016-09-08</td>\n",
" <td>2016-10-07</td>\n",
" <td>45</td>\n",
" <td>High School or Below</td>\n",
" <td>male</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>PAIDOFF</td>\n",
" <td>1000</td>\n",
" <td>30</td>\n",
" <td>2016-09-08</td>\n",
" <td>2016-10-07</td>\n",
" <td>33</td>\n",
" <td>Bechalor</td>\n",
" <td>female</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>PAIDOFF</td>\n",
" <td>1000</td>\n",
" <td>15</td>\n",
" <td>2016-09-08</td>\n",
" <td>2016-09-22</td>\n",
" <td>27</td>\n",
" <td>college</td>\n",
" <td>male</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>PAIDOFF</td>\n",
" <td>1000</td>\n",
" <td>30</td>\n",
" <td>2016-09-09</td>\n",
" <td>2016-10-08</td>\n",
" <td>28</td>\n",
" <td>college</td>\n",
" <td>female</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>PAIDOFF</td>\n",
" <td>1000</td>\n",
" <td>30</td>\n",
" <td>2016-09-09</td>\n",
" <td>2016-10-08</td>\n",
" <td>29</td>\n",
" <td>college</td>\n",
" <td>male</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" loan_status principal terms effective_date due_date age \\\n",
"0 PAIDOFF 1000 30 2016-09-08 2016-10-07 45 \n",
"1 PAIDOFF 1000 30 2016-09-08 2016-10-07 33 \n",
"2 PAIDOFF 1000 15 2016-09-08 2016-09-22 27 \n",
"3 PAIDOFF 1000 30 2016-09-09 2016-10-08 28 \n",
"4 PAIDOFF 1000 30 2016-09-09 2016-10-08 29 \n",
"\n",
" education gender \n",
"0 High School or Below male \n",
"1 Bechalor female \n",
"2 college male \n",
"3 college female \n",
"4 college male "
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df = pd.read_csv(\n",
" 'loan_train.csv',\n",
" parse_dates=['due_date', 'effective_date'],\n",
" dtype={\n",
" 'loan_status': 'category',\n",
" 'education': 'category',\n",
" 'Gender': 'category',\n",
" },\n",
" usecols=lambda x: \"Unnamed\" not in x)\n",
"# Make all columns names to lower case\n",
"df.columns = df.columns.str.lower()\n",
"df.head()"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<class 'pandas.core.frame.DataFrame'>\n",
"RangeIndex: 346 entries, 0 to 345\n",
"Data columns (total 8 columns):\n",
" # Column Non-Null Count Dtype \n",
"--- ------ -------------- ----- \n",
" 0 loan_status 346 non-null category \n",
" 1 principal 346 non-null int64 \n",
" 2 terms 346 non-null int64 \n",
" 3 effective_date 346 non-null datetime64[ns]\n",
" 4 due_date 346 non-null datetime64[ns]\n",
" 5 age 346 non-null int64 \n",
" 6 education 346 non-null category \n",
" 7 gender 346 non-null category \n",
"dtypes: category(3), datetime64[ns](2), int64(3)\n",
"memory usage: 15.0 KB\n"
]
}
],
"source": [
"df.info()"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"path = Union[str, Path]\n",
"\n",
"class DataFrameSelector(BaseEstimator, TransformerMixin):\n",
" def __init__(self, weekend_on: int = 3):\n",
" self.weekend_on = weekend_on\n",
" def fit(self, X: DataFrame, y=None) -> 'DataFrameSelector':\n",
" return self\n",
" def transform(self, X: DataFrame) -> ndarray:\n",
" attributes: List[str] = ['principal', 'terms', 'age', 'gender', 'education']\n",
" # Include new feature 'loan at end of the week' \n",
" X: DataFrame = X.copy()\n",
" dayofweek: Series = X['effective_date'].dt.dayofweek\n",
" X['weekend'] = dayofweek.apply(lambda x: 1 if (x>3) else 0) # (dayofweek >= weekend_on).astype(int) \n",
" X['gender'].replace(to_replace=['male','female'], value=[0,1],inplace=True)\n",
"\n",
" features: DataFrame = X[attributes + ['weekend']]\n",
"\n",
" encoded_features: DataFrame = pd.get_dummies(features).select_dtypes(exclude=['category'])\n",
" return encoded_features.values\n",
"\n",
"def read_csv(p: path) -> DataFrame:\n",
" df = pd.read_csv(\n",
" p,\n",
" parse_dates=['due_date', 'effective_date'],\n",
" dtype={\n",
" 'loan_status': 'category',\n",
" 'education': 'category',\n",
" 'Gender': 'category',\n",
" },\n",
" usecols=lambda x: \"Unnamed\" not in x)\n",
" \n",
" df.columns = df.columns.str.lower() # Make all columns names to lower case\n",
"\n",
" return df"
]
},
{
"cell_type": "markdown",
"metadata": {
"button": false,
"new_sheet": false,
"run_control": {
"read_only": false
}
},
"source": [
"# Classification "
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"df_train = read_csv('./loan_train.csv')\n",
"df_test = read_csv('./loan_test.csv')"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"y_encoder = LabelEncoder()\n",
"y_encoder.fit(df_train['loan_status'].values)\n",
"y_train = y_encoder.transform(df_train['loan_status'].values)\n",
"y_test = y_encoder.transform(df_test['loan_status'].values)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# K Nearest Neighbor(KNN)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## K Nearest Neighbor(KNN): find best K by train/test split"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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MDIzM5GVlQU/Pz+kpKRg3Lhx2v0eHh5ISEjQbk+bNg3PPfccgoODK3UcIiK6M7qKwtKlS7F3715s3boVq1evRtOmTdGlSxd06NBB1+U5jUYjhg8fjhkzZkBRFERHRyMwMBBJSUkIDg5GeHj4Hb8QIiK6c7qKgtFoRLt27dCuXTsUFRVhx44d+Prrr7F8+XK0b98eMTExCA0NveVzhIWFISwszGrbzU5+mzZtmr70RERUrSo1JbW4uBg7d+5ESkoKcnNzERkZiYCAACxYsEC7EA8REd29dLUU9u7diy1btiA1NRWhoaHo3r07Jk+eDFdXVwDA448/jpEjR+L555+3aVgiIrItXUXh448/RteuXTFkyJA/HGT28vLC0KFDqzsbERHZma6iMHfu3Nvu06NHjzsOQ0REjqVrTGHOnDk4cuSI1bYjR47oKhZERHT30FUUDh8+jKZNm1pta9KkCX7++WebhCIiIsfQVRRcXFxQXFxsta24uBhGo9EmoYiIyDF0FYVWrVph6dKlKCoqAgAUFRUhISEBrVu3tmU2IiKyM10DzXFxcViwYAGGDx8OLy8vXLp0Ca1bt8bYsWNtnY+IiOxIV1Hw8vLClClTcOHCBeTm5sLf3x+1atWycTQiIrK3Si2d7evri1q1akFKCUVRAAAGg12u00NERHagqyjk5eUhISEBR44cweXLl63uS0pKskkwIiKyP11f85cuXQqTyYS//e1vcHd3x6xZsxAeHo4XXnjB1vmIiMiOdBWFtLQ0jBw5EkFBQRBCICgoCCNHjsS6detsnY+IiOxIV1EwGAzaOQmenp4oKCiAm5sb8vLybBqOiIjsS9eYQuPGjZGamor27dujVatWeP/99+Hq6soroxER3WN0FYWxY8dCSgkAGDp0KNauXYsrV66gV69eNg1HRET2dduioCgKEhMTMWLECACAq6srnn76aZsHIyIi+7vtmILBYMCBAwcghLBHHiIiciBdA829evXC559/DovFYus8RETkQLrGFL7//nvk5+fjm2++gbe3t9V9//znP20SjIiI7E/3QDMREd37dBWFZs2a2ToHERE5AV1F4VbrGw0cOLDawhARkWPpKgq5ublWt/Pz83H48GG0b9/eJqGIiMgxdBWFUaNGVdi2b98+bNu2rdoDERGR41T5YggtW7bErl27qjMLERE5mK6Wwvnz561u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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"_X_train, _X_test, _y_train, _y_test = train_test_split(\n",
" df_train,\n",
" df_train['loan_status'].values,\n",
" test_size=0.2,\n",
" random_state=42\n",
")\n",
"\n",
"with plt.style.context('ggplot'):\n",
" fig, ax = plt.subplots(1)\n",
" x = range(1,10)\n",
" y = [np.mean(_y_test == Pipeline(steps=[\n",
" ('selector', DataFrameSelector()),\n",
" ('std_scaler', StandardScaler()),\n",
" ('knc', KNeighborsClassifier(n_neighbors=n))\n",
" ]) \\\n",
" .fit(_X_train, _y_train) \\\n",
" .predict(_X_test)) \n",
" for n in x]\n",
" ax.plot(x, y)\n",
" ax.set(ylim=[0, 1], xlabel='Number of neighbours', ylabel='Accuracy on test samples')\n",
" ax.axvline(np.argmax(y)+1, c='blue')\n",
" plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**7** seems to be the best coefficient with this random seed. Let's try a GridSearchCV"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## K Nearest Neighbor(KNN): GridSearchCV¶"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Pipeline(steps=[('selector', DataFrameSelector(weekend_on=4)),\n",
" ('std_scaler', StandardScaler()),\n",
" ('knc', KNeighborsClassifier())])\n",
"Best parameter (CV score=0.703):\n",
"Train set Accuracy: 0.815028901734104\n",
"Jaccard similarity score: 0.7\n",
"F1-score: 0.7001989201477693\n"
]
}
],
"source": [
"# Parameters of pipelines can be set using ‘__’ separated parameter names:\n",
"param_grid = {\n",
" 'knc__n_neighbors': np.arange(1, 10),\n",
"}\n",
"\n",
"knn_pipeline = Pipeline(steps=[\n",
" ('selector', DataFrameSelector(weekend_on=4)),\n",
" ('std_scaler', StandardScaler()),\n",
" ('knc', KNeighborsClassifier(n_neighbors=4))\n",
"])\n",
"\n",
"search = GridSearchCV(knn_pipeline, param_grid, cv=3, n_jobs=-1)\n",
"search.fit(df_train, y_train)\n",
"\n",
"knn_pipeline = estimator = search.best_estimator_\n",
"\n",
"print(estimator)\n",
"print(\"Best parameter (CV score=%0.3f):\" % search.best_score_)\n",
"# In binary and multiclass classification, accuracy_score\n",
"# is equal to the jaccard_score function.\n",
"print(\"Train set Accuracy: \", accuracy_score(y_train, estimator.predict(df_train)))\n",
"\n",
"y_hat = estimator.predict(df_test)\n",
"print(\"Jaccard similarity score: \", jaccard_score(y_test, y_hat))\n",
"print(\"F1-score: \", f1_score(y_test, y_hat, average='weighted'))"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"with plt.style.context('ggplot'):\n",
" fig, ax = plt.subplots(1)\n",
" x = [_res['knc__n_neighbors'] for _res in search.cv_results_['params']]\n",
" y_mean = search.cv_results_['mean_test_score']\n",
" sigma = search.cv_results_['std_test_score']\n",
" ax.plot(x, y_mean)\n",
" ax.fill_between(x, y_mean - sigma, y_mean + sigma, facecolor ='blue', alpha = 0.25)\n",
" ax.set(ylim=[0, 1], xlabel='Number of neighbours', ylabel='jaccard_score')\n",
" plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Not convinced by 7 as an optimum!, but let's take 7"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"knn_pipeline = Pipeline(steps=[\n",
" ('selector', DataFrameSelector()),\n",
" ('std_scaler', StandardScaler()),\n",
" ('knc', KNeighborsClassifier(n_neighbors=7))\n",
" ]) \\\n",
" .fit(df_train, y_train) "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Decision Tree"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"I'm using a `DecisionTreeClassifier(criterion=\"entropy\", max_depth=1)` model. THe GridSearchCV finds the best `max_depth` by itself"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Pipeline(steps=[('selector', DataFrameSelector(weekend_on=4)),\n",
" ('std_scaler', StandardScaler()),\n",
" ('dt',\n",
" DecisionTreeClassifier(criterion='entropy', max_depth=3))])\n",
"Best parameter (CV score=0.674):\n",
"Train set Accuracy: 0.7514450867052023\n",
"Jaccard similarity score: 0.7407407407407407\n",
"F1-score: 0.6304176516942475\n"
]
}
],
"source": [
"# Parameters of pipelines can be set using ‘__’ separated parameter names:\n",
"param_grid = {\n",
" 'dt__max_depth': range(1, 10)\n",
"}\n",
"\n",
"dt_pipeline = Pipeline(steps=[\n",
" ('selector', DataFrameSelector(weekend_on=4)),\n",
" ('std_scaler', StandardScaler()),\n",
" ('dt', DecisionTreeClassifier(criterion=\"entropy\", max_depth=1))\n",
"])\n",
"\n",
"search = GridSearchCV(dt_pipeline, param_grid, cv=3, n_jobs=-1)\n",
"search.fit(df_train, y_train)\n",
"\n",
"dt_pipeline = estimator = search.best_estimator_\n",
"\n",
"print(estimator)\n",
"print(\"Best parameter (CV score=%0.3f):\" % search.best_score_)\n",
"# In binary and multiclass classification, accuracy_score\n",
"# is equal to the jaccard_score function.\n",
"print(\"Train set Accuracy: \", accuracy_score(y_train, estimator.predict(df_train)))\n",
"\n",
"y_hat = estimator.predict(df_test)\n",
"print(\"Jaccard similarity score: \", jaccard_score(y_test, y_hat))\n",
"print(\"F1-score: \", f1_score(y_test, y_hat, average='weighted'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Support Vector Machine"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Pipeline(steps=[('selector', DataFrameSelector(weekend_on=4)),\n",
" ('std_scaler', StandardScaler()),\n",
" ('svc', SVC(C=1.2222222222222223))])\n",
"Best parameter (CV score=0.674):\n",
"Train set Accuracy: 0.7774566473988439\n",
"Jaccard similarity score: 0.7222222222222222\n",
"F1-score: 0.6212664277180406\n"
]
}
],
"source": [
"# Parameters of pipelines can be set using ‘__’ separated parameter names:\n",
"param_grid = {\n",
" 'svc__C': np.linspace(1, 3, 10),\n",
"}\n",
"\n",
"svc_pipeline = Pipeline(steps=[\n",
" ('selector', DataFrameSelector(weekend_on=4)),\n",
" ('std_scaler', StandardScaler()),\n",
" ('svc', SVC(C=0.01, kernel='rbf'))\n",
"])\n",
"\n",
"search = GridSearchCV(svc_pipeline, param_grid, cv=3, n_jobs=-1)\n",
"search.fit(df_train.copy(), y_train.copy())\n",
"\n",
"svc_pipeline = estimator = search.best_estimator_\n",
"\n",
"print(estimator)\n",
"print(\"Best parameter (CV score=%0.3f):\" % search.best_score_)\n",
"# In binary and multiclass classification, accuracy_score\n",
"# is equal to the jaccard_score function.\n",
"print(\"Train set Accuracy: \", accuracy_score(y_train, estimator.predict(df_train)))\n",
"\n",
"y_hat = estimator.predict(df_test)\n",
"print(\"Jaccard similarity score: \", jaccard_score(y_test, y_hat))\n",
"print(\"F1-score: \", f1_score(y_test, y_hat, average='weighted', labels=np.unique(y_hat)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Logistic Regression"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Best parameter (CV score=0.749):\n",
"Train set Accuracy: 0.7514450867052023\n",
"Jaccard similarity score: 0.7407407407407407\n",
"F1-score: 0.851063829787234\n",
"Log-loss: 0.5190440232613492\n"
]
}
],
"source": [
"# Parameters of pipelines can be set using ‘__’ separated parameter names:\n",
"param_grid = {\n",
" 'lr__C': np.linspace(0.01, 1, 10),\n",
" 'lr__tol': np.linspace(0.0001, 0.001, 10),\n",
"}\n",
"\n",
"lr_pipeline = Pipeline(steps=[\n",
" ('selector', DataFrameSelector(weekend_on=4)),\n",
" ('std_scaler', StandardScaler()),\n",
" ('lr', LogisticRegression(solver='lbfgs'))\n",
"])\n",
"\n",
"search = GridSearchCV(lr_pipeline, param_grid, cv=3, n_jobs=-1)\n",
"search.fit(df_train, y_train)\n",
"\n",
"lr_pipeline = estimator = search.best_estimator_\n",
"\n",
"print(\"Best parameter (CV score=%0.3f):\" % search.best_score_)\n",
"# In binary and multiclass classification, accuracy_score\n",
"# is equal to the jaccard_score function.\n",
"print(\"Train set Accuracy: \", accuracy_score(y_train, estimator.predict(df_train)))\n",
"\n",
"\n",
"y_hat = estimator.predict(df_test)\n",
"y_hat_prob = estimator.predict_proba(df_test)\n",
"print(\"Jaccard similarity score: \", jaccard_score(y_test, y_hat))\n",
"print(\"F1-score: \", f1_score(y_test, y_hat, average='weighted', labels=np.unique(y_hat)))\n",
"print(\"Log-loss: \", log_loss(y_test, y_hat_prob))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Model Evaluation using Test set"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"df_test = read_csv('./loan_test.csv')\n",
"y_test = y_encoder.transform(df_test['loan_status'].values)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## KNN"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Jaccard score: 0.71\n",
"F1-score: 0.67\n"
]
}
],
"source": [
"y_hat = knn_pipeline.predict(df_test)\n",
"print(f\"Jaccard score: {jaccard_score(y_test, y_hat):.2f}\")\n",
"print(f\"F1-score: {f1_score(y_test, y_hat, average='weighted'):.2f}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Decision Tree"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Jaccard score: 0.74\n",
"F1-score: 0.63\n"
]
}
],
"source": [
"y_hat = dt_pipeline.predict(df_test)\n",
"print(f\"Jaccard score: {jaccard_score(y_test, y_hat):.2f}\")\n",
"print(f\"F1-score: {f1_score(y_test, y_hat, average='weighted'):.2f}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Logistic Regression"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Jaccard score: 0.74\n",
"F1-score: 0.63\n",
"LogLoss: 0.52\n"
]
}
],
"source": [
"y_hat = lr_pipeline.predict(df_test)\n",
"print(f\"Jaccard score: {jaccard_score(y_test, y_hat):.2f}\")\n",
"print(f\"F1-score: {f1_score(y_test, y_hat, average='weighted'):.2f}\")\n",
"y_hat_prob = lr_pipeline.predict_proba(df_test)\n",
"print(f\"LogLoss: {log_loss(y_test, y_hat_prob):.2f}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## SVM"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Jaccard score: 0.72\n",
"F1-score: 0.62\n"
]
}
],
"source": [
"y_hat = svc_pipeline.predict(df_test)\n",
"print(f\"Jaccard score: {jaccard_score(y_test, y_hat):.2f}\")\n",
"print(f\"F1-score: {f1_score(y_test, y_hat, average='weighted'):.2f}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Report"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Jaccard</th>\n",
" <th>F1-score</th>\n",
" <th>LogLoss</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>KNN</th>\n",
" <td>0.71</td>\n",
" <td>0.67</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Decision Tree</th>\n",
" <td>0.74</td>\n",
" <td>0.63</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>SVM</th>\n",
" <td>0.72</td>\n",
" <td>0.62</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Log. reg.</th>\n",
" <td>0.74</td>\n",
" <td>0.63</td>\n",
" <td>0.52</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Jaccard F1-score LogLoss\n",
"KNN 0.71 0.67 NaN\n",
"Decision Tree 0.74 0.63 NaN\n",
"SVM 0.72 0.62 NaN\n",
"Log. reg. 0.74 0.63 0.52"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pipelines = {\n",
" 'KNN': knn_pipeline,\n",
" 'Decision Tree': dt_pipeline,\n",
" 'SVM': svc_pipeline,\n",
" 'Log. reg.': lr_pipeline\n",
"}\n",
"metrics = {\n",
" 'Jaccard': jaccard_score,\n",
" 'F1-score': f1_score,\n",
" 'LogLoss': log_loss\n",
"}\n",
"results = DataFrame(columns=list(metrics), index=list(pipelines))\n",
"\n",
"\n",
"y_hat = None\n",
"\n",
"for _p in pipelines:\n",
" y_hat = pipelines[_p].predict(df_test)\n",
" y_hat_prob = (_p == 'Log. reg.') and pipelines[_p].predict_proba(df_test)\n",
" \n",
" results.loc[_p, 'Jaccard'] = jaccard_score(y_test, y_hat)\n",
" results.loc[_p, 'F1-score'] = f1_score(y_test, y_hat, average='weighted')\n",
" results.loc[_p, 'LogLoss'] = log_loss(y_test, y_hat_prob) if _p == 'Log. reg.' else np.NaN\n",
"\n",
"results.astype(float).round(2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Results are impacted by the small number of samples while using a GridSearchCV**"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Xarray",
"language": "python",
"name": "xarray"
},
"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.8.3"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment