model-registry/notebooks/wip/Evaluating_Binary_Classification.ipynb

1362 lines
99 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"id": "-iRvitW_mOmI"
},
"outputs": [],
"source": [
"import numpy as np\n",
"import pandas as pd"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"id": "ZTL4F90RnqNA"
},
"outputs": [],
"source": [
"# https://www.statsmodels.org/stable/index.html\n",
"import statsmodels.api as sm"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"id": "fK4vZwBPnA5z"
},
"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>Record</th>\n",
" <th>Spam</th>\n",
" <th>Recipients</th>\n",
" <th>Hyperlinks</th>\n",
" <th>Characters</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>19</td>\n",
" <td>1</td>\n",
" <td>47</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2</td>\n",
" <td>0</td>\n",
" <td>15</td>\n",
" <td>1</td>\n",
" <td>58</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>3</td>\n",
" <td>1</td>\n",
" <td>13</td>\n",
" <td>11</td>\n",
" <td>88</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>4</td>\n",
" <td>1</td>\n",
" <td>17</td>\n",
" <td>11</td>\n",
" <td>68</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>5</td>\n",
" <td>0</td>\n",
" <td>15</td>\n",
" <td>1</td>\n",
" <td>87</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>495</th>\n",
" <td>496</td>\n",
" <td>0</td>\n",
" <td>15</td>\n",
" <td>2</td>\n",
" <td>97</td>\n",
" </tr>\n",
" <tr>\n",
" <th>496</th>\n",
" <td>497</td>\n",
" <td>0</td>\n",
" <td>20</td>\n",
" <td>5</td>\n",
" <td>72</td>\n",
" </tr>\n",
" <tr>\n",
" <th>497</th>\n",
" <td>498</td>\n",
" <td>1</td>\n",
" <td>41</td>\n",
" <td>11</td>\n",
" <td>52</td>\n",
" </tr>\n",
" <tr>\n",
" <th>498</th>\n",
" <td>499</td>\n",
" <td>1</td>\n",
" <td>16</td>\n",
" <td>11</td>\n",
" <td>74</td>\n",
" </tr>\n",
" <tr>\n",
" <th>499</th>\n",
" <td>500</td>\n",
" <td>1</td>\n",
" <td>13</td>\n",
" <td>2</td>\n",
" <td>32</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>500 rows × 5 columns</p>\n",
"</div>"
],
"text/plain": [
" Record Spam Recipients Hyperlinks Characters\n",
"0 1 0 19 1 47\n",
"1 2 0 15 1 58\n",
"2 3 1 13 11 88\n",
"3 4 1 17 11 68\n",
"4 5 0 15 1 87\n",
".. ... ... ... ... ...\n",
"495 496 0 15 2 97\n",
"496 497 0 20 5 72\n",
"497 498 1 41 11 52\n",
"498 499 1 16 11 74\n",
"499 500 1 13 2 32\n",
"\n",
"[500 rows x 5 columns]"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"spamDf = pd.read_excel(\"https://www.dropbox.com/scl/fi/v24mmhg5hmefmnv99uqsy/Spam.xlsx?rlkey=iq7exnueq84sy7y2b8ud70mp0&dl=1\")\n",
"spamDf"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"id": "AgPRgw9TnYLJ"
},
"outputs": [
{
"data": {
"text/plain": [
"(2500, (500, 5))"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"spamDf.size, spamDf.shape"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"id": "zqcLaMdZoasO"
},
"outputs": [],
"source": [
"from sklearn.model_selection import train_test_split"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"id": "Y_JGlYFloXHm"
},
"outputs": [
{
"data": {
"text/plain": [
"((350, 5), (150, 5))"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Split the dataset into training and testing sets\n",
"trainSet, testSet = train_test_split(\n",
" spamDf,\n",
" test_size=0.3,\n",
" random_state=1,\n",
" stratify=spamDf['Spam']\n",
")\n",
"trainSet.shape, testSet.shape"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"id": "P8pFCQgIpAu3"
},
"outputs": [],
"source": [
"# Fit the logistic regression model\n",
"features = ['Recipients', 'Hyperlinks', 'Characters']\n",
"xTrain = trainSet[features]\n",
"yTrain = trainSet['Spam'].astype(int)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"id": "6sHvxFpspMKh"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Optimization terminated successfully.\n",
" Current function value: 0.430522\n",
" Iterations 6\n",
" Logit Regression Results \n",
"==============================================================================\n",
"Dep. Variable: Spam No. Observations: 350\n",
"Model: Logit Df Residuals: 346\n",
"Method: MLE Df Model: 3\n",
"Date: Sun, 09 Jun 2024 Pseudo R-squ.: 0.3784\n",
"Time: 15:05:50 Log-Likelihood: -150.68\n",
"converged: True LL-Null: -242.40\n",
"Covariance Type: nonrobust LLR p-value: 1.606e-39\n",
"==============================================================================\n",
" coef std err z P>|z| [0.025 0.975]\n",
"------------------------------------------------------------------------------\n",
"const -4.3440 0.757 -5.741 0.000 -5.827 -2.861\n",
"Recipients 0.1071 0.035 3.083 0.002 0.039 0.175\n",
"Hyperlinks 0.5803 0.059 9.833 0.000 0.465 0.696\n",
"Characters -0.0132 0.006 -2.154 0.031 -0.025 -0.001\n",
"==============================================================================\n"
]
}
],
"source": [
"spamBasedOnRecipientsHyperlinksCharactersLogitModel = sm.Logit(\n",
" yTrain,\n",
" sm.add_constant(xTrain)\n",
")\n",
"spamBasedOnRecipientsHyperlinksCharactersLogitModelFit = spamBasedOnRecipientsHyperlinksCharactersLogitModel.fit()\n",
"print(spamBasedOnRecipientsHyperlinksCharactersLogitModelFit.summary())"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"id": "5YbGrnLcp4EK"
},
"outputs": [],
"source": [
"predict1 = spamBasedOnRecipientsHyperlinksCharactersLogitModelFit.predict(sm.add_constant(testSet[features]))\n",
"testSet['predict1'] = predict1\n",
"sumTable = pd.DataFrame({'A': testSet['Spam'], 'Prob': testSet['predict1']})\n",
"sumTable.to_csv(\"ROC.csv\", index=True)\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"id": "nnM895bnYFuU"
},
"outputs": [],
"source": [
"sumTable1 = pd.DataFrame({'A': testSet['Spam'], 'Prob': testSet['predict1']})"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"id": "N0GKRfOerVZk"
},
"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>A</th>\n",
" <th>Prob</th>\n",
" <th>P</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>186</th>\n",
" <td>0</td>\n",
" <td>0.739633</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>423</th>\n",
" <td>0</td>\n",
" <td>0.079193</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>369</th>\n",
" <td>1</td>\n",
" <td>0.712801</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>283</th>\n",
" <td>1</td>\n",
" <td>0.838428</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>266</th>\n",
" <td>1</td>\n",
" <td>0.789240</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>156</th>\n",
" <td>1</td>\n",
" <td>0.850576</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>54</th>\n",
" <td>0</td>\n",
" <td>0.180012</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>322</th>\n",
" <td>0</td>\n",
" <td>0.376942</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>314</th>\n",
" <td>0</td>\n",
" <td>0.040472</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>296</th>\n",
" <td>0</td>\n",
" <td>0.102076</td>\n",
" <td>0</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>150 rows × 3 columns</p>\n",
"</div>"
],
"text/plain": [
" A Prob P\n",
"186 0 0.739633 1\n",
"423 0 0.079193 0\n",
"369 1 0.712801 1\n",
"283 1 0.838428 1\n",
"266 1 0.789240 1\n",
".. .. ... ..\n",
"156 1 0.850576 1\n",
"54 0 0.180012 0\n",
"322 0 0.376942 0\n",
"314 0 0.040472 0\n",
"296 0 0.102076 0\n",
"\n",
"[150 rows x 3 columns]"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Make predictions based on probability threshold of 0.5\n",
"testSet['predictions'] = (testSet['predict1'] > 0.5).astype(int)\n",
"sumTable1['P'] = testSet['predictions']\n",
"sumTable1"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"id": "xlQk7hqYsHwL"
},
"outputs": [],
"source": [
"from sklearn.metrics import accuracy_score, recall_score, precision_score, roc_auc_score, roc_curve"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"id": "7FS8w-2ysIlk"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Accuracy: 0.78\n"
]
}
],
"source": [
"# Calculate accuracy\n",
"accuracy = accuracy_score(sumTable1['A'], sumTable1['P'])\n",
"spamBasedOnRecipientsHyperlinksCharactersLogitModelFit.customMetrics = {}\n",
"spamBasedOnRecipientsHyperlinksCharactersLogitModelFit.customMetrics['accuracy'] = accuracy\n",
"print(f'Accuracy: {spamBasedOnRecipientsHyperlinksCharactersLogitModelFit.customMetrics['accuracy']}')"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"id": "yuSL_r7AsYT3"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Recall: 0.7532467532467533\n"
]
}
],
"source": [
"# Calculate recall\n",
"recall = recall_score(sumTable1['A'], sumTable1['P'])\n",
"spamBasedOnRecipientsHyperlinksCharactersLogitModelFit.customMetrics['recall'] = recall\n",
"print(f'Recall: {recall}')"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"id": "NicDWx4esa9G"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Precision: 0.8055555555555556\n"
]
}
],
"source": [
"# Calculate precision\n",
"precision = precision_score(sumTable1['A'], sumTable1['P'])\n",
"spamBasedOnRecipientsHyperlinksCharactersLogitModelFit.customMetrics['precision'] = precision\n",
"print(f'Precision: {precision}')"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"id": "SgxhSyW-spz7"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Sensitivity: 0.7532467532467533\n",
"Specificity: 0.8082191780821918\n"
]
}
],
"source": [
"# Sensitivity and Specificity (Sensitivity is same as recall)\n",
"sensitivity = recall\n",
"specificity = sum((sumTable1['A'] == 0) & (sumTable1['P'] == 0)) / sum(sumTable1['A'] == 0)\n",
"spamBasedOnRecipientsHyperlinksCharactersLogitModelFit.customMetrics['sensitivity'] = sensitivity\n",
"spamBasedOnRecipientsHyperlinksCharactersLogitModelFit.customMetrics['specificity'] = specificity\n",
"print(f'Sensitivity: {sensitivity}')\n",
"print(f'Specificity: {specificity}')"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"id": "Y4Bufrh8tPIp"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"F1 Score: 0.778523489932886\n"
]
}
],
"source": [
"# Calculate F1 Score\n",
"f1Score = 2 * (precision * recall) / (precision + recall)\n",
"spamBasedOnRecipientsHyperlinksCharactersLogitModelFit.customMetrics['f1Score'] = f1Score\n",
"print(f'F1 Score: {f1Score}')"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"id": "7NS_N1R_tcf9"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"AUC: 0.8305461661626046\n"
]
}
],
"source": [
"# Plot ROC curve\n",
"fpr, tpr, _ = roc_curve(testSet['Spam'], testSet['predict1'])\n",
"roc_auc = roc_auc_score(testSet['Spam'], testSet['predict1'])\n",
"spamBasedOnRecipientsHyperlinksCharactersLogitModelFit.customMetrics['roc_auc'] = roc_auc\n",
"# Calculate AUC\n",
"print(f'AUC: {roc_auc}')\n"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {
"id": "OZLGYNGpuGWY"
},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"id": "1K-2SMbUt90Z"
},
"outputs": [
{
"data": {
"image/png": "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",
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"\n",
"plt.figure()\n",
"plt.plot(fpr, tpr, color='darkorange', lw=2, label=f'ROC curve (area = {roc_auc:.2f})')\n",
"plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')\n",
"plt.xlim([0.0, 1.0])\n",
"plt.ylim([0.0, 1.05])\n",
"plt.xlabel('False Positive Rate')\n",
"plt.ylabel('True Positive Rate')\n",
"plt.title('Receiver Operating Characteristic')\n",
"plt.legend(loc=\"lower right\")\n",
"plt.show()\n"
]
},
{
"cell_type": "code",
"execution_count": 59,
"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>Record</th>\n",
" <th>Spam</th>\n",
" <th>Recipients</th>\n",
" <th>Hyperlinks</th>\n",
" <th>Characters</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>19</td>\n",
" <td>1</td>\n",
" <td>47</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2</td>\n",
" <td>0</td>\n",
" <td>15</td>\n",
" <td>1</td>\n",
" <td>58</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>3</td>\n",
" <td>1</td>\n",
" <td>13</td>\n",
" <td>11</td>\n",
" <td>88</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>4</td>\n",
" <td>1</td>\n",
" <td>17</td>\n",
" <td>11</td>\n",
" <td>68</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>5</td>\n",
" <td>0</td>\n",
" <td>15</td>\n",
" <td>1</td>\n",
" <td>87</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Record Spam Recipients Hyperlinks Characters\n",
"0 1 0 19 1 47\n",
"1 2 0 15 1 58\n",
"2 3 1 13 11 88\n",
"3 4 1 17 11 68\n",
"4 5 0 15 1 87"
]
},
"execution_count": 59,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# KFold\n",
"from sklearn.model_selection import KFold\n",
"# Initialize KFold\n",
"# k=2\n",
"# k=5\n",
"k=10\n",
"kf = KFold(n_splits=k, shuffle=True, random_state=55)\n",
"spamDf.head()"
]
},
{
"cell_type": "code",
"execution_count": 60,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Optimization terminated successfully.\n",
" Current function value: 0.460658\n",
" Iterations 6\n",
"expr=1\n",
" Logit Regression Results \n",
"==============================================================================\n",
"Dep. Variable: Spam No. Observations: 450\n",
"Model: Logit Df Residuals: 446\n",
"Method: MLE Df Model: 3\n",
"Date: Sun, 09 Jun 2024 Pseudo R-squ.: 0.3343\n",
"Time: 15:23:34 Log-Likelihood: -207.30\n",
"converged: True LL-Null: -311.38\n",
"Covariance Type: nonrobust LLR p-value: 7.258e-45\n",
"==============================================================================\n",
" coef std err z P>|z| [0.025 0.975]\n",
"------------------------------------------------------------------------------\n",
"const -4.0452 0.691 -5.857 0.000 -5.399 -2.691\n",
"Recipients 0.1205 0.035 3.407 0.001 0.051 0.190\n",
"Hyperlinks 0.5087 0.047 10.832 0.000 0.417 0.601\n",
"Characters -0.0123 0.005 -2.405 0.016 -0.022 -0.002\n",
"==============================================================================\n",
"Optimization terminated successfully.\n",
" Current function value: 0.458531\n",
" Iterations 6\n",
"expr=2\n",
" Logit Regression Results \n",
"==============================================================================\n",
"Dep. Variable: Spam No. Observations: 450\n",
"Model: Logit Df Residuals: 446\n",
"Method: MLE Df Model: 3\n",
"Date: Sun, 09 Jun 2024 Pseudo R-squ.: 0.3383\n",
"Time: 15:23:34 Log-Likelihood: -206.34\n",
"converged: True LL-Null: -311.85\n",
"Covariance Type: nonrobust LLR p-value: 1.759e-45\n",
"==============================================================================\n",
" coef std err z P>|z| [0.025 0.975]\n",
"------------------------------------------------------------------------------\n",
"const -3.7954 0.651 -5.835 0.000 -5.070 -2.520\n",
"Recipients 0.0955 0.031 3.036 0.002 0.034 0.157\n",
"Hyperlinks 0.5174 0.047 10.961 0.000 0.425 0.610\n",
"Characters -0.0126 0.005 -2.451 0.014 -0.023 -0.003\n",
"==============================================================================\n",
"Optimization terminated successfully.\n",
" Current function value: 0.459673\n",
" Iterations 6\n",
"expr=3\n",
" Logit Regression Results \n",
"==============================================================================\n",
"Dep. Variable: Spam No. Observations: 450\n",
"Model: Logit Df Residuals: 446\n",
"Method: MLE Df Model: 3\n",
"Date: Sun, 09 Jun 2024 Pseudo R-squ.: 0.3365\n",
"Time: 15:23:34 Log-Likelihood: -206.85\n",
"converged: True LL-Null: -311.76\n",
"Covariance Type: nonrobust LLR p-value: 3.205e-45\n",
"==============================================================================\n",
" coef std err z P>|z| [0.025 0.975]\n",
"------------------------------------------------------------------------------\n",
"const -3.5764 0.692 -5.172 0.000 -4.932 -2.221\n",
"Recipients 0.0957 0.037 2.578 0.010 0.023 0.169\n",
"Hyperlinks 0.5080 0.047 10.921 0.000 0.417 0.599\n",
"Characters -0.0153 0.005 -2.954 0.003 -0.025 -0.005\n",
"==============================================================================\n",
"Optimization terminated successfully.\n",
" Current function value: 0.452198\n",
" Iterations 6\n",
"expr=4\n",
" Logit Regression Results \n",
"==============================================================================\n",
"Dep. Variable: Spam No. Observations: 450\n",
"Model: Logit Df Residuals: 446\n",
"Method: MLE Df Model: 3\n",
"Date: Sun, 09 Jun 2024 Pseudo R-squ.: 0.3476\n",
"Time: 15:23:34 Log-Likelihood: -203.49\n",
"converged: True LL-Null: -311.92\n",
"Covariance Type: nonrobust LLR p-value: 9.609e-47\n",
"==============================================================================\n",
" coef std err z P>|z| [0.025 0.975]\n",
"------------------------------------------------------------------------------\n",
"const -4.0845 0.673 -6.068 0.000 -5.404 -2.765\n",
"Recipients 0.1071 0.034 3.176 0.001 0.041 0.173\n",
"Hyperlinks 0.5174 0.047 10.997 0.000 0.425 0.610\n",
"Characters -0.0112 0.005 -2.152 0.031 -0.021 -0.001\n",
"==============================================================================\n",
"Optimization terminated successfully.\n",
" Current function value: 0.456077\n",
" Iterations 6\n",
"expr=5\n",
" Logit Regression Results \n",
"==============================================================================\n",
"Dep. Variable: Spam No. Observations: 450\n",
"Model: Logit Df Residuals: 446\n",
"Method: MLE Df Model: 3\n",
"Date: Sun, 09 Jun 2024 Pseudo R-squ.: 0.3407\n",
"Time: 15:23:34 Log-Likelihood: -205.23\n",
"converged: True LL-Null: -311.28\n",
"Covariance Type: nonrobust LLR p-value: 1.033e-45\n",
"==============================================================================\n",
" coef std err z P>|z| [0.025 0.975]\n",
"------------------------------------------------------------------------------\n",
"const -3.7435 0.657 -5.695 0.000 -5.032 -2.455\n",
"Recipients 0.1010 0.033 3.026 0.002 0.036 0.166\n",
"Hyperlinks 0.5043 0.046 11.030 0.000 0.415 0.594\n",
"Characters -0.0124 0.005 -2.361 0.018 -0.023 -0.002\n",
"==============================================================================\n",
"Optimization terminated successfully.\n",
" Current function value: 0.449368\n",
" Iterations 6\n",
"expr=6\n",
" Logit Regression Results \n",
"==============================================================================\n",
"Dep. Variable: Spam No. Observations: 450\n",
"Model: Logit Df Residuals: 446\n",
"Method: MLE Df Model: 3\n",
"Date: Sun, 09 Jun 2024 Pseudo R-squ.: 0.3512\n",
"Time: 15:23:34 Log-Likelihood: -202.22\n",
"converged: True LL-Null: -311.70\n",
"Covariance Type: nonrobust LLR p-value: 3.360e-47\n",
"==============================================================================\n",
" coef std err z P>|z| [0.025 0.975]\n",
"------------------------------------------------------------------------------\n",
"const -3.9098 0.694 -5.635 0.000 -5.270 -2.550\n",
"Recipients 0.1211 0.036 3.363 0.001 0.051 0.192\n",
"Hyperlinks 0.5235 0.048 10.982 0.000 0.430 0.617\n",
"Characters -0.0172 0.005 -3.297 0.001 -0.028 -0.007\n",
"==============================================================================\n",
"Optimization terminated successfully.\n",
" Current function value: 0.455797\n",
" Iterations 6\n",
"expr=7\n",
" Logit Regression Results \n",
"==============================================================================\n",
"Dep. Variable: Spam No. Observations: 450\n",
"Model: Logit Df Residuals: 446\n",
"Method: MLE Df Model: 3\n",
"Date: Sun, 09 Jun 2024 Pseudo R-squ.: 0.3415\n",
"Time: 15:23:34 Log-Likelihood: -205.11\n",
"converged: True LL-Null: -311.47\n",
"Covariance Type: nonrobust LLR p-value: 7.501e-46\n",
"==============================================================================\n",
" coef std err z P>|z| [0.025 0.975]\n",
"------------------------------------------------------------------------------\n",
"const -3.7370 0.671 -5.573 0.000 -5.051 -2.423\n",
"Recipients 0.1079 0.034 3.193 0.001 0.042 0.174\n",
"Hyperlinks 0.5177 0.047 10.908 0.000 0.425 0.611\n",
"Characters -0.0153 0.005 -2.941 0.003 -0.026 -0.005\n",
"==============================================================================\n",
"Optimization terminated successfully.\n",
" Current function value: 0.445224\n",
" Iterations 6\n",
"expr=8\n",
" Logit Regression Results \n",
"==============================================================================\n",
"Dep. Variable: Spam No. Observations: 450\n",
"Model: Logit Df Residuals: 446\n",
"Method: MLE Df Model: 3\n",
"Date: Sun, 09 Jun 2024 Pseudo R-squ.: 0.3572\n",
"Time: 15:23:34 Log-Likelihood: -200.35\n",
"converged: True LL-Null: -311.70\n",
"Covariance Type: nonrobust LLR p-value: 5.249e-48\n",
"==============================================================================\n",
" coef std err z P>|z| [0.025 0.975]\n",
"------------------------------------------------------------------------------\n",
"const -3.7848 0.667 -5.677 0.000 -5.092 -2.478\n",
"Recipients 0.1079 0.033 3.243 0.001 0.043 0.173\n",
"Hyperlinks 0.5328 0.048 11.053 0.000 0.438 0.627\n",
"Characters -0.0162 0.005 -3.070 0.002 -0.027 -0.006\n",
"==============================================================================\n",
"Optimization terminated successfully.\n",
" Current function value: 0.461358\n",
" Iterations 6\n",
"expr=9\n",
" Logit Regression Results \n",
"==============================================================================\n",
"Dep. Variable: Spam No. Observations: 450\n",
"Model: Logit Df Residuals: 446\n",
"Method: MLE Df Model: 3\n",
"Date: Sun, 09 Jun 2024 Pseudo R-squ.: 0.3338\n",
"Time: 15:23:34 Log-Likelihood: -207.61\n",
"converged: True LL-Null: -311.63\n",
"Covariance Type: nonrobust LLR p-value: 7.718e-45\n",
"==============================================================================\n",
" coef std err z P>|z| [0.025 0.975]\n",
"------------------------------------------------------------------------------\n",
"const -3.7859 0.659 -5.741 0.000 -5.078 -2.493\n",
"Recipients 0.1045 0.032 3.224 0.001 0.041 0.168\n",
"Hyperlinks 0.5039 0.046 10.879 0.000 0.413 0.595\n",
"Characters -0.0133 0.005 -2.574 0.010 -0.023 -0.003\n",
"==============================================================================\n",
"Optimization terminated successfully.\n",
" Current function value: 0.467026\n",
" Iterations 6\n",
"expr=10\n",
" Logit Regression Results \n",
"==============================================================================\n",
"Dep. Variable: Spam No. Observations: 450\n",
"Model: Logit Df Residuals: 446\n",
"Method: MLE Df Model: 3\n",
"Date: Sun, 09 Jun 2024 Pseudo R-squ.: 0.3258\n",
"Time: 15:23:34 Log-Likelihood: -210.16\n",
"converged: True LL-Null: -311.70\n",
"Covariance Type: nonrobust LLR p-value: 9.141e-44\n",
"==============================================================================\n",
" coef std err z P>|z| [0.025 0.975]\n",
"------------------------------------------------------------------------------\n",
"const -3.8264 0.669 -5.716 0.000 -5.138 -2.514\n",
"Recipients 0.1148 0.035 3.319 0.001 0.047 0.183\n",
"Hyperlinks 0.5033 0.047 10.821 0.000 0.412 0.594\n",
"Characters -0.0151 0.005 -2.921 0.003 -0.025 -0.005\n",
"==============================================================================\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:22: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['val_predictions'] = val_predictions\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:23: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['yHatCross'] = valSet['val_predictions'].apply(lambda x: 1 if x > 0.5 else 0)\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:24: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['isCrossCorrect'] = valSet.apply(lambda row: 1 if row['Spam'] == row['yHatCross'] else 0, axis=1)\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:22: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['val_predictions'] = val_predictions\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:23: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['yHatCross'] = valSet['val_predictions'].apply(lambda x: 1 if x > 0.5 else 0)\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:24: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['isCrossCorrect'] = valSet.apply(lambda row: 1 if row['Spam'] == row['yHatCross'] else 0, axis=1)\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:22: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['val_predictions'] = val_predictions\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:23: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['yHatCross'] = valSet['val_predictions'].apply(lambda x: 1 if x > 0.5 else 0)\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:24: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['isCrossCorrect'] = valSet.apply(lambda row: 1 if row['Spam'] == row['yHatCross'] else 0, axis=1)\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:22: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['val_predictions'] = val_predictions\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:23: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['yHatCross'] = valSet['val_predictions'].apply(lambda x: 1 if x > 0.5 else 0)\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:24: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['isCrossCorrect'] = valSet.apply(lambda row: 1 if row['Spam'] == row['yHatCross'] else 0, axis=1)\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:22: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['val_predictions'] = val_predictions\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:23: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['yHatCross'] = valSet['val_predictions'].apply(lambda x: 1 if x > 0.5 else 0)\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:24: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['isCrossCorrect'] = valSet.apply(lambda row: 1 if row['Spam'] == row['yHatCross'] else 0, axis=1)\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:22: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['val_predictions'] = val_predictions\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:23: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['yHatCross'] = valSet['val_predictions'].apply(lambda x: 1 if x > 0.5 else 0)\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:24: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['isCrossCorrect'] = valSet.apply(lambda row: 1 if row['Spam'] == row['yHatCross'] else 0, axis=1)\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:22: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['val_predictions'] = val_predictions\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:23: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['yHatCross'] = valSet['val_predictions'].apply(lambda x: 1 if x > 0.5 else 0)\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:24: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['isCrossCorrect'] = valSet.apply(lambda row: 1 if row['Spam'] == row['yHatCross'] else 0, axis=1)\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:22: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['val_predictions'] = val_predictions\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:23: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['yHatCross'] = valSet['val_predictions'].apply(lambda x: 1 if x > 0.5 else 0)\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:24: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['isCrossCorrect'] = valSet.apply(lambda row: 1 if row['Spam'] == row['yHatCross'] else 0, axis=1)\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:22: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['val_predictions'] = val_predictions\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:23: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['yHatCross'] = valSet['val_predictions'].apply(lambda x: 1 if x > 0.5 else 0)\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:24: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['isCrossCorrect'] = valSet.apply(lambda row: 1 if row['Spam'] == row['yHatCross'] else 0, axis=1)\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:22: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['val_predictions'] = val_predictions\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:23: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['yHatCross'] = valSet['val_predictions'].apply(lambda x: 1 if x > 0.5 else 0)\n",
"/var/folders/v4/9b_k_xyj56ggnxlhf09pt8y40000gn/T/ipykernel_38981/1702950247.py:24: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" valSet['isCrossCorrect'] = valSet.apply(lambda row: 1 if row['Spam'] == row['yHatCross'] else 0, axis=1)\n"
]
}
],
"source": [
"check = kf.split(spamDf)\n",
"check\n",
"experiment = 1\n",
"# Loop through each fold\n",
"# Initialize variables to store results\n",
"accuracies = []\n",
"\n",
"for train_index, val_index in check:\n",
" # Split the data\n",
" trainSet, valSet = spamDf.iloc[train_index], spamDf.iloc[val_index]\n",
"\n",
" # Fit the model\n",
"\n",
" trainModel = sm.Logit(\n",
" trainSet[\"Spam\"],\n",
" sm.add_constant(trainSet[['Recipients', 'Hyperlinks', 'Characters']])\n",
" )\n",
" trainModelFit = trainModel.fit()\n",
"\n",
" # Predict on the validation set\n",
" val_predictions = trainModelFit.predict(sm.add_constant(valSet[['Recipients', 'Hyperlinks', 'Characters']]))\n",
" valSet['val_predictions'] = val_predictions\n",
" valSet['yHatCross'] = valSet['val_predictions'].apply(lambda x: 1 if x > 0.5 else 0)\n",
" valSet['isCrossCorrect'] = valSet.apply(lambda row: 1 if row['Spam'] == row['yHatCross'] else 0, axis=1)\n",
" accuracy = (np.sum(valSet['isCrossCorrect']) / len(valSet['yHatCross'])) * 100\n",
" accuracies.append(accuracy)\n",
"\n",
"\n",
" # Print summary for each fold (optional)\n",
" print(f'expr={experiment}')\n",
" experiment = experiment +1\n",
" print(trainModelFit.summary())"
]
},
{
"cell_type": "code",
"execution_count": 61,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Average accuracies across all folds: 78.8\n"
]
},
{
"data": {
"text/plain": [
"([82.0, 78.0, 82.0, 74.0, 80.0, 78.0, 78.0, 68.0, 80.0, 88.0], None)"
]
},
"execution_count": 61,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"accuracies, print(f\"Average accuracies across all folds: {sum(accuracies) /len(accuracies)}\")"
]
},
{
"cell_type": "code",
"execution_count": 62,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'accuracy': 0.78,\n",
" 'recall': 0.7532467532467533,\n",
" 'precision': 0.8055555555555556,\n",
" 'sensitivity': 0.7532467532467533,\n",
" 'specificity': 0.8082191780821918,\n",
" 'f1Score': 0.778523489932886,\n",
" 'roc_auc': 0.8305461661626046,\n",
" 'k-fold5': {'k': 5,\n",
" 'accuracies': [80.0, 78.0, 80.0, 73.0, 84.0],\n",
" 'accuracyAvg': 79.0},\n",
" 'k-fold10': {'k': 10,\n",
" 'accuracies': [82.0, 78.0, 82.0, 74.0, 80.0, 78.0, 78.0, 68.0, 80.0, 88.0],\n",
" 'accuracyAvg': 78.8},\n",
" 'k-fold2': {'k': 2, 'accuracies': [76.0, 78.8], 'accuracyAvg': 77.4}}"
]
},
"execution_count": 62,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"spamBasedOnRecipientsHyperlinksCharactersLogitModelFit.customMetrics[f'k-fold{k}'] = {\n",
" \"k\": k,\n",
" \"accuracies\": accuracies,\n",
" \"accuracyAvg\": sum(accuracies) /len(accuracies)\n",
" \n",
"}\n",
"spamBasedOnRecipientsHyperlinksCharactersLogitModelFit.customMetrics"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 63,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<statsmodels.discrete.discrete_model.BinaryResultsWrapper at 0x3689745f0>"
]
},
"execution_count": 63,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from functions.exportModel import exportModel\n",
"exportModel({\n",
" \"modelName\": \"spamBasedOnRecipientsHyperlinksCharactersLogitModelFit\",\n",
" \"model\": spamBasedOnRecipientsHyperlinksCharactersLogitModelFit,\n",
" \"description\": \"spamDf Logit with hold out\",\n",
" \"modelType\": \"sm.Logit\",\n",
" \"baseRelativePath\": \"..\",\n",
" \"inputs\": [\n",
" {\n",
" \"name\": \"const\",\n",
" \"type\": \"int\"\n",
" },\n",
" {\n",
" \"name\": \"Recipients\",\n",
" \"type\": \"int\"\n",
" },\n",
" {\n",
" \"name\": \"Hyperlinks\",\n",
" \"type\": \"int\"\n",
" },\n",
" {\n",
" \"name\": \"Characters\",\n",
" \"type\": \"int\"\n",
" }\n",
" ],\n",
" \"output\": {\n",
" \"name\": \"Spam_probibility\",\n",
" \"type\": \"float\"\n",
" }\n",
"})"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"colab": {
"provenance": []
},
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.12.3"
}
},
"nbformat": 4,
"nbformat_minor": 4
}