818 lines
70 KiB
Plaintext
818 lines
70 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 1,
|
||
"id": "initial_id",
|
||
"metadata": {
|
||
"jupyter": {
|
||
"is_executing": true
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"import pandas as pd\n",
|
||
"from sklearn.preprocessing import MinMaxScaler, StandardScaler\n",
|
||
"from sklearn.model_selection import KFold\n",
|
||
"from sklearn import decomposition"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 2,
|
||
"id": "67503952-9074-4cdb-9d7e-d9142f7c319c",
|
||
"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>age</th>\n",
|
||
" <th>trestbps</th>\n",
|
||
" <th>chol</th>\n",
|
||
" <th>thalach</th>\n",
|
||
" <th>oldpeak</th>\n",
|
||
" <th>sex_0</th>\n",
|
||
" <th>sex_1</th>\n",
|
||
" <th>cp_1</th>\n",
|
||
" <th>cp_2</th>\n",
|
||
" <th>cp_3</th>\n",
|
||
" <th>...</th>\n",
|
||
" <th>slope_1</th>\n",
|
||
" <th>slope_2</th>\n",
|
||
" <th>slope_3</th>\n",
|
||
" <th>thal_3.0</th>\n",
|
||
" <th>thal_6.0</th>\n",
|
||
" <th>thal_7.0</th>\n",
|
||
" <th>ca_0.0</th>\n",
|
||
" <th>ca_1.0</th>\n",
|
||
" <th>ca_2.0</th>\n",
|
||
" <th>ca_3.0</th>\n",
|
||
" </tr>\n",
|
||
" </thead>\n",
|
||
" <tbody>\n",
|
||
" <tr>\n",
|
||
" <th>0</th>\n",
|
||
" <td>0.708333</td>\n",
|
||
" <td>0.481132</td>\n",
|
||
" <td>0.244292</td>\n",
|
||
" <td>0.603053</td>\n",
|
||
" <td>0.370968</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>True</td>\n",
|
||
" <td>True</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>...</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>True</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>True</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>True</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>1</th>\n",
|
||
" <td>0.791667</td>\n",
|
||
" <td>0.622642</td>\n",
|
||
" <td>0.365297</td>\n",
|
||
" <td>0.282443</td>\n",
|
||
" <td>0.241935</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>True</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>...</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>True</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>True</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>True</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>2</th>\n",
|
||
" <td>0.791667</td>\n",
|
||
" <td>0.245283</td>\n",
|
||
" <td>0.235160</td>\n",
|
||
" <td>0.442748</td>\n",
|
||
" <td>0.419355</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>True</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>...</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>True</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>True</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>True</td>\n",
|
||
" <td>False</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>3</th>\n",
|
||
" <td>0.166667</td>\n",
|
||
" <td>0.339623</td>\n",
|
||
" <td>0.283105</td>\n",
|
||
" <td>0.885496</td>\n",
|
||
" <td>0.564516</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>True</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>True</td>\n",
|
||
" <td>...</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>True</td>\n",
|
||
" <td>True</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>True</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" </tr>\n",
|
||
" <tr>\n",
|
||
" <th>4</th>\n",
|
||
" <td>0.250000</td>\n",
|
||
" <td>0.339623</td>\n",
|
||
" <td>0.178082</td>\n",
|
||
" <td>0.770992</td>\n",
|
||
" <td>0.225806</td>\n",
|
||
" <td>True</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>True</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>...</td>\n",
|
||
" <td>True</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>True</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>True</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" <td>False</td>\n",
|
||
" </tr>\n",
|
||
" </tbody>\n",
|
||
"</table>\n",
|
||
"<p>5 rows × 28 columns</p>\n",
|
||
"</div>"
|
||
],
|
||
"text/plain": [
|
||
" age trestbps chol thalach oldpeak sex_0 sex_1 cp_1 \\\n",
|
||
"0 0.708333 0.481132 0.244292 0.603053 0.370968 False True True \n",
|
||
"1 0.791667 0.622642 0.365297 0.282443 0.241935 False True False \n",
|
||
"2 0.791667 0.245283 0.235160 0.442748 0.419355 False True False \n",
|
||
"3 0.166667 0.339623 0.283105 0.885496 0.564516 False True False \n",
|
||
"4 0.250000 0.339623 0.178082 0.770992 0.225806 True False False \n",
|
||
"\n",
|
||
" cp_2 cp_3 ... slope_1 slope_2 slope_3 thal_3.0 thal_6.0 thal_7.0 \\\n",
|
||
"0 False False ... False False True False True False \n",
|
||
"1 False False ... False True False True False False \n",
|
||
"2 False False ... False True False False False True \n",
|
||
"3 False True ... False False True True False False \n",
|
||
"4 True False ... True False False True False False \n",
|
||
"\n",
|
||
" ca_0.0 ca_1.0 ca_2.0 ca_3.0 \n",
|
||
"0 True False False False \n",
|
||
"1 False False False True \n",
|
||
"2 False False True False \n",
|
||
"3 True False False False \n",
|
||
"4 True False False False \n",
|
||
"\n",
|
||
"[5 rows x 28 columns]"
|
||
]
|
||
},
|
||
"execution_count": 2,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"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",
|
||
"# add new axis to y, new shape: (n, 1)\n",
|
||
"y = y.to_numpy()\n",
|
||
"y = y.reshape((len(y),1))\n",
|
||
"\n",
|
||
"# binarize y\n",
|
||
"y[y>0] = 1\n",
|
||
"\n",
|
||
"factor_columns = ['sex', 'cp', 'fbs', 'restecg', 'exang', 'slope', 'thal', 'ca']\n",
|
||
"numeric_columns = [column for column in X.columns if column not in factor_columns]\n",
|
||
"\n",
|
||
"# transform factors into onehot vectors\n",
|
||
"X = pd.get_dummies(X, columns=factor_columns)\n",
|
||
"\n",
|
||
"# min max scaling of numeric columns\n",
|
||
"scaler = MinMaxScaler()\n",
|
||
"X[numeric_columns] = scaler.fit_transform(X[numeric_columns])\n",
|
||
"\n",
|
||
"X.head()"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 3,
|
||
"id": "2bbee865-c000-43da-84d9-ce7e04874110",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"def get_model(n_features):\n",
|
||
" model = tf.keras.models.Sequential([\n",
|
||
" tf.keras.layers.InputLayer(shape=(n_features,)),\n",
|
||
" tf.keras.layers.Dense(30, activation='relu'),\n",
|
||
" tf.keras.layers.Dense(30, activation='relu'),\n",
|
||
" tf.keras.layers.Dense(1, activation='sigmoid')\n",
|
||
" ], name='test')\n",
|
||
" model.compile(optimizer=tf.keras.optimizers.Adam(), \n",
|
||
" loss=tf.keras.losses.BinaryCrossentropy())\n",
|
||
" return model"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 4,
|
||
"id": "38eb4f87-ca3c-4ecf-a8ca-29422822d933",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"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 <function TensorFlowTrainer.make_predict_function.<locals>.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 <function TensorFlowTrainer.make_predict_function.<locals>.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": [
|
||
"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",
|
||
"\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 for {epochs} epochs')\n",
|
||
"for i, (train_idx, test_idx) in enumerate(kf.split(X)):\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'\\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",
|
||
" # 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",
|
||
" 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 # 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'\\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:.3%}')"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 5,
|
||
"id": "95215693-47c9-4202-92f5-efbc65bc32c9",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"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: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"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"\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: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"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"\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: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"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"\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: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"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"\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: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"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"\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",
|
||
"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",
|
||
"\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'\\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 = KMeans(n_clusters=2, n_init=10)\n",
|
||
" model.fit(X_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",
|
||
" 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'\\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%}')"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 6,
|
||
"id": "880302e4-82c1-47b9-9fe3-cb3567511639",
|
||
"metadata": {},
|
||
"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%}')"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "15b73e96-8b24-4087-b491-f9248577a886",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Clustering and PCA\n",
|
||
"Um zu analysieren, ob ähnliche Merkmale auch zur gleichen Diagnose führen, wird zuerst ein k-Means Clustering angewandt."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 40,
|
||
"id": "79631688-07cb-450d-9958-8d8341722d7d",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/html": [
|
||
"<style>#sk-container-id-6 {color: black;}#sk-container-id-6 pre{padding: 0;}#sk-container-id-6 div.sk-toggleable {background-color: white;}#sk-container-id-6 label.sk-toggleable__label {cursor: pointer;display: block;width: 100%;margin-bottom: 0;padding: 0.3em;box-sizing: border-box;text-align: center;}#sk-container-id-6 label.sk-toggleable__label-arrow:before {content: \"▸\";float: left;margin-right: 0.25em;color: #696969;}#sk-container-id-6 label.sk-toggleable__label-arrow:hover:before {color: black;}#sk-container-id-6 div.sk-estimator:hover label.sk-toggleable__label-arrow:before {color: black;}#sk-container-id-6 div.sk-toggleable__content {max-height: 0;max-width: 0;overflow: hidden;text-align: left;background-color: #f0f8ff;}#sk-container-id-6 div.sk-toggleable__content pre {margin: 0.2em;color: black;border-radius: 0.25em;background-color: #f0f8ff;}#sk-container-id-6 input.sk-toggleable__control:checked~div.sk-toggleable__content {max-height: 200px;max-width: 100%;overflow: auto;}#sk-container-id-6 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {content: \"▾\";}#sk-container-id-6 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-6 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-6 input.sk-hidden--visually {border: 0;clip: rect(1px 1px 1px 1px);clip: rect(1px, 1px, 1px, 1px);height: 1px;margin: -1px;overflow: hidden;padding: 0;position: absolute;width: 1px;}#sk-container-id-6 div.sk-estimator {font-family: monospace;background-color: #f0f8ff;border: 1px dotted black;border-radius: 0.25em;box-sizing: border-box;margin-bottom: 0.5em;}#sk-container-id-6 div.sk-estimator:hover {background-color: #d4ebff;}#sk-container-id-6 div.sk-parallel-item::after {content: \"\";width: 100%;border-bottom: 1px solid gray;flex-grow: 1;}#sk-container-id-6 div.sk-label:hover label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-6 div.sk-serial::before {content: \"\";position: absolute;border-left: 1px solid gray;box-sizing: border-box;top: 0;bottom: 0;left: 50%;z-index: 0;}#sk-container-id-6 div.sk-serial {display: flex;flex-direction: column;align-items: center;background-color: white;padding-right: 0.2em;padding-left: 0.2em;position: relative;}#sk-container-id-6 div.sk-item {position: relative;z-index: 1;}#sk-container-id-6 div.sk-parallel {display: flex;align-items: stretch;justify-content: center;background-color: white;position: relative;}#sk-container-id-6 div.sk-item::before, #sk-container-id-6 div.sk-parallel-item::before {content: \"\";position: absolute;border-left: 1px solid gray;box-sizing: border-box;top: 0;bottom: 0;left: 50%;z-index: -1;}#sk-container-id-6 div.sk-parallel-item {display: flex;flex-direction: column;z-index: 1;position: relative;background-color: white;}#sk-container-id-6 div.sk-parallel-item:first-child::after {align-self: flex-end;width: 50%;}#sk-container-id-6 div.sk-parallel-item:last-child::after {align-self: flex-start;width: 50%;}#sk-container-id-6 div.sk-parallel-item:only-child::after {width: 0;}#sk-container-id-6 div.sk-dashed-wrapped {border: 1px dashed gray;margin: 0 0.4em 0.5em 0.4em;box-sizing: border-box;padding-bottom: 0.4em;background-color: white;}#sk-container-id-6 div.sk-label label {font-family: monospace;font-weight: bold;display: inline-block;line-height: 1.2em;}#sk-container-id-6 div.sk-label-container {text-align: center;}#sk-container-id-6 div.sk-container {/* jupyter's `normalize.less` sets `[hidden] { display: none; }` but bootstrap.min.css set `[hidden] { display: none !important; }` so we also need the `!important` here to be able to override the default hidden behavior on the sphinx rendered scikit-learn.org. See: https://github.com/scikit-learn/scikit-learn/issues/21755 */display: inline-block !important;position: relative;}#sk-container-id-6 div.sk-text-repr-fallback {display: none;}</style><div id=\"sk-container-id-6\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>KMeans(n_clusters=2, n_init='auto', random_state=42)</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item\"><div class=\"sk-estimator sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-6\" type=\"checkbox\" checked><label for=\"sk-estimator-id-6\" class=\"sk-toggleable__label sk-toggleable__label-arrow\">KMeans</label><div class=\"sk-toggleable__content\"><pre>KMeans(n_clusters=2, n_init='auto', random_state=42)</pre></div></div></div></div></div>"
|
||
],
|
||
"text/plain": [
|
||
"KMeans(n_clusters=2, n_init='auto', random_state=42)"
|
||
]
|
||
},
|
||
"execution_count": 40,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"# prepare data for clustering\n",
|
||
"from sklearn.decomposition import PCA\n",
|
||
"from sklearn.preprocessing import StandardScaler\n",
|
||
"from sklearn.cluster import KMeans\n",
|
||
"from sklearn.metrics import confusion_matrix\n",
|
||
"import matplotlib.pyplot as plt\n",
|
||
"import numpy as np\n",
|
||
"\n",
|
||
"# prepare model KMeans\n",
|
||
"kmeans = KMeans(n_clusters=2, random_state=42, n_init='auto')\n",
|
||
"kmeans.fit(X)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 42,
|
||
"id": "98eb04bb-e1f2-43e2-a18f-8c4c6c5dc788",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"50.2% der Datensätze wurden mithilfe von KMeans richtig einem Cluster zugeordnet\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"# calculate percentage of data points correctly assigned to each cluster\n",
|
||
"cluster1 = kmeans.labels_ == 0\n",
|
||
"cluster2 = kmeans.labels_ == 1\n",
|
||
"\n",
|
||
"perc_cluster1 = np.round(np.mean(cluster1 == y) * 100, decimals=2)\n",
|
||
"perc_cluster2 = np.round(np.mean(cluster2 == y) * 100, decimals=2)\n",
|
||
"\n",
|
||
"# choose cluster with higher correspondence\n",
|
||
"if perc_cluster1 > perc_cluster2:\n",
|
||
" km_healthy = cluster1\n",
|
||
" max_perc = perc_cluster1\n",
|
||
"else:\n",
|
||
" km_healthy = cluster2\n",
|
||
" max_perc = perc_cluster2\n",
|
||
"\n",
|
||
"print(f\"{max_perc}% der Datensätze wurden mithilfe von KMeans richtig einem Cluster zugeordnet\")\n",
|
||
"\n",
|
||
"# hier vlt noch irgendwie diskutieren ob das ein smart way ist um das auszuwerten, anscheinend gibt's dafür andere Metriken"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 44,
|
||
"id": "e622bdca-9518-4483-8f76-9b0613b2d055",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Proportion of variance explained by each principal component:\n",
|
||
"[2.34198813e-01 1.25628556e-01 1.09931362e-01 8.74811618e-02\n",
|
||
" 7.82747684e-02 6.31208837e-02 6.24229494e-02 5.34948492e-02\n",
|
||
" 4.17139647e-02 3.17012077e-02 2.52492654e-02 2.21354486e-02\n",
|
||
" 1.84895571e-02 1.74748048e-02 8.28895271e-03 5.47222590e-03\n",
|
||
" 4.87868838e-03 3.91078109e-03 3.44014667e-03 2.69161359e-03\n",
|
||
" 6.65456914e-32 1.02213094e-32 7.63880223e-33 7.57571809e-33\n",
|
||
" 5.61957590e-33 3.94708724e-33 2.10583548e-33 3.10679768e-34]\n"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"image/png": "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lyVLluj333+3OeUmy6RJk1S3bl0VLVpUxYoVU/Xq1fXss8/muj5gBwPYadq0aUaS+emnn2zib775ppFkpkyZYowxpmfPnsbf399UqFDBjBkzxqxcudJ888035vz586ZOnTomJCTEvPbaa2bZsmVmxIgRxs/Pz9x88802OSWZcuXKmZo1a5rZs2ebRYsWmZtuuslIMp999pl13J49e0yxYsVMpUqVzIwZM8ySJUvMvffeaySZcePGWcetWrXKSDJly5Y1d911l1m0aJFZvHixSUpKMi+//LKRZN59912zYcMGs2HDBnP06FHrXGJjY615MjMzTYcOHYyfn58ZMWKEWbZsmXnttddMSEiIqV+/vrlw4YJ1bGxsrImJiTE1a9Y0M2bMMN988425++67jSSzZs2afJe1PcvqwoULZsOGDaZ+/fqmYsWK1tpTUlLyzDt8+HAjyWzdutUm/vTTTxtJZvfu3cYYYyZNmmTGjBljFi1aZNasWWM++ugjU7duXVOtWjWTlpZmfVx8fLyRZGJjY82QIUPM8uXLzYIFC2zuu9ILL7xg3njjDbNkyRKzevVqM3nyZBMXF2datWplM65nz54mICDA1KhRw7z22mtmxYoV5vnnnzcWi8WMGjXKOu7SpUumVatWxs/Pzzz11FNm6dKlZtGiRebZZ581s2fPto576KGHjL+/v3nyySfN119/bT755BNTvXp1ExkZaZKSkvJdF1mv+x9++MFcunTJ5paenm4ztl+/fiYgIMC6jaxcudL4+PiY4cOHW8ccOHDA+vq+4YYbzLx588xnn31mGjdubPz9/c369etzPPeBAwesMXvXTW6PbdmypQkLCzNVqlQxkydPNsuXLzf9+/c3ksxHH31kHXf27FlTr149Ex4ebsaPH29WrFhh3nzzTRMaGmpat25tMjMzjTGXt4dWrVqZwMBA89JLL5lly5aZ+Ph4U7FiRSPJxMfH57tss7bLcuXKmS5dupgvv/zSfPzxx6Zy5cqmePHiZt++fcYYYy5evGiioqLM/fffb/P4S5cumejoaHP33Xfn+zyu7ruy7rtyX2DM5f1UhQoVzLXXXms+/fRTs3TpUnPjjTcaPz8/a+3GGLN161ZTtGhRU6FCBTN58mSzcuVK8/HHH5uuXbua1NRUm2WxatUq6+OytoPy5ctbl+/IkSONn5+fueWWW2xqGTRokJk0aZL5+uuvzbfffmveeOMNEx4ebh588EGbcbnNI7v169cbSWbIkCH5jsvi6L49NjbWNG3a1MyfP9988cUXpmrVqqZUqVJm0KBBpkuXLmbx4sVm1qxZJjIy0tSpU8f6ejPm79dwdHS0eeutt8w333xjnnjiCSPJPProo07XZM963LVrlwkNDTW1a9c2M2bMMMuWLTNPPvmk8fHxMSNHjrSOy1qXFSpUMPfff79ZsmSJmT17tilfvrypUqWKdb+xa9cu06xZMxMVFWXdf2/YsMEYY8zs2bONJPP444+bZcuWmRUrVpjJkyebJ554wq51gpxoeGG37G/8p0+fNosXLzYRERGmWLFi1sahZ8+eRpKZOnWqzeMnT55sJJlPP/3UJj5u3DgjySxbtswak2SCg4NtmpH09HRTvXp1U7lyZWvsnnvuMYGBgebQoUM2OTt27GiKFCliTp06ZYz5ewfUokWLHPP67LPPcrzRZMn+5vD1118bSeaVV16xGTd37lybN05jLje8QUFB5vfff7fGzp8/b0qVKmX+97//5XiuKzmyrFq2bGmuueaafPNl2b9/v7FYLDY7zUuXLpmoqCjTrFmzXB+TmZlpLl26ZH7//XcjySxcuNB6X1ZT+/zzz+d4XG4Nb25516xZYySZbdu2We/Leg1ln//NN99sqlWrZv19xowZRpJ5//3383yeDRs2GEnm9ddft4n/8ccfJjg42DzzzDN5PtaYv1/3ud18fX1txl64cMHUr1/fxMXFmZ9//tlERkaali1b2jTGWQ1vdHS0OX/+vDWemppqSpUqZdq2bZvjua9sWq+U37rJq+GVZDZu3GiTp2bNmqZDhw7W38eMGWN8fHxyNIiff/65kWSWLl1qjDHmq6++MpLMm2++aTPupZdecqjhbdCggU1Tc/DgQePv72/69u1rjcXHx5uAgADz119/WWNZ211Bf0C6uu/Kui+3hjcyMtLatBpjTFJSkvHx8TFjxoyxxlq3bm1KlChh/UM6v2WRveHNb/muW7cu11wZGRnm0qVLZsaMGcbX19ecOHEi33lkN2fOHCPJTJ48Od9xWRzdt0dFRZkzZ85YYwsWLDCSTL169WxeBxMmTDCSzPbt262xrNfwla91Yy7/Uevj42Pd3zpakz3rsUOHDiYmJibHQYXHHnvMBAUFWZdz1rrM3lh/+umnRpK1qTXGmE6dOuW6Ph577DFTokSJHHE4j1Ma4LDrr79e/v7+KlasmG655RZFRUXpq6++UmRkpM24O++80+b3b7/9ViEhIbrrrrts4r169ZKkHP/ObdOmjU1OX19fdevWTb/99pv+/PNPa842bdqoXLlyOXKeO3cux79As9fkqG+//dam5ix33323QkJCcsyhXr16Kl++vPX3oKAgVa1a1fov1Pyex5FlZa+4uDi1atVKs2bNUlpamiTpq6++UlJSknr37m0dd/ToUfXr10/lypWTn5+f/P39FRsbK+ny6Q/Z2btc9+/fr/vuu09RUVHy9fWVv7+/WrZsmWtei8WiW2+91SZWp04dm2X31VdfKSgoyKb27BYvXiyLxaIHHnhA6enp1ltUVJTq1q1r96fiZ8yYoZ9++snmtnHjRpsxgYGB+vTTT5WcnKwGDRrIGKPZs2fL19c3R7477rhDQUFB1t+LFSumW2+9VWvXrs3xr9krObpusouKitK1115rE8u+XBcvXqxatWqpXr16NsusQ4cONv92X7VqlSTlOP/1vvvuK7CO7OOv/FdubGysmjZtas0vSY888ogk6f3337fG3nnnHdWuXVstWrSw63mc3Xflp1WrVipWrJj198jISJUuXdq6PM+dO6c1a9aoa9euTp9TndfyvXL5JCQkqHPnzgoLC7NuWz169FBGRobDp5g4ytH9VatWrWw+JFijRg1JUseOHW1eB1nx7PvLYsWK5biazH333afMzEytXbvW6ZryW48XLlzQypUrdfvtt6tIkSI228XNN9+sCxcu6IcffrDJmb3GOnXq5Dqf3Fx77bU6deqU7r33Xi1cuDDHKUtwHB9ag8NmzJihGjVqyM/PT5GRkbl+CrxIkSIqXry4TSw5OVlRUVE5zussXbq0/Pz8lJycbBOPiorKkTcrlpycrJiYGCUnJ+f6/NHR0dZxV3L1E+vJycny8/PL8cZlsVgUFRWV4/nCwsJy5AgMDNT58+cLfB5HlpUj+vTpo/vvv1+LFi3SXXfdpWnTpqlo0aLq2rWrJCkzM1Pt27fXkSNHNGLECNWuXVshISHKzMzU9ddfn2vt9izXM2fOqHnz5goKCtKLL76oqlWrqkiRIvrjjz90xx135MhbpEgRm4ZQurzsLly4YP392LFjio6Olo9P3n+7//XXXzLG5GhqslSsWLHA2qXLb772fGitcuXKat68uZYsWaJHHnkkz2WT1+s7LS1NZ86cUWhoaI77nVk32dnzmvzrr7/022+/yd/fP9ccWW++WdtD9py5zS0/eS2Lbdu2WX+PjIxUt27d9N5772no0KHatWuXvvvuO7333nt2P4+z+678FLQ8T548qYyMDKc/wJnf8s3aDxw6dEjNmzdXtWrV9Oabb6pChQoKCgrSjz/+qEcffdSu18WVsv5IP3DggF3jHd1fZb+STEBAQL7xK7d5Sbluy9mXiaM1FbQek5OTlZ6errfffltvv/12jrGScjSl2XMGBgZKkl3ro3v37kpPT9f777+vO++8U5mZmWrcuLFefPFFtWvXrsDHIycaXjjMnjf+3K5+EBYWpo0bN8oYY3P/0aNHlZ6ervDwcJvxSUlJOXJkxbJ2JGFhYUpMTMwxLusDLNlzunpd2LCwMKWnp+vYsWM2Ta8xRklJSWrcuLFL+a98HkeWlSPuuOMOlSxZUlOnTlXLli21ePFi9ejRQ0WLFpV0+UOA27Zt0/Tp09WzZ0/r4/L7RLY9y/Xbb7/VkSNHtHr1autRXUk5PuzniIiICK1bt06ZmZl5Nr3h4eGyWCz67rvvrG84V8ot5ooPPvhAS5Ys0bXXXqt33nlH3bp103XXXZdjXF6v74CAAOu6yM6ZdeOM8PBwBQcH5/kBy6zXX9b2kJycbPPmntvc8pPXssjeMAwYMEAzZ87UwoUL9fXXX6tEiRIFXp3kSs7uu1xRqlQp+fr6Wv8r5aj8lm9WbMGCBTp79qzmz59vPdovSVu3bnXqORs1aqRSpUpp4cKFGjNmTIHLxJP7q9z89ddfOWK5vTe4s6aSJUvK19dX3bt316OPPprrmLi4OIdyFuTBBx/Ugw8+qLNnz2rt2rWKj4/XLbfcol9//dVmPcM+nNKAf0ybNm105swZLViwwCY+Y8YM6/1XWrlypc2OLSMjQ3PnzlWlSpWsR0vatGljbaSy5yxSpIhdl/lx5K/urBo//vhjm/i8efN09uzZHHNwlqPLyhFBQUG67777tGzZMo0bN06XLl2yOSUg680heyPoyJG03Hgib8eOHXXhwgXrVTVyc8stt8gYo8OHD6tRo0Y5brVr13b6+bPbsWOHnnjiCfXo0UPfffed6tSpo27duunkyZM5xs6fP9/myNXp06f15Zdfqnnz5rmeAiF5bt1kd8stt2jfvn0KCwvLdZllXa2gVatWkqRZs2bZPP6TTz5x6Plmz54tY4z1999//13r16/XjTfeaDOuYcOGatq0qcaNG6dZs2apV69eV/21v7OuOvHZZ585/W/pvJZv1vLJ7XVhjLE5/cMR/v7+GjJkiPbs2aMXXngh1zFHjx7V999/L8mz+6vcnD59WosWLbKJffLJJ/Lx8bGe3uLumooUKaJWrVopISFBderUyXW7yO0ocUHs+Y9fSEiIOnbsqOeee05paWnatWuXw88DjvDiH9SjRw+9++676tmzpw4ePKjatWtr3bp1evnll3XzzTerbdu2NuPDw8PVunVrjRgxQiEhIZo4caL27Nljc2my+Ph4LV68WK1atdLzzz+vUqVKadasWVqyZIleeeWVXP8tnF2tWrUkSVOmTFGxYsUUFBSkuLi4XHde7dq1U4cOHTRkyBClpqaqWbNm2r59u+Lj41W/fn11797dxaV0maPLylF9+vTRu+++q/Hjx6t69epq2rSp9b7q1aurUqVKGjp0qIwxKlWqlL788kvrZbWc1bRpU5UsWVL9+vVTfHy8/P39NWvWLJt/Wzvq3nvv1bRp09SvXz/98ssvatWqlTIzM7Vx40bVqFFD99xzj5o1a6aHH35YDz74oDZt2qQWLVooJCREiYmJWrdunWrXrm09PzQ/O3fuVHp6eo54pUqVFBERobNnz6pr166Ki4vTxIkTFRAQoE8//VQNGjTQgw8+mOON19fXV+3atdPgwYOVmZmpcePGKTU1VaNGjcqzBk+tm+wGDhyoefPmqUWLFho0aJDq1KmjzMxMHTp0SMuWLdOTTz6p6667Tu3bt1eLFi30zDPP6OzZs2rUqJG+//57zZw506HnO3r0qG6//XY99NBDSklJUXx8vIKCgjRs2LAcYwcMGKBu3brJYrGof//+7pqyR40fP1433HCDrrvuOg0dOlSVK1fWX3/9pUWLFum9996zOXc0u4CAAL3++us6c+aMGjdurPXr1+vFF19Ux44drZcFa9eunQICAnTvvffqmWee0YULFzRp0qRc/9Cy19NPP63du3crPj5eP/74o+677z7rF0+sXbtWU6ZM0ahRo9SsWTOP76+yCwsL0yOPPKJDhw6patWqWrp0qd5//3098sgj1tMxPFHTm2++qRtuuEHNmzfXI488ogoVKuj06dP67bff9OWXX1o/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OXPbbiwWi1599VWn8p05c0aPPfaYYmJiFBwcrBo1amjSpElO1/fXX3+pV69eio6OVpEiRXTTTTdp7969eeazZx/tyLZiTz5HtpWC8jmzrdhToyPbiqPvcwVtK/bkc3RbsbdGe7cXe/I5sq3Yk8/RbeVqQMN7lTt79qzq1q2rd955x+Vca9as0aOPPqoffvhBy5cvV3p6utq3b6+zZ886nTMmJkZjx47Vpk2btGnTJrVu3VpdunTJt1mx108//aQpU6aoTp06LuW55pprlJiYaL3t2LHDpXwnT55Us2bN5O/vr6+++ko///yzXn/9dae/ne+nn36yqS/ri1Puvvtup/KNGzdOkydP1jvvvKPdu3frlVde0auvvqq3337bqXyS1LdvXy1fvlwzZ87Ujh071L59e7Vt21aHDx+26/EFvY5feeUVjR8/Xu+8845++uknRUVFqV27djp9+rRT+c6ePatmzZpp7NixLtd37tw5bdmyRSNGjNCWLVs0f/58/frrr+rcubPTOSWpatWqeuedd7Rjxw6tW7dOFSpUUPv27XXs2DGn8mVZsGCBNm7cWOBXbdqT76abbrJ5bS5dutTpfPv27dMNN9yg6tWra/Xq1dq2bZtGjBihoKAgp3NeWVtiYqKmTp0qi8Vic6lKR/INGjRIX3/9tT7++GPt3r1bgwYN0uOPP66FCxc6nM8Yo9tuu0379+/XwoULlZCQoNjYWLVt2zbPfa49+2hHthV78jmyrRSUz5ltxZ4aHdlWHHmfs2dbsTefI9uKPTkd2V7syefItmJPPke3lauCwb+GJPPFF1+4Ld/Ro0eNJLNmzRq35TTGmJIlS5oPPvjApRynT582VapUMcuXLzctW7Y0AwYMcCpPfHy8qVu3rku1ZDdkyBBzww03uDXnlQYMGGAqVapkMjMznXp8p06dTO/evW1id9xxh3nggQecynfu3Dnj6+trFi9ebBOvW7euee655xzOl/11nJmZaaKioszYsWOtsQsXLpjQ0FAzefJkh/Nd6cCBA0aSSUhIcLq+3Pz4449Gkvn999/dljMlJcVIMitWrHA6359//mnKli1rdu7caWJjY80bb7zhdH09e/Y0Xbp0sevx9uTr1q2b06/BvHJm16VLF9O6dWun811zzTVm9OjRNrEGDRqY4cOHO5zvl19+MZLMzp07rbH09HRTqlQp8/7779tVY/Z9tKvbSn77fGe2FXveQxzdVuzJ6ci2klc+Z7eV3PK5sq3kldOV7cWeZejItpJbPle2lcLCEd7/sJSUFElSqVKl3JIvIyNDc+bM0dmzZ9WkSROXcj366KPq1KmT2rZt63Jde/fuVXR0tOLi4nTPPfdo//79LuVbtGiRGjVqpLvvvlulS5dW/fr19f7777tcpySlpaXp448/Vu/evWWxWJzKccMNN2jlypX69ddfJUnbtm3TunXrdPPNNzuVLz09XRkZGTmOLAQHB7vlGwwPHDigpKQktW/f3hoLDAxUy5YttX79epfze0JKSoosFovTR/WzS0tL05QpUxQaGqq6des6lSMzM1Pdu3fX008/rWuuucYtda1evVqlS5dW1apV9dBDD+no0aNO17ZkyRJVrVpVHTp0UOnSpXXddde5dIpWdn/99ZeWLFmiPn36OJ3jhhtu0KJFi3T48GEZY7Rq1Sr9+uuv6tChg8O5Ll68KEk2242vr68CAgLs3m6y76Nd3Vbcvc+3J5+j20pBOR3dVnLL58q2kld9rmwr2XO6ur0UtAwd3VZyy+fObeUfU9gdN+wnNx7hzczMNLfeeqtbjlRu377dhISEGF9fXxMaGmqWLFniUr7Zs2ebWrVqmfPnzxtjjEtHeJcuXWo+//xzs337duvR4sjISHP8+HGn6wsMDDSBgYFm2LBhZsuWLWby5MkmKCjIfPTRR07nzDJ37lzj6+trDh8+7HSOzMxMM3ToUGOxWIyfn5+xWCzm5ZdfdqmuJk2amJYtW5rDhw+b9PR0M3PmTGOxWEzVqlUdzpX9dfz9998bSTnm/NBDD5n27ds7nO9KnjjCe/78edOwYUNz//33u5zzyy+/NCEhIcZisZjo6Gjz448/Op3v5ZdfNu3atbP+Z8DVI7xz5swxixcvNjt27DCLFi0ydevWNddcc425cOGCw/kSExONJFOkSBEzfvx4k5CQYMaMGWMsFotZvXq10zVeady4caZkyZLW/YYz+S5evGh69OhhJBk/Pz8TEBBgZsyY4VS+tLQ0Exsba+6++25z4sQJc/HiRTNmzBgjya7XdW77aFe2lYL2+Y5uK/a8hzi6reSX05ltJa98zm4reeVzZVvJLacr24s968WRbSWvfK5sK4WFhvdfxJ0Nb//+/U1sbKz5448/XM518eJFs3fvXvPTTz+ZoUOHmvDwcLNr1y6nch06dMiULl3abN261RpzpeHN7syZMyYyMtK8/vrrTufw9/c3TZo0sYk9/vjj5vrrr3e1PNO+fXtzyy23uJRj9uzZJiYmxsyePdts377dzJgxw5QqVcpMnz7d6Zy//fabadGihZFkfH19TePGjc39999vatSo4XCuvBreI0eO2Izr27ev6dChg8P5ruTuhjctLc106dLF1K9f36SkpLic88yZM2bv3r1mw4YNpnfv3qZChQrmr7/+cjjfpk2bTGRkpE0j5GrDm92RI0eMv7+/mTdvnsP5Dh8+bCSZe++912bcrbfeau655x631FitWjXz2GOP2ZUrr3yvvvqqqVq1qlm0aJHZtm2befvtt03RokXN8uXLncq3adMmU7duXet206FDB9OxY0fTsWPHAvPlto92ZVspaJ/v6LZSUD5ntpX8cjqzreSWz5Vtxd73TUe2ldxyurK92FOjI9tKXvlc2VYKCw3vv4i7Gt7HHnvMxMTEmP3797teVC7atGljHn74Yace+8UXX1jfHLJukozFYjG+vr4mPT3d5fratm1r+vXr5/Tjy5cvb/r06WMTmzhxoomOjnaproMHDxofHx+zYMECl/LExMSYd955xyb2wgsvmGrVqrmU15jLbzpZb7Zdu3Y1N998s8M5sr+O9+3bZySZLVu22Izr3Lmz6dGjh8P5ruTOhjctLc3cdtttpk6dOg7/h8Debbdy5cp2HY3Pnu+NN96wbiNXbjc+Pj4mNjbWrfVdef6ovfkuXrxo/Pz8zAsvvGAz7plnnjFNmzYtMF9BNa5du9ZIsvlD2dF8586dM/7+/jnOVe/Tp4/Lf3idOnXKHD161BhjzLXXXmv69++fb6689tHObiv27PMd2VYKyufMtuLo+1JB20pe+ZzdVpypr6BtJa+czm4v9tToyLaSVz5Xt5XCwjm8/yHGGD322GOaP3++vv32W8XFxXnsebLOX3NUmzZttGPHDm3dutV6a9Soke6//35t3bpVvr6+LtV28eJF7d69W2XKlHE6R7NmzXJcouXXX39VbGysS7VNmzZNpUuXVqdOnVzKc+7cOfn42G7avr6+Ll2WLEtISIjKlCmjkydP6ptvvlGXLl1czhkXF6eoqCjr1Smky+fprVmzRk2bNnU5vztcunRJXbt21d69e7VixQqFhYV55Hmc3Xa6d++u7du3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",
|
||
"text/plain": [
|
||
"<Figure size 800x600 with 1 Axes>"
|
||
]
|
||
},
|
||
"metadata": {},
|
||
"output_type": "display_data"
|
||
}
|
||
],
|
||
"source": [
|
||
"# apply PCA\n",
|
||
"pca = PCA()\n",
|
||
"pca.fit(X)\n",
|
||
"\n",
|
||
"print(f\"Proportion of variance explained by each principal component:\\n{pca.explained_variance_ratio_}\")\n",
|
||
"\n",
|
||
"# Plot the proportion of variance explained\n",
|
||
"plt.figure(figsize=(8, 6))\n",
|
||
"plt.bar(range(1, len(pca.explained_variance_ratio_) + 1), pca.explained_variance_ratio_, color='skyblue')\n",
|
||
"plt.xlabel('Principal Component')\n",
|
||
"plt.ylabel('Proportion of Variance Explained')\n",
|
||
"plt.title('Proportion of Variance Explained by Principal Components')\n",
|
||
"plt.xticks(range(1, len(pca.explained_variance_ratio_) + 1))\n",
|
||
"plt.grid(axis='y', linestyle='--', alpha=0.7)\n",
|
||
"plt.show()"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 47,
|
||
"id": "6e850f89-f6ba-4cce-8203-1e307e172505",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Contributions of features to the first principal component:\n",
|
||
" Feature Contribution\n",
|
||
"17 exang_1 0.332492\n",
|
||
"23 thal_7.0 0.331968\n",
|
||
"10 cp_4 0.328138\n",
|
||
"19 slope_2 0.272838\n",
|
||
"6 sex_1 0.196119\n",
|
||
"15 restecg_2 0.131840\n",
|
||
"25 ca_1.0 0.107372\n",
|
||
"4 oldpeak 0.094386\n",
|
||
"26 ca_2.0 0.078705\n",
|
||
"0 age 0.046441\n",
|
||
"27 ca_3.0 0.046405\n",
|
||
"22 thal_6.0 0.041738\n",
|
||
"1 trestbps 0.020995\n",
|
||
"20 slope_3 0.020165\n",
|
||
"12 fbs_1 0.013534\n",
|
||
"2 chol 0.005950\n",
|
||
"14 restecg_1 0.004777\n",
|
||
"7 cp_1 -0.009925\n",
|
||
"11 fbs_0 -0.013534\n",
|
||
"3 thalach -0.092913\n",
|
||
"13 restecg_0 -0.136618\n",
|
||
"8 cp_2 -0.139917\n",
|
||
"9 cp_3 -0.178297\n",
|
||
"5 sex_0 -0.196119\n",
|
||
"24 ca_0.0 -0.232482\n",
|
||
"18 slope_1 -0.293003\n",
|
||
"16 exang_0 -0.332492\n",
|
||
"21 thal_3.0 -0.373706\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"# get the loadings or weights of features in the first principal component\n",
|
||
"first_pc_loadings = pca.components_[0]\n",
|
||
"\n",
|
||
"# create a DataFrame to display the contributions of features to the first principal component\n",
|
||
"pc_loadings_df = pd.DataFrame({\"Feature\": X.columns, \"Contribution\": first_pc_loadings})\n",
|
||
"pc_loadings_df = pc_loadings_df.sort_values(by=\"Contribution\", ascending=False)\n",
|
||
"\n",
|
||
"print(\"Contributions of features to the first principal component:\")\n",
|
||
"print(pc_loadings_df)"
|
||
]
|
||
}
|
||
],
|
||
"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.5"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 5
|
||
}
|