diff --git a/Cleaning.ipynb b/Cleaning.ipynb
new file mode 100644
index 0000000..b86592a
--- /dev/null
+++ b/Cleaning.ipynb
@@ -0,0 +1,269 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "c95fbd16-09ed-497b-892a-473496150996",
+ "metadata": {},
+ "source": [
+ "
Cleaning
\n",
+ "Import dataset using the ucirepo package
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "3eb339fa-ef85-4544-9ad0-bc22d4de9f1a",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " age | \n",
+ " sex | \n",
+ " cp | \n",
+ " trestbps | \n",
+ " chol | \n",
+ " fbs | \n",
+ " restecg | \n",
+ " thalach | \n",
+ " exang | \n",
+ " oldpeak | \n",
+ " slope | \n",
+ " ca | \n",
+ " thal | \n",
+ " goal | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " 0 | \n",
+ " 63 | \n",
+ " 1 | \n",
+ " 1 | \n",
+ " 145 | \n",
+ " 233 | \n",
+ " 1 | \n",
+ " 2 | \n",
+ " 150 | \n",
+ " 0 | \n",
+ " 2.3 | \n",
+ " 3 | \n",
+ " 0.0 | \n",
+ " 6.0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 1 | \n",
+ " 67 | \n",
+ " 1 | \n",
+ " 4 | \n",
+ " 160 | \n",
+ " 286 | \n",
+ " 0 | \n",
+ " 2 | \n",
+ " 108 | \n",
+ " 1 | \n",
+ " 1.5 | \n",
+ " 2 | \n",
+ " 3.0 | \n",
+ " 3.0 | \n",
+ " 2 | \n",
+ "
\n",
+ " \n",
+ " 2 | \n",
+ " 67 | \n",
+ " 1 | \n",
+ " 4 | \n",
+ " 120 | \n",
+ " 229 | \n",
+ " 0 | \n",
+ " 2 | \n",
+ " 129 | \n",
+ " 1 | \n",
+ " 2.6 | \n",
+ " 2 | \n",
+ " 2.0 | \n",
+ " 7.0 | \n",
+ " 1 | \n",
+ "
\n",
+ " \n",
+ " 3 | \n",
+ " 37 | \n",
+ " 1 | \n",
+ " 3 | \n",
+ " 130 | \n",
+ " 250 | \n",
+ " 0 | \n",
+ " 0 | \n",
+ " 187 | \n",
+ " 0 | \n",
+ " 3.5 | \n",
+ " 3 | \n",
+ " 0.0 | \n",
+ " 3.0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " 4 | \n",
+ " 41 | \n",
+ " 0 | \n",
+ " 2 | \n",
+ " 130 | \n",
+ " 204 | \n",
+ " 0 | \n",
+ " 2 | \n",
+ " 172 | \n",
+ " 0 | \n",
+ " 1.4 | \n",
+ " 1 | \n",
+ " 0.0 | \n",
+ " 3.0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " age sex cp trestbps chol fbs restecg thalach exang oldpeak slope \\\n",
+ "0 63 1 1 145 233 1 2 150 0 2.3 3 \n",
+ "1 67 1 4 160 286 0 2 108 1 1.5 2 \n",
+ "2 67 1 4 120 229 0 2 129 1 2.6 2 \n",
+ "3 37 1 3 130 250 0 0 187 0 3.5 3 \n",
+ "4 41 0 2 130 204 0 2 172 0 1.4 1 \n",
+ "\n",
+ " ca thal goal \n",
+ "0 0.0 6.0 0 \n",
+ "1 3.0 3.0 2 \n",
+ "2 2.0 7.0 1 \n",
+ "3 0.0 3.0 0 \n",
+ "4 0.0 3.0 0 "
+ ]
+ },
+ "execution_count": 1,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "from ucimlrepo import fetch_ucirepo\n",
+ "import pandas as pd\n",
+ "\n",
+ "# fetch dataset \n",
+ "heart_disease = fetch_ucirepo(id=45) \n",
+ " \n",
+ "# data (as pandas dataframes) \n",
+ "X = heart_disease.data.features \n",
+ "y = heart_disease.data.targets \n",
+ "\n",
+ "df = pd.concat([X, y], axis=1)\n",
+ "df = df.rename(columns={'num':'goal'})\n",
+ "\n",
+ "df.head()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "8c5ab8b9-e46a-4968-b0c8-fe393f093f73",
+ "metadata": {},
+ "source": [
+ "Get overview of all missing values. As there are only a few, those rows can be dropped.
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "6f7e6a3a-63cb-40e2-8746-937c24b184ef",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "age 0\n",
+ "sex 0\n",
+ "cp 0\n",
+ "trestbps 0\n",
+ "chol 0\n",
+ "fbs 0\n",
+ "restecg 0\n",
+ "thalach 0\n",
+ "exang 0\n",
+ "oldpeak 0\n",
+ "slope 0\n",
+ "ca 4\n",
+ "thal 2\n",
+ "goal 0\n",
+ "dtype: int64"
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df.isna().sum()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "d1639e92-d401-49fb-a1f1-67250ffa2c81",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df.dropna(inplace=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "d7bf2c46-7885-4dfe-a4e7-8b8439cf0434",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# save 'cleaned' dataset as csv file for further processing\n",
+ "df.to_csv('./data/dataset_cleaned.csv', index=False)"
+ ]
+ }
+ ],
+ "metadata": {
+ "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.11.7"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/Experiments.ipynb b/Experiments.ipynb
index 87d8d45..9cff778 100644
--- a/Experiments.ipynb
+++ b/Experiments.ipynb
@@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "code",
- "execution_count": 2,
+ "execution_count": 1,
"id": "initial_id",
"metadata": {
"jupyter": {
@@ -12,12 +12,14 @@
"outputs": [],
"source": [
"import pandas as pd\n",
- "from sklearn.preprocessing import MinMaxScaler, StandardScaler"
+ "from sklearn.preprocessing import MinMaxScaler, StandardScaler\n",
+ "from sklearn.model_selection import KFold\n",
+ "from sklearn import decomposition"
]
},
{
"cell_type": "code",
- "execution_count": 21,
+ "execution_count": 2,
"id": "67503952-9074-4cdb-9d7e-d9142f7c319c",
"metadata": {},
"outputs": [
@@ -216,14 +218,13 @@
"[5 rows x 28 columns]"
]
},
- "execution_count": 21,
+ "execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df = pd.read_csv('./data/dataset_cleaned.csv')\n",
- "df.dropna(inplace=True)\n",
"\n",
"# extract all columns except 'goal' --> X\n",
"X = df.loc[:, df.columns != 'goal']\n",
@@ -252,7 +253,7 @@
},
{
"cell_type": "code",
- "execution_count": 18,
+ "execution_count": 3,
"id": "2bbee865-c000-43da-84d9-ce7e04874110",
"metadata": {},
"outputs": [],
@@ -271,7 +272,7 @@
},
{
"cell_type": "code",
- "execution_count": 41,
+ "execution_count": 4,
"id": "38eb4f87-ca3c-4ecf-a8ca-29422822d933",
"metadata": {},
"outputs": [
@@ -279,57 +280,50 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "Training fold 0 for 20 epochs\n",
- "Train samples:\t267\n",
- "Test samples:\t30\n",
- "Accuracy of fold 0: 0.9\n",
- "Training fold 1 for 20 epochs\n",
- "Train samples:\t267\n",
- "Test samples:\t30\n",
- "Accuracy of fold 1: 0.8666666666666667\n",
- "Training fold 2 for 20 epochs\n",
- "Train samples:\t267\n",
- "Test samples:\t30\n",
- "Accuracy of fold 2: 0.8666666666666667\n",
- "Training fold 3 for 20 epochs\n",
- "Train samples:\t267\n",
- "Test samples:\t30\n",
- "Accuracy of fold 3: 0.9\n",
- "Training fold 4 for 20 epochs\n",
- "Train samples:\t267\n",
- "Test samples:\t30\n",
- "Accuracy of fold 4: 0.9\n",
- "Training fold 5 for 20 epochs\n",
- "Train samples:\t267\n",
- "Test samples:\t30\n",
- "Accuracy of fold 5: 0.8333333333333334\n",
- "Training fold 6 for 20 epochs\n",
- "Train samples:\t267\n",
- "Test samples:\t30\n",
- "Accuracy of fold 6: 0.7666666666666667\n",
- "Training fold 7 for 20 epochs\n",
- "Train samples:\t268\n",
- "Test samples:\t29\n",
- "Accuracy of fold 7: 0.8275862068965517\n",
- "Training fold 8 for 20 epochs\n",
- "Train samples:\t268\n",
- "Test samples:\t29\n",
- "Accuracy of fold 8: 0.7586206896551724\n",
- "Training fold 9 for 20 epochs\n",
- "Train samples:\t268\n",
- "Test samples:\t29\n",
- "Accuracy of fold 9: 0.7586206896551724\n",
- "Avg accuracy 0.837816091954023\n"
+ "Training 10 folds for 20 epochs\n",
+ "Fold 0\n",
+ "\tTrain samples:\t267\tTest samples:\t30\n",
+ "\tAccuracy: 90.000%\n",
+ "Fold 1\n",
+ "\tTrain samples:\t267\tTest samples:\t30\n",
+ "\tAccuracy: 80.000%\n",
+ "Fold 2\n",
+ "\tTrain samples:\t267\tTest samples:\t30\n",
+ "\tAccuracy: 90.000%\n",
+ "Fold 3\n",
+ "\tTrain samples:\t267\tTest samples:\t30\n",
+ "\tAccuracy: 90.000%\n",
+ "Fold 4\n",
+ "\tTrain samples:\t267\tTest samples:\t30\n",
+ "WARNING:tensorflow:5 out of the last 5 calls to .one_step_on_data_distributed at 0x0000023D0BD63C40> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has reduce_retracing=True option that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/guide/function#controlling_retracing and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
+ "\tAccuracy: 90.000%\n",
+ "Fold 5\n",
+ "\tTrain samples:\t267\tTest samples:\t30\n",
+ "WARNING:tensorflow:6 out of the last 6 calls to .one_step_on_data_distributed at 0x0000023D0D548CC0> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has reduce_retracing=True option that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/guide/function#controlling_retracing and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
+ "\tAccuracy: 86.667%\n",
+ "Fold 6\n",
+ "\tTrain samples:\t267\tTest samples:\t30\n",
+ "\tAccuracy: 80.000%\n",
+ "Fold 7\n",
+ "\tTrain samples:\t268\tTest samples:\t29\n",
+ "\tAccuracy: 86.207%\n",
+ "Fold 8\n",
+ "\tTrain samples:\t268\tTest samples:\t29\n",
+ "\tAccuracy: 79.310%\n",
+ "Fold 9\n",
+ "\tTrain samples:\t268\tTest samples:\t29\n",
+ "\tAccuracy: 82.759%\n",
+ "Avg accuracy 85.494%\n"
]
}
],
"source": [
- "from sklearn.model_selection import KFold\n",
- "from sklearn import decomposition\n",
"import tensorflow as tf\n",
"\n",
+ "use_pca = True\n",
"# number of components extracted from the pca\n",
"n_features = 8\n",
+ "n_features = n_features if use_pca else len(X.columns)\n",
"\n",
"epochs = 20\n",
"k_folds = 10\n",
@@ -338,43 +332,47 @@
"kf = KFold(n_splits=k_folds)\n",
"\n",
"accuracies = []\n",
+ "print(f'Training {k_folds} folds for {epochs} epochs')\n",
"for i, (train_idx, test_idx) in enumerate(kf.split(X)):\n",
- " print(f'Training fold {i} for {epochs} epochs')\n",
"\n",
+ " print(f'Fold {i}')\n",
+ " \n",
" # extract train and test data from the cleaned dataset\n",
" X_train, X_test = X.iloc[train_idx], X.iloc[test_idx]\n",
" y_train, y_test = y[train_idx], y[test_idx]\n",
"\n",
- " print(f'Train samples:\\t{len(X_train)}')\n",
- " print(f'Test samples:\\t{len(X_test)}')\n",
+ " print(f'\\tTrain samples:\\t{len(X_train)}\\tTest samples:\\t{len(X_test)}')\n",
"\n",
- " # do pca based on the train data of the given fold to extract 'n_features'\n",
- " pca = decomposition.PCA(n_components=n_features)\n",
- " pca.fit(X_train)\n",
- " X_train = pca.transform(X_train)\n",
+ " if use_pca:\n",
+ " # do pca based on the train data of the given fold to extract 'n_features'\n",
+ " pca = decomposition.PCA(n_components=n_features)\n",
+ " pca.fit(X_train)\n",
+ " X_train = pca.transform(X_train)\n",
"\n",
" # train the model using the components extracted from pca\n",
" model = get_model(n_features)\n",
" model.fit(X_train, y_train, epochs=epochs, verbose=0)\n",
"\n",
- " # transform test data using on the pca model trained on the train data\n",
- " X_test = pca.transform(X_test)\n",
+ " if use_pca:\n",
+ " # transform test data using on the pca model trained on the train data\n",
+ " X_test = pca.transform(X_test)\n",
+ " \n",
" y_pred = model.predict(X_test, verbose=0)\n",
- " y_pred = y_pred > 0.5\n",
+ " y_pred = y_pred > 0.5 # threshold to binarize\n",
"\n",
" # calculate the accuracy of the train data for the current fold\n",
" accuracy = sum(y_pred == y_test)[0] / len(y_pred)\n",
" accuracies.append(accuracy)\n",
- " print(f'Accuracy of fold {i}: {accuracy}')\n",
+ " print(f'\\tAccuracy: {accuracy:.3%}')\n",
"\n",
"# calculate the average accuracy over all folds\n",
"avg_accuracy = sum(accuracies) / len(accuracies)\n",
- "print(f'Avg accuracy {avg_accuracy}')"
+ "print(f'Avg accuracy {avg_accuracy:.3%}')"
]
},
{
"cell_type": "code",
- "execution_count": 42,
+ "execution_count": 5,
"id": "95215693-47c9-4202-92f5-efbc65bc32c9",
"metadata": {},
"outputs": [
@@ -382,17 +380,15 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "Training fold 0 for 20 epochs\n",
- "Train samples:\t237\n",
- "Test samples:\t60\n"
+ "Training 5 folds\n",
+ "Fold 0\n",
+ "\tTrain samples:\t237\tTest samples:\t60\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
- "C:\\Users\\maxwi\\anaconda3\\Lib\\site-packages\\sklearn\\cluster\\_kmeans.py:870: FutureWarning: The default value of `n_init` will change from 10 to 'auto' in 1.4. Set the value of `n_init` explicitly to suppress the warning\n",
- " warnings.warn(\n",
"C:\\Users\\maxwi\\anaconda3\\Lib\\site-packages\\sklearn\\cluster\\_kmeans.py:1382: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=1.\n",
" warnings.warn(\n"
]
@@ -401,20 +397,16 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "[0 1 1 0 0 0 0 0 1 1 1 0 1 0 0 0 0 0 0 0 1 0 0 1 1 0 0 0 0 1 0 1 0 1 0 0 1\n",
- " 1 1 1 1 1 0 0 0 1 0 1 0 0 0 1 0 0 1 1 1 1 0 1]\n",
- "Accuracy of fold 0: 0.5833333333333334\n",
- "Training fold 1 for 20 epochs\n",
- "Train samples:\t237\n",
- "Test samples:\t60\n"
+ "\tAccuracy 58.333%\n",
+ "\n",
+ "Fold 1\n",
+ "\tTrain samples:\t237\tTest samples:\t60\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
- "C:\\Users\\maxwi\\anaconda3\\Lib\\site-packages\\sklearn\\cluster\\_kmeans.py:870: FutureWarning: The default value of `n_init` will change from 10 to 'auto' in 1.4. Set the value of `n_init` explicitly to suppress the warning\n",
- " warnings.warn(\n",
"C:\\Users\\maxwi\\anaconda3\\Lib\\site-packages\\sklearn\\cluster\\_kmeans.py:1382: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=1.\n",
" warnings.warn(\n"
]
@@ -423,20 +415,16 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "[1 0 1 0 1 1 0 0 1 0 0 1 1 1 0 0 1 0 0 1 1 0 0 1 0 0 0 0 0 1 1 1 0 0 1 1 1\n",
- " 0 0 0 0 0 0 1 0 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1]\n",
- "Accuracy of fold 1: 0.5\n",
- "Training fold 2 for 20 epochs\n",
- "Train samples:\t238\n",
- "Test samples:\t59\n"
+ "\tAccuracy 50.000%\n",
+ "\n",
+ "Fold 2\n",
+ "\tTrain samples:\t238\tTest samples:\t59\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
- "C:\\Users\\maxwi\\anaconda3\\Lib\\site-packages\\sklearn\\cluster\\_kmeans.py:870: FutureWarning: The default value of `n_init` will change from 10 to 'auto' in 1.4. Set the value of `n_init` explicitly to suppress the warning\n",
- " warnings.warn(\n",
"C:\\Users\\maxwi\\anaconda3\\Lib\\site-packages\\sklearn\\cluster\\_kmeans.py:1382: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=1.\n",
" warnings.warn(\n"
]
@@ -445,20 +433,16 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "[0 0 0 0 1 0 0 1 1 0 0 1 0 1 1 0 0 0 1 1 0 1 0 0 1 0 1 1 1 0 1 1 0 0 0 0 0\n",
- " 0 1 1 0 1 1 1 0 1 0 1 0 0 0 1 0 0 0 0 1 1 0]\n",
- "Accuracy of fold 2: 0.559322033898305\n",
- "Training fold 3 for 20 epochs\n",
- "Train samples:\t238\n",
- "Test samples:\t59\n"
+ "\tAccuracy 55.932%\n",
+ "\n",
+ "Fold 3\n",
+ "\tTrain samples:\t238\tTest samples:\t59\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
- "C:\\Users\\maxwi\\anaconda3\\Lib\\site-packages\\sklearn\\cluster\\_kmeans.py:870: FutureWarning: The default value of `n_init` will change from 10 to 'auto' in 1.4. Set the value of `n_init` explicitly to suppress the warning\n",
- " warnings.warn(\n",
"C:\\Users\\maxwi\\anaconda3\\Lib\\site-packages\\sklearn\\cluster\\_kmeans.py:1382: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=1.\n",
" warnings.warn(\n"
]
@@ -467,20 +451,16 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "[0 1 0 1 1 1 0 0 0 1 0 1 1 0 1 0 1 1 1 1 0 1 0 0 0 0 1 1 1 0 1 0 1 0 1 0 1\n",
- " 1 1 1 1 0 0 1 1 1 0 0 1 0 1 1 1 0 0 0 1 1 1]\n",
- "Accuracy of fold 3: 0.576271186440678\n",
- "Training fold 4 for 20 epochs\n",
- "Train samples:\t238\n",
- "Test samples:\t59\n"
+ "\tAccuracy 57.627%\n",
+ "\n",
+ "Fold 4\n",
+ "\tTrain samples:\t238\tTest samples:\t59\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
- "C:\\Users\\maxwi\\anaconda3\\Lib\\site-packages\\sklearn\\cluster\\_kmeans.py:870: FutureWarning: The default value of `n_init` will change from 10 to 'auto' in 1.4. Set the value of `n_init` explicitly to suppress the warning\n",
- " warnings.warn(\n",
"C:\\Users\\maxwi\\anaconda3\\Lib\\site-packages\\sklearn\\cluster\\_kmeans.py:1382: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=1.\n",
" warnings.warn(\n"
]
@@ -489,16 +469,16 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "[1 1 1 1 1 0 0 0 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 0 1 0 1 1 0 1 1 1\n",
- " 1 0 1 0 1 0 0 0 1 1 0 1 0 0 0 1 0 0 0 0 0 1]\n",
- "Accuracy of fold 4: 0.5254237288135594\n",
- "Avg accuracy 0.5488700564971751\n"
+ "\tAccuracy 52.542%\n",
+ "\n",
+ "Avg accuracy 54.887%\n"
]
}
],
"source": [
"from sklearn.cluster import KMeans\n",
"\n",
+ "use_pca = True\n",
"# number of components extracted from the pca\n",
"n_features = 10\n",
"\n",
@@ -508,48 +488,127 @@
"kf = KFold(n_splits=k_folds)\n",
"\n",
"accuracies = []\n",
+ "print(f'Training {k_folds} folds')\n",
"for i, (train_idx, test_idx) in enumerate(kf.split(X[numeric_columns])):\n",
- " print(f'Training fold {i} for {epochs} epochs')\n",
"\n",
+ " print(f'Fold {i}')\n",
+ " \n",
" # extract train and test data from the cleaned dataset\n",
" X_train, X_test = X.iloc[train_idx], X.iloc[test_idx]\n",
" y_train, y_test = y[train_idx], y[test_idx]\n",
"\n",
- " print(f'Train samples:\\t{len(X_train)}')\n",
- " print(f'Test samples:\\t{len(X_test)}')\n",
+ " print(f'\\tTrain samples:\\t{len(X_train)}\\tTest samples:\\t{len(X_test)}')\n",
"\n",
- " # do pca based on the train data of the given fold to extract 'n_features'\n",
- " #pca = decomposition.PCA(n_components=n_features)\n",
- " #pca.fit(X_train)\n",
- " #X_train = pca.transform(X_train)\n",
+ " if use_pca:\n",
+ " # do pca based on the train data of the given fold to extract 'n_features'\n",
+ " pca = decomposition.PCA(n_components=n_features)\n",
+ " pca.fit(X_train)\n",
+ " X_train = pca.transform(X_train)\n",
"\n",
- " model = KMeans(n_clusters=2)\n",
+ " model = KMeans(n_clusters=2, n_init=10)\n",
" model.fit(X_train)\n",
"\n",
- " #X_test = pca.transform(X_test)\n",
+ " if use_pca:\n",
+ " X_test = pca.transform(X_test)\n",
+ " \n",
" y_pred = model.predict(X_test)\n",
- " print(y_pred)\n",
- " \n",
"\n",
" # calculate the accuracy of the train data for the current fold\n",
" accuracy1 = sum(y_pred == y_test)[0] / len(y_pred)\n",
" accuracy2 = sum(y_pred != y_test)[0] / len(y_pred)\n",
" accuracy = max(accuracy1, accuracy2)\n",
" accuracies.append(accuracy)\n",
- " print(f'Accuracy of fold {i}: {accuracy}')\n",
+ " print(f'\\tAccuracy {accuracy:.3%}')\n",
+ " print()\n",
"\n",
"# calculate the average accuracy over all folds\n",
"avg_accuracy = sum(accuracies) / len(accuracies)\n",
- "print(f'Avg accuracy {avg_accuracy}')"
+ "print(f'Avg accuracy {avg_accuracy:.3%}')"
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 6,
"id": "880302e4-82c1-47b9-9fe3-cb3567511639",
"metadata": {},
- "outputs": [],
- "source": []
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Training 5 folds\n",
+ "Fold 0\n",
+ "\tTrain samples:\t237\tTest samples:\t60\n",
+ "\tAccuracy 85.000%\n",
+ "\n",
+ "Fold 1\n",
+ "\tTrain samples:\t237\tTest samples:\t60\n",
+ "\tAccuracy 90.000%\n",
+ "\n",
+ "Fold 2\n",
+ "\tTrain samples:\t238\tTest samples:\t59\n",
+ "\tAccuracy 84.746%\n",
+ "\n",
+ "Fold 3\n",
+ "\tTrain samples:\t238\tTest samples:\t59\n",
+ "\tAccuracy 76.271%\n",
+ "\n",
+ "Fold 4\n",
+ "\tTrain samples:\t238\tTest samples:\t59\n",
+ "\tAccuracy 77.966%\n",
+ "\n",
+ "Avg accuracy 82.797%\n"
+ ]
+ }
+ ],
+ "source": [
+ "from sklearn.ensemble import RandomForestClassifier\n",
+ "\n",
+ "use_pca = True\n",
+ "# number of components extracted from the pca\n",
+ "n_features = 10\n",
+ "\n",
+ "k_folds = 5\n",
+ "\n",
+ "# used to split the dataset into k folds\n",
+ "kf = KFold(n_splits=k_folds)\n",
+ "\n",
+ "accuracies = []\n",
+ "print(f'Training {k_folds} folds')\n",
+ "for i, (train_idx, test_idx) in enumerate(kf.split(X[numeric_columns])):\n",
+ " print(f'Fold {i}')\n",
+ "\n",
+ " # extract train and test data from the cleaned dataset\n",
+ " X_train, X_test = X.iloc[train_idx], X.iloc[test_idx]\n",
+ " y_train, y_test = y[train_idx], y[test_idx]\n",
+ " y_train, y_test = y_train[:, 0], y_test[:, 0]\n",
+ "\n",
+ " print(f'\\tTrain samples:\\t{len(X_train)}\\tTest samples:\\t{len(X_test)}')\n",
+ "\n",
+ " if use_pca:\n",
+ " # do pca based on the train data of the given fold to extract 'n_features'\n",
+ " pca = decomposition.PCA(n_components=n_features)\n",
+ " pca.fit(X_train)\n",
+ " X_train = pca.transform(X_train)\n",
+ "\n",
+ " model = RandomForestClassifier(max_depth=2, random_state=0)\n",
+ " model.fit(X_train, y_train)\n",
+ "\n",
+ " if use_pca:\n",
+ " X_test = pca.transform(X_test)\n",
+ " \n",
+ " y_pred = model.predict(X_test)\n",
+ "\n",
+ " # calculate the accuracy of the train data for the current fold\n",
+ " accuracy = sum(y_pred == y_test) / len(y_pred)\n",
+ " accuracies.append(accuracy)\n",
+ " print(f'\\tAccuracy {accuracy:.3%}')\n",
+ " print()\n",
+ "\n",
+ "# calculate the average accuracy over all folds\n",
+ "avg_accuracy = sum(accuracies) / len(accuracies)\n",
+ "print(f'Avg accuracy {avg_accuracy:.3%}')"
+ ]
}
],
"metadata": {
diff --git a/Exploration.ipynb b/Exploration.ipynb
index be2a4a6..218098f 100644
--- a/Exploration.ipynb
+++ b/Exploration.ipynb
@@ -2,34 +2,11 @@
"cells": [
{
"cell_type": "code",
- "execution_count": 3,
+ "execution_count": 1,
"id": "37d611da-6f56-46d8-905a-62026750150c",
"metadata": {
"tags": []
},
- "outputs": [],
- "source": [
- "from ucimlrepo import fetch_ucirepo\n",
- "import pandas as pd\n",
- "\n",
- "# fetch dataset \n",
- "heart_disease = fetch_ucirepo(id=45) \n",
- " \n",
- "# data (as pandas dataframes) \n",
- "X = heart_disease.data.features \n",
- "y = heart_disease.data.targets \n",
- "\n",
- "male=1\n",
- "female=0"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "id": "ae26378f-c104-4664-a313-ed8d9edfed42",
- "metadata": {
- "tags": []
- },
"outputs": [
{
"data": {
@@ -174,31 +151,31 @@
"4 0.0 3.0 0 "
]
},
- "execution_count": 6,
+ "execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
- "df = pd.concat([X, y], axis=1)\n",
- "df = df.rename(columns={'num':'goal'})\n",
+ "import pandas as pd\n",
+ "import numpy as np\n",
+ "\n",
+ "df = pd.read_csv('./data/dataset_cleaned.csv')\n",
+ "\n",
+ "# extract all columns except 'goal' --> X\n",
+ "X = df.loc[:, df.columns != 'goal']\n",
+ "# extract only the column 'goal' --> y\n",
+ "y = df.loc[:, 'goal']\n",
+ "\n",
+ "male=1\n",
+ "female=0\n",
"\n",
"df.head()"
]
},
{
"cell_type": "code",
- "execution_count": 8,
- "id": "feef6121-af85-4bd5-a04f-f2ff38b3c556",
- "metadata": {},
- "outputs": [],
- "source": [
- "# df.to_csv('./data/dataset_cleaned.csv', index=False)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 128,
+ "execution_count": 2,
"id": "6b3e5424-4a7e-4e53-82b9-d78e38939834",
"metadata": {
"tags": []
@@ -206,7 +183,7 @@
"outputs": [
{
"data": {
- "image/png": "iVBORw0KGgoAAAANSUhEUgAAAigAAAGxCAYAAABIjE2TAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAAr7UlEQVR4nO3dfVxUdaLH8e+EMqIOKCAMs45IZu1umI/dXKoV8ilC3NLyqQe5mdtezXtJuRU9iXu7Uu4trdzsYc2H0sy6Wd3FhzBR82ob6mpqXVcNAl9ClCkDaoPhuX/0crYRULGZ5Qd+3q/Xeb085/zOOb9xd/DTmQdslmVZAgAAMMglTT0BAACAMxEoAADAOAQKAAAwDoECAACMQ6AAAADjECgAAMA4BAoAADAOgQIAAIxDoAAAAOMQKIDBnnvuOdlsNiUmJjb1VOpVXFwsm82mhQsX+rbl5OTIZrM16jzHjx9XTk6O1q9f36jj6rtW165dNWzYsEad51yWLl2qOXPm1LvPZrMpJycnoNcDQKAARnv11VclSXv27NFf/vKXJp7N+bnnnnu0ZcuWRh1z/PhxzZgxo9GBciHXuhBnC5QtW7bonnvuCfocgIsNgQIYauvWrdq5c6fS0tIkSfPnz2/iGZ2fzp07q3///kG9xvHjx/9h1zqX/v37q3Pnzk06B6AlIlAAQ50OkieffFJJSUlatmyZ7x/mHzt48KBuvfVWORwOdejQQbfffrsKCwvrvPQi/RA9w4cPV2RkpNq0aaPevXtr+fLl5zWfQ4cOadSoUXI4HIqIiNDo0aNVXl5eZ1x9L7usW7dOycnJioqKUlhYmLp06aKRI0fq+PHjKi4uVqdOnSRJM2bMkM1mk81mU0ZGht/5tm/frltvvVUdO3ZUt27dGrzWaStWrNBVV12lNm3a6NJLL9Vzzz3nt3/hwoWy2WwqLi72275+/XrZbDbf3Zzk5GTl5eXpyy+/9M3tx9es7yWe3bt36ze/+Y06duyoNm3aqFevXlq0aFG913njjTf0yCOPyOVyKTw8XIMGDdLevXvrfUzAxYRAAQx04sQJvfHGG7r66quVmJiou+++W1VVVXrrrbf8xh07dkwpKSkqKCjQU089peXLlys2NlajR4+uc86CggJde+21Onr0qF588UW999576tWrl0aPHl0nZOqbz6BBg/TBBx8oNzdXb731lpxOZ73XOVNxcbHS0tIUGhqqV199VatXr9aTTz6pdu3aqaamRnFxcVq9erUkacKECdqyZYu2bNmixx57zO88I0aM0GWXXaa33npLL7744lmvuWPHDmVmZur+++/XihUrlJSUpH/7t3/Tf/3Xf51zvmd64YUXdO2118rpdPrmdraXlfbu3aukpCTt2bNHzz33nN555x398pe/VEZGhmbNmlVn/MMPP6wvv/xSf/rTn/Tyyy9r3759Sk9PV21tbaPnCrQoFgDjLF682JJkvfjii5ZlWVZVVZXVvn176/rrr/cb98c//tGSZK1atcpv+7333mtJshYsWODb9vOf/9zq3bu3dfLkSb+xw4YNs+Li4qza2toG5zNv3jxLkvXee+/5bZ84cWKd60yfPt368Y+Wt99+25Jk7dixo8Hzf/3115Yka/r06XX2nT7f448/3uC+H4uPj7dsNlud6w0ePNgKDw+3jh07ZlmWZS1YsMCSZBUVFfmNKygosCRZBQUFvm1paWlWfHx8vXM/c95jxoyx7Ha7VVJS4jcuNTXVatu2rXX06FG/69x0001+45YvX25JsrZs2VLv9YCLBXdQAAPNnz9fYWFhGjNmjCSpffv2uu222/TRRx9p3759vnEbNmyQw+HQjTfe6Hf82LFj/db379+v//u//9Ptt98uSfr+++99y0033aSysrKzvqxQUFAgh8Oh4cOH+20fN27cOR9Lr169FBoaqt/+9rdatGiRvvjii3MeU5+RI0ee99grr7xSPXv29Ns2btw4eTwebd++/YKuf77WrVungQMHyu12+23PyMjQ8ePH69x9OfPv9KqrrpIkffnll0GdJ2A6AgUwzP79+7Vx40alpaXJsiwdPXpUR48e1a233irp75/skaTDhw8rNja2zjnO3PbVV19JkrKystS6dWu/ZdKkSZKkb775psE5NXQdp9N5zsfTrVs3rV27VjExMZo8ebK6deumbt266dlnnz3nsT8WFxd33mPrm9fpbYcPH27UdRvr8OHD9c7V5XLVe/2oqCi/dbvdLumHl9WAi1mrpp4AAH+vvvqqLMvS22+/rbfffrvO/kWLFumJJ55QSEiIoqKi9Mknn9QZc+abV6OjoyVJ2dnZGjFiRL3XveKKKxqc0/lepyHXX3+9rr/+etXW1mrr1q16/vnnlZmZqdjYWN9donNpzHer1Dev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fD7rAwAA8A/E7+IBAADGIVAAAIBxCBQAAGAcAgUAABiHQEFAWJal3/72t4qMjJTNZtOOHTuaZB7FxcVNen0AgZWRkaGbb765qaeBJtCkX3WPlmP16tVauHCh1q9fr0svvVTR0dFNPSUAQDNGoCAgDhw4oLi4OL4JGAAQELzEg58sIyNDU6ZMUUlJiWw2m7p27SrLsjRr1ixdeumlCgsLU8+ePfX222/7jlm/fr1sNpvWrFmj3r17KywsTDfccIMqKiq0atUq/eIXv1B4eLjGjh2r48eP+45bvXq1rrvuOnXo0EFRUVEaNmyYDhw4cNb5ffbZZ7rpppvUvn17xcbG6s4779Q333wTtL8P4GKVnJysKVOmKDMzUx07dlRsbKxefvllHTt2TP/8z/8sh8Ohbt26adWqVZKk2tpaTZgwQQkJCQoLC9MVV1yhZ5999qzXONfPFrQcBAp+smeffVa///3v1blzZ5WVlamwsFCPPvqoFixYoHnz5mnPnj26//77dccdd2jDhg1+x+bk5Gju3LnavHmzSktLNWrUKM2ZM0dLly5VXl6e8vPz9fzzz/vGHzt2TFOnTlVhYaE+/PBDXXLJJbrlllt06tSpeudWVlamAQMGqFevXtq6datWr16tr776SqNGjQrq3wlwsVq0aJGio6P1ySefaMqUKfqXf/kX3XbbbUpKStL27ds1dOhQ3XnnnTp+/LhOnTqlzp07a/ny5frss8/0+OOP6+GHH9by5csbPP/5/mxBC2ABATB79mwrPj7esizLqq6uttq0aWNt3rzZb8yECROssWPHWpZlWQUFBZYka+3atb79ubm5liTrwIEDvm333nuvNXTo0AavW1FRYUmydu3aZVmWZRUVFVmSrL/+9a+WZVnWY489Zg0ZMsTvmNLSUkuStXfv3gt+vADqGjBggHXdddf51r///nurXbt21p133unbVlZWZkmytmzZUu85Jk2aZI0cOdK3Pn78eOs3v/mNZVnn97MFLQfvQUHAffbZZ/ruu+80ePBgv+01NTXq3bu337arrrrK9+fY2Fi1bdtWl156qd+2Tz75xLd+4MABPfbYY/r444/1zTff+O6clJSUKDExsc5ctm3bpoKCArVv377OvgMHDujyyy+/sAcJoF4/fk6HhIQoKipKPXr08G2LjY2VJFVUVEiSXnzxRf3pT3/Sl19+qRMnTqimpka9evWq99yN+dmC5o9AQcCdjoa8vDz97Gc/89t35i8Ca926te/PNpvNb/30th+/fJOeni63261XXnlFLpdLp06dUmJiompqahqcS3p6up566qk6++Li4hr3wACcU33P4TOf59IPz83ly5fr/vvv19NPP61f/epXcjgc+sMf/qC//OUv9Z67MT9b0PwRKAi4X/7yl7Lb7SopKdGAAQMCdt7Dhw/r888/10svvaTrr79ekrRp06azHtOnTx/993//t7p27apWrfi/O2CSjz76SElJSZo0aZJv29ne9B6sny0wEz+xEXAOh0NZWVm6//77derUKV133XXyeDzavHmz2rdvr/Hjx1/QeTt27KioqCi9/PLLiouLU0lJiR566KGzHjN58mS98sorGjt2rP793/9d0dHR2r9/v5YtW6ZXXnlFISEhFzQXAD/dZZddpsWLF2vNmjVKSEjQa6+9psLCQiUkJNQ7Plg/W2AmAgVB8R//8R+KiYlRbm6uvvjiC3Xo0EF9+vTRww8/fMHnvOSSS7Rs2TL967/+qxITE3XFFVfoueeeU3JycoPHuFwu/e///q8efPBBDR06VF6vV/Hx8brxxht1ySV8iA1oSr/73e+0Y8cOjR49WjabTWPHjtWkSZN8H0OuTzB+tsBMNsuyrKaeBAAAwI/xn5AAAMA4BAoAADAOgQIAAIxDoAAAAOMQKAAAwDgECgAAMA6BAgAAjEOgAAAA4xAoAADAOAQKAAAwDoECAACM8/9kmCBawMoUogAAAABJRU5ErkJggg==",
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAAigAAAGxCAYAAABIjE2TAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAzy0lEQVR4nO3deXxU5d3///cYyCSBZEISkpmUECJGqgZZvbVBS5DNEEAWZa2SW6S2UvqNSFXcCL2VAK0CSt2osigUlFuQymbY4QYtiyCgpUATwJI0ipBJABMg5/eHP6YOCYHgjHMlvJ6Px3k8ONe5znU+J2Fm3jnb2CzLsgQAAGCQawJdAAAAwIUIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgogMFeeukl2Ww2paSkBLqUKuXn58tms2nWrFmetuzsbNlsthqNc+rUKWVnZ2vdunU1Wq+qbTVr1kw9e/as0TiXMm/ePE2dOrXKZTabTdnZ2T7dHgACCmC0t956S5K0d+9effLJJwGu5vI8+OCD2rJlS43WOXXqlMaPH1/jgHIl27oS1QWULVu26MEHH/R7DcDVhoACGGrbtm3atWuXMjIyJElvvvlmgCu6PE2aNNFtt93m122cOnXqR9vWpdx2221q0qRJQGsA6iICCmCo84Fk4sSJSk1N1fz58z0fzN/35Zdf6p577lF4eLgiIyM1dOhQbd26tdKpF+m70NO7d29FRUUpJCREbdq00bvvvntZ9Rw9elQDBgxQeHi4HA6HBg4cqMLCwkr9qjrtsmbNGqWlpSk6OlqhoaFq2rSp+vfvr1OnTik/P1+NGzeWJI0fP142m002m02ZmZle4+3YsUP33HOPGjVqpObNm190W+ctWrRIN998s0JCQnTttdfqpZde8lo+a9Ys2Ww25efne7WvW7dONpvNczQnLS1NS5cu1aFDhzy1fX+bVZ3i2bNnj+6++241atRIISEhat26tWbPnl3ldv7yl7/oqaeeUnx8vCIiItSlSxft27evyn0CriYEFMBAp0+f1l/+8hfdcsstSklJ0QMPPKCSkhK99957Xv1OnjypTp06ae3atZo0aZLeffddxcXFaeDAgZXGXLt2rTp06KATJ07otdde0wcffKDWrVtr4MCBlYJMVfV06dJFH330kXJycvTee+/J6XRWuZ0L5efnKyMjQ8HBwXrrrbe0YsUKTZw4UQ0aNFB5eblcLpdWrFghSRo+fLi2bNmiLVu26JlnnvEap1+/frruuuv03nvv6bXXXqt2mzt37lRWVpYeeeQRLVq0SKmpqfp//+//6Y9//OMl673QK6+8og4dOsjpdHpqq+600r59+5Samqq9e/fqpZde0vvvv68bb7xRmZmZmjx5cqX+Tz75pA4dOqQ///nPeuONN7R//3716tVL586dq3GtQJ1iATDOnDlzLEnWa6+9ZlmWZZWUlFgNGza07rjjDq9+f/rTnyxJ1vLly73aH3roIUuSNXPmTE/bT3/6U6tNmzbWmTNnvPr27NnTcrlc1rlz5y5az6uvvmpJsj744AOv9hEjRlTazrhx46zvv7UsXLjQkmTt3LnzouN/9dVXliRr3LhxlZadH+/ZZ5+96LLvS0xMtGw2W6Xtde3a1YqIiLBOnjxpWZZlzZw505Jk5eXlefVbu3atJclau3atpy0jI8NKTEyssvYL6x40aJBlt9utw4cPe/VLT0+3wsLCrBMnTnhtp0ePHl793n33XUuStWXLliq3B1wtOIICGOjNN99UaGioBg0aJElq2LCh7r33Xm3cuFH79+/39Fu/fr3Cw8N11113ea0/ePBgr/kDBw7o73//u4YOHSpJOnv2rGfq0aOHCgoKqj2tsHbtWoWHh6t3795e7UOGDLnkvrRu3VrBwcH65S9/qdmzZ+uf//znJdepSv/+/S+770033aRWrVp5tQ0ZMkRut1s7duy4ou1frjVr1qhz585KSEjwas/MzNSpU6cqHX258Gd68803S5IOHTrk1zoB0xFQAMMcOHBAGzZsUEZGhizL0okTJ3TixAndc889kv5zZ48kHTt2THFxcZXGuLDt3//+tyRpzJgxql+/vtf08MMPS5K+/vrri9Z0se04nc5L7k/z5s21atUqxcbGauTIkWrevLmaN2+uadOmXXLd73O5XJfdt6q6zrcdO3asRtutqWPHjlVZa3x8fJXbj46O9pq32+2SvjutBlzN6gW6AADe3nrrLVmWpYULF2rhwoWVls+ePVvPPfecgoKCFB0drb/97W+V+lx48WpMTIwkaezYserXr1+V223RosVFa7rc7VzMHXfcoTvuuEPnzp3Ttm3b9PLLLysrK0txcXGeo0SXUpNnq1RV1/m284EgJCREklRWVubVr7qgdjmio6NVUFBQqf3o0aOS/vO7AFA9jqAABjl37pxmz56t5s2ba+3atZWmRx99VAUFBVq+fLkkqWPHjiopKfHMnzd//nyv+RYtWig5OVm7du1S+/btq5zCw8MvWlenTp1UUlKiJUuWeLXPmzevRvsXFBSkW2+9VX/6058kyXO6xddHDfbu3atdu3Z5tc2bN0/h4eFq27atpO8e6CZJn332mVe/C/fxfH2XW1vnzp21Zs0aTyA5b86cOQoLCwv4bdFAbcERFMAgy5cv19GjRzVp0iSlpaVVWp6SkqLp06frzTffVM+ePTVs2DBNmTJFv/jFL/Tcc8/puuuu0/Lly7Vy5UpJ0jXX/OdvkNdff13p6enq3r27MjMz9ZOf/ETffPONvvjiC+3YsaPSHULfd//992vKlCm6//779fzzzys5OVnLli3zbKc6r732mtasWaOMjAw1bdpU3377rec0VZcuXSRJ4eHhSkxM1AcffKDOnTsrKipKMTExnhBRU/Hx8erdu7eys7Plcrn0zjvvKDc3V5MmTVJYWJgk6ZZbblGLFi00ZswYnT17Vo0aNdKiRYu0adOmSuO1bNlS77//vl599VW1a9dO11xzjdq3b1/ltseNG6cPP/xQnTp10rPPPquoqCjNnTtXS5cu1eTJk+VwOK5on4CrTqCv0gXwH3369LGCg4OtoqKii/YZNGiQVa9ePauwsNCyLMs6fPiw1a9fP6thw4ZWeHi41b9/f2vZsmVV3nWza9cua8CAAVZsbKxVv359y+l0WnfeeafnbqHqfPnll1b//v29trN58+ZL3sWzZcsWq2/fvlZiYqJlt9ut6Ohoq2PHjtaSJUu8xl+1apXVpk0by263W5KsYcOGeY331VdfVarpYnfxZGRkWAsXLrRuuukmKzg42GrWrJn14osvVlr/H//4h9WtWzcrIiLCaty4sTVq1Chr6dKlle7i+eabb6x77rnHioyMtGw2m9c2VcXdR7t377Z69eplORwOKzg42GrVqpXXz8iy/nMXz3vvvefVnpeXV+lnClyNbJZlWQHKRgD8ZMKECXr66ad1+PBhnnIKoFbiFA9Qy02fPl2S9NOf/lRnzpzRmjVr9NJLL+kXv/gF4QRArUVAAWq5sLAwTZkyRfn5+SorK1PTpk31+OOP6+mnnw50aQBwxTjFAwAAjMNtxgAAwDgEFAAAYBwCCgAAME6tvEi2oqJCR48eVXh4eI0efw0AAALHsiyVlJQoPj7e60GSVamVAeXo0aOVvikUAADUDkeOHLnkYxBqZUA5/50hR44cUURERICrAQAAl8PtdishIaHa7/46r1YGlPOndSIiIggoAADUMpdzeQYXyQIAAOMQUAAAgHEIKAAAwDi18hqUy2FZls6ePatz584FuhRICgoKUr169bgtHABwWepkQCkvL1dBQYFOnToV6FLwPWFhYXK5XAoODg50KQAAw9W5gFJRUaG8vDwFBQUpPj5ewcHB/NUeYJZlqby8XF999ZXy8vKUnJx8yQf0AACubnUuoJSXl6uiokIJCQkKCwsLdDn4/4WGhqp+/fo6dOiQysvLFRISEuiSAAAGq7N/xvIXunn4nQAALhefGAAAwDgEFAAAYJw6dw1KdZo9sfRH3V7+xIwa9U9LS1Pr1q01depU/xQkqVmzZsrKylJWVtZF+2RnZ2vx4sXauXOn3+oAAKA6HEG5ytlsNi1evDjQZQAA4KVGASUnJ0e33HKLwsPDFRsbqz59+mjfvn1efSzLUnZ2tuLj4xUaGqq0tDTt3bvXq09ZWZlGjRqlmJgYNWjQQL1799aXX375w/cGAADUCTUKKOvXr9fIkSP18ccfKzc3V2fPnlW3bt108uRJT5/JkyfrxRdf1PTp07V161Y5nU517dpVJSUlnj5ZWVlatGiR5s+fr02bNqm0tFQ9e/bkqa/67jkujz32mKKiouR0OpWdne1ZVlxcrF/+8peKjY1VRESE7rzzTu3atcuz/ODBg7r77rsVFxenhg0b6pZbbtGqVasuuq1mzZpJkvr27SubzeaZP+/tt99Ws2bN5HA4NGjQIM/vcM6cOYqOjlZZWZlX//79++v+++//YT8AAABUw2tQVqxY4TU/c+ZMxcbGavv27fr5z38uy7I0depUPfXUU+rXr58kafbs2YqLi9O8efP00EMPqbi4WG+++abefvttdenSRZL0zjvvKCEhQatWrVL37t19tGu10+zZszV69Gh98skn2rJlizIzM9WhQwd16dJFGRkZioqK0rJly+RwOPT666+rc+fO+sc//qGoqCiVlpaqR48eeu655xQSEqLZs2erV69e2rdvn5o2bVppW1u3blVsbKxmzpypu+66S0FBQZ5lBw8e1OLFi/Xhhx/q+PHjGjBggCZOnKjnn39e9957r377299qyZIluvfeeyVJX3/9tT788MNK/0cAU/3Y16QBtU1Nr6P0tR90DUpxcbEkKSoqSpKUl5enwsJCdevWzdPHbrerY8eO2rx5syRp+/btOnPmjFef+Ph4paSkePpcqKysTG6322uqq26++WaNGzdOycnJuv/++9W+fXutXr1aa9eu1e7du/Xee++pffv2Sk5O1h//+EdFRkZq4cKFkqRWrVrpoYceUsuWLZWcnKznnntO1157rZYsWVLltho3bixJioyMlNPp9MxL3x3JmTVrllJSUnTHHXfovvvu0+rVqyV999C1IUOGaObMmZ7+c+fOVZMmTZSWluannwwA4GpyxQHFsiyNHj1at99+u1JSUiRJhYWFkqS4uDivvnFxcZ5lhYWFCg4OVqNGjS7a50I5OTlyOByeKSEh4UrLNt7NN9/sNe9yuVRUVKTt27ertLRU0dHRatiwoWfKy8vTwYMHJUknT57UY489phtvvFGRkZFq2LCh/v73v+vw4cM1rqNZs2YKDw+vVMd5I0aM0EcffaR//etfkr47mpaZmcnXCgAAfOKKbzP+zW9+o88++0ybNm2qtOzCDynLsi75wVVdn7Fjx2r06NGeebfbXWdDSv369b3mbTabKioqVFFRIZfLpXXr1lVaJzIyUpL0u9/9TitXrtQf//hHXXfddQoNDdU999yj8vJyn9VxXps2bdSqVSvNmTNH3bt31+7du/XXv/61xtsBAKAqVxRQRo0apSVLlmjDhg1q0qSJp93pdEr67iiJy+XytBcVFXmOqjidTpWXl+v48eNeR1GKioqUmppa5fbsdrvsdvuVlFpntG3bVoWFhapXr16li1nP27hxozIzM9W3b19JUmlpqfLz86sdt379+ld8cfKDDz6oKVOm6F//+pe6dOlSZ0MjAODHV6NTPJZl6Te/+Y3ef/99rVmzRklJSV7Lk5KS5HQ6lZub62krLy/X+vXrPeGjXbt2ql+/vlefgoIC7dmz56IBBVKXLl30s5/9TH369NHKlSuVn5+vzZs36+mnn9a2bdskSdddd53ef/997dy5U7t27dKQIUO8jnpUpVmzZlq9erUKCwt1/PjxGtU0dOhQ/etf/9KMGTP0wAMPXPG+AQBwoRodQRk5cqTmzZunDz74QOHh4Z5rRhwOh0JDQ2Wz2ZSV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",
"text/plain": [
"