DSA_SoSe_24/Experiments.ipynb

1245 lines
84 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

{
"cells": [
{
"cell_type": "code",
"execution_count": 8,
"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": 10,
"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": 10,
"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": 12,
"id": "79631688-07cb-450d-9958-8d8341722d7d",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<style>#sk-container-id-1 {\n",
" /* Definition of color scheme common for light and dark mode */\n",
" --sklearn-color-text: black;\n",
" --sklearn-color-line: gray;\n",
" /* Definition of color scheme for unfitted estimators */\n",
" --sklearn-color-unfitted-level-0: #fff5e6;\n",
" --sklearn-color-unfitted-level-1: #f6e4d2;\n",
" --sklearn-color-unfitted-level-2: #ffe0b3;\n",
" --sklearn-color-unfitted-level-3: chocolate;\n",
" /* Definition of color scheme for fitted estimators */\n",
" --sklearn-color-fitted-level-0: #f0f8ff;\n",
" --sklearn-color-fitted-level-1: #d4ebff;\n",
" --sklearn-color-fitted-level-2: #b3dbfd;\n",
" --sklearn-color-fitted-level-3: cornflowerblue;\n",
"\n",
" /* Specific color for light theme */\n",
" --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
" --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
" --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
" --sklearn-color-icon: #696969;\n",
"\n",
" @media (prefers-color-scheme: dark) {\n",
" /* Redefinition of color scheme for dark theme */\n",
" --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
" --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
" --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
" --sklearn-color-icon: #878787;\n",
" }\n",
"}\n",
"\n",
"#sk-container-id-1 {\n",
" color: var(--sklearn-color-text);\n",
"}\n",
"\n",
"#sk-container-id-1 pre {\n",
" padding: 0;\n",
"}\n",
"\n",
"#sk-container-id-1 input.sk-hidden--visually {\n",
" border: 0;\n",
" clip: rect(1px 1px 1px 1px);\n",
" clip: rect(1px, 1px, 1px, 1px);\n",
" height: 1px;\n",
" margin: -1px;\n",
" overflow: hidden;\n",
" padding: 0;\n",
" position: absolute;\n",
" width: 1px;\n",
"}\n",
"\n",
"#sk-container-id-1 div.sk-dashed-wrapped {\n",
" border: 1px dashed var(--sklearn-color-line);\n",
" margin: 0 0.4em 0.5em 0.4em;\n",
" box-sizing: border-box;\n",
" padding-bottom: 0.4em;\n",
" background-color: var(--sklearn-color-background);\n",
"}\n",
"\n",
"#sk-container-id-1 div.sk-container {\n",
" /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
" but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
" so we also need the `!important` here to be able to override the\n",
" default hidden behavior on the sphinx rendered scikit-learn.org.\n",
" See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
" display: inline-block !important;\n",
" position: relative;\n",
"}\n",
"\n",
"#sk-container-id-1 div.sk-text-repr-fallback {\n",
" display: none;\n",
"}\n",
"\n",
"div.sk-parallel-item,\n",
"div.sk-serial,\n",
"div.sk-item {\n",
" /* draw centered vertical line to link estimators */\n",
" background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
" background-size: 2px 100%;\n",
" background-repeat: no-repeat;\n",
" background-position: center center;\n",
"}\n",
"\n",
"/* Parallel-specific style estimator block */\n",
"\n",
"#sk-container-id-1 div.sk-parallel-item::after {\n",
" content: \"\";\n",
" width: 100%;\n",
" border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
" flex-grow: 1;\n",
"}\n",
"\n",
"#sk-container-id-1 div.sk-parallel {\n",
" display: flex;\n",
" align-items: stretch;\n",
" justify-content: center;\n",
" background-color: var(--sklearn-color-background);\n",
" position: relative;\n",
"}\n",
"\n",
"#sk-container-id-1 div.sk-parallel-item {\n",
" display: flex;\n",
" flex-direction: column;\n",
"}\n",
"\n",
"#sk-container-id-1 div.sk-parallel-item:first-child::after {\n",
" align-self: flex-end;\n",
" width: 50%;\n",
"}\n",
"\n",
"#sk-container-id-1 div.sk-parallel-item:last-child::after {\n",
" align-self: flex-start;\n",
" width: 50%;\n",
"}\n",
"\n",
"#sk-container-id-1 div.sk-parallel-item:only-child::after {\n",
" width: 0;\n",
"}\n",
"\n",
"/* Serial-specific style estimator block */\n",
"\n",
"#sk-container-id-1 div.sk-serial {\n",
" display: flex;\n",
" flex-direction: column;\n",
" align-items: center;\n",
" background-color: var(--sklearn-color-background);\n",
" padding-right: 1em;\n",
" padding-left: 1em;\n",
"}\n",
"\n",
"\n",
"/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
"clickable and can be expanded/collapsed.\n",
"- Pipeline and ColumnTransformer use this feature and define the default style\n",
"- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
"*/\n",
"\n",
"/* Pipeline and ColumnTransformer style (default) */\n",
"\n",
"#sk-container-id-1 div.sk-toggleable {\n",
" /* Default theme specific background. It is overwritten whether we have a\n",
" specific estimator or a Pipeline/ColumnTransformer */\n",
" background-color: var(--sklearn-color-background);\n",
"}\n",
"\n",
"/* Toggleable label */\n",
"#sk-container-id-1 label.sk-toggleable__label {\n",
" cursor: pointer;\n",
" display: block;\n",
" width: 100%;\n",
" margin-bottom: 0;\n",
" padding: 0.5em;\n",
" box-sizing: border-box;\n",
" text-align: center;\n",
"}\n",
"\n",
"#sk-container-id-1 label.sk-toggleable__label-arrow:before {\n",
" /* Arrow on the left of the label */\n",
" content: \"▸\";\n",
" float: left;\n",
" margin-right: 0.25em;\n",
" color: var(--sklearn-color-icon);\n",
"}\n",
"\n",
"#sk-container-id-1 label.sk-toggleable__label-arrow:hover:before {\n",
" color: var(--sklearn-color-text);\n",
"}\n",
"\n",
"/* Toggleable content - dropdown */\n",
"\n",
"#sk-container-id-1 div.sk-toggleable__content {\n",
" max-height: 0;\n",
" max-width: 0;\n",
" overflow: hidden;\n",
" text-align: left;\n",
" /* unfitted */\n",
" background-color: var(--sklearn-color-unfitted-level-0);\n",
"}\n",
"\n",
"#sk-container-id-1 div.sk-toggleable__content.fitted {\n",
" /* fitted */\n",
" background-color: var(--sklearn-color-fitted-level-0);\n",
"}\n",
"\n",
"#sk-container-id-1 div.sk-toggleable__content pre {\n",
" margin: 0.2em;\n",
" border-radius: 0.25em;\n",
" color: var(--sklearn-color-text);\n",
" /* unfitted */\n",
" background-color: var(--sklearn-color-unfitted-level-0);\n",
"}\n",
"\n",
"#sk-container-id-1 div.sk-toggleable__content.fitted pre {\n",
" /* unfitted */\n",
" background-color: var(--sklearn-color-fitted-level-0);\n",
"}\n",
"\n",
"#sk-container-id-1 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
" /* Expand drop-down */\n",
" max-height: 200px;\n",
" max-width: 100%;\n",
" overflow: auto;\n",
"}\n",
"\n",
"#sk-container-id-1 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
" content: \"▾\";\n",
"}\n",
"\n",
"/* Pipeline/ColumnTransformer-specific style */\n",
"\n",
"#sk-container-id-1 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
" color: var(--sklearn-color-text);\n",
" background-color: var(--sklearn-color-unfitted-level-2);\n",
"}\n",
"\n",
"#sk-container-id-1 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
" background-color: var(--sklearn-color-fitted-level-2);\n",
"}\n",
"\n",
"/* Estimator-specific style */\n",
"\n",
"/* Colorize estimator box */\n",
"#sk-container-id-1 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
" /* unfitted */\n",
" background-color: var(--sklearn-color-unfitted-level-2);\n",
"}\n",
"\n",
"#sk-container-id-1 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
" /* fitted */\n",
" background-color: var(--sklearn-color-fitted-level-2);\n",
"}\n",
"\n",
"#sk-container-id-1 div.sk-label label.sk-toggleable__label,\n",
"#sk-container-id-1 div.sk-label label {\n",
" /* The background is the default theme color */\n",
" color: var(--sklearn-color-text-on-default-background);\n",
"}\n",
"\n",
"/* On hover, darken the color of the background */\n",
"#sk-container-id-1 div.sk-label:hover label.sk-toggleable__label {\n",
" color: var(--sklearn-color-text);\n",
" background-color: var(--sklearn-color-unfitted-level-2);\n",
"}\n",
"\n",
"/* Label box, darken color on hover, fitted */\n",
"#sk-container-id-1 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
" color: var(--sklearn-color-text);\n",
" background-color: var(--sklearn-color-fitted-level-2);\n",
"}\n",
"\n",
"/* Estimator label */\n",
"\n",
"#sk-container-id-1 div.sk-label label {\n",
" font-family: monospace;\n",
" font-weight: bold;\n",
" display: inline-block;\n",
" line-height: 1.2em;\n",
"}\n",
"\n",
"#sk-container-id-1 div.sk-label-container {\n",
" text-align: center;\n",
"}\n",
"\n",
"/* Estimator-specific */\n",
"#sk-container-id-1 div.sk-estimator {\n",
" font-family: monospace;\n",
" border: 1px dotted var(--sklearn-color-border-box);\n",
" border-radius: 0.25em;\n",
" box-sizing: border-box;\n",
" margin-bottom: 0.5em;\n",
" /* unfitted */\n",
" background-color: var(--sklearn-color-unfitted-level-0);\n",
"}\n",
"\n",
"#sk-container-id-1 div.sk-estimator.fitted {\n",
" /* fitted */\n",
" background-color: var(--sklearn-color-fitted-level-0);\n",
"}\n",
"\n",
"/* on hover */\n",
"#sk-container-id-1 div.sk-estimator:hover {\n",
" /* unfitted */\n",
" background-color: var(--sklearn-color-unfitted-level-2);\n",
"}\n",
"\n",
"#sk-container-id-1 div.sk-estimator.fitted:hover {\n",
" /* fitted */\n",
" background-color: var(--sklearn-color-fitted-level-2);\n",
"}\n",
"\n",
"/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
"\n",
"/* Common style for \"i\" and \"?\" */\n",
"\n",
".sk-estimator-doc-link,\n",
"a:link.sk-estimator-doc-link,\n",
"a:visited.sk-estimator-doc-link {\n",
" float: right;\n",
" font-size: smaller;\n",
" line-height: 1em;\n",
" font-family: monospace;\n",
" background-color: var(--sklearn-color-background);\n",
" border-radius: 1em;\n",
" height: 1em;\n",
" width: 1em;\n",
" text-decoration: none !important;\n",
" margin-left: 1ex;\n",
" /* unfitted */\n",
" border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
" color: var(--sklearn-color-unfitted-level-1);\n",
"}\n",
"\n",
".sk-estimator-doc-link.fitted,\n",
"a:link.sk-estimator-doc-link.fitted,\n",
"a:visited.sk-estimator-doc-link.fitted {\n",
" /* fitted */\n",
" border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
" color: var(--sklearn-color-fitted-level-1);\n",
"}\n",
"\n",
"/* On hover */\n",
"div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
".sk-estimator-doc-link:hover,\n",
"div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
".sk-estimator-doc-link:hover {\n",
" /* unfitted */\n",
" background-color: var(--sklearn-color-unfitted-level-3);\n",
" color: var(--sklearn-color-background);\n",
" text-decoration: none;\n",
"}\n",
"\n",
"div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
".sk-estimator-doc-link.fitted:hover,\n",
"div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
".sk-estimator-doc-link.fitted:hover {\n",
" /* fitted */\n",
" background-color: var(--sklearn-color-fitted-level-3);\n",
" color: var(--sklearn-color-background);\n",
" text-decoration: none;\n",
"}\n",
"\n",
"/* Span, style for the box shown on hovering the info icon */\n",
".sk-estimator-doc-link span {\n",
" display: none;\n",
" z-index: 9999;\n",
" position: relative;\n",
" font-weight: normal;\n",
" right: .2ex;\n",
" padding: .5ex;\n",
" margin: .5ex;\n",
" width: min-content;\n",
" min-width: 20ex;\n",
" max-width: 50ex;\n",
" color: var(--sklearn-color-text);\n",
" box-shadow: 2pt 2pt 4pt #999;\n",
" /* unfitted */\n",
" background: var(--sklearn-color-unfitted-level-0);\n",
" border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
"}\n",
"\n",
".sk-estimator-doc-link.fitted span {\n",
" /* fitted */\n",
" background: var(--sklearn-color-fitted-level-0);\n",
" border: var(--sklearn-color-fitted-level-3);\n",
"}\n",
"\n",
".sk-estimator-doc-link:hover span {\n",
" display: block;\n",
"}\n",
"\n",
"/* \"?\"-specific style due to the `<a>` HTML tag */\n",
"\n",
"#sk-container-id-1 a.estimator_doc_link {\n",
" float: right;\n",
" font-size: 1rem;\n",
" line-height: 1em;\n",
" font-family: monospace;\n",
" background-color: var(--sklearn-color-background);\n",
" border-radius: 1rem;\n",
" height: 1rem;\n",
" width: 1rem;\n",
" text-decoration: none;\n",
" /* unfitted */\n",
" color: var(--sklearn-color-unfitted-level-1);\n",
" border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
"}\n",
"\n",
"#sk-container-id-1 a.estimator_doc_link.fitted {\n",
" /* fitted */\n",
" border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
" color: var(--sklearn-color-fitted-level-1);\n",
"}\n",
"\n",
"/* On hover */\n",
"#sk-container-id-1 a.estimator_doc_link:hover {\n",
" /* unfitted */\n",
" background-color: var(--sklearn-color-unfitted-level-3);\n",
" color: var(--sklearn-color-background);\n",
" text-decoration: none;\n",
"}\n",
"\n",
"#sk-container-id-1 a.estimator_doc_link.fitted:hover {\n",
" /* fitted */\n",
" background-color: var(--sklearn-color-fitted-level-3);\n",
"}\n",
"</style><div id=\"sk-container-id-1\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>KMeans(n_clusters=2, 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 fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-1\" type=\"checkbox\" checked><label for=\"sk-estimator-id-1\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;&nbsp;KMeans<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.cluster.KMeans.html\">?<span>Documentation for KMeans</span></a><span class=\"sk-estimator-doc-link fitted\">i<span>Fitted</span></span></label><div class=\"sk-toggleable__content fitted\"><pre>KMeans(n_clusters=2, random_state=42)</pre></div> </div></div></div></div>"
],
"text/plain": [
"KMeans(n_clusters=2, random_state=42)"
]
},
"execution_count": 12,
"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": 14,
"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": 16,
"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": "markdown",
"id": "088df814-6d04-450a-9091-a1e4acc6805e",
"metadata": {},
"source": [
"#### Interpretation\n",
"Eine Hauptkomponente mit einem größeren Anteil der Varianz erklärt mehr Variation in den Daten und ist daher wichtiger für die Reduktion der Dimensionalität. Die Summe aller Anteile der Varianz erklärt die Gesamtvarianz der Daten. \n",
"In diesem spezifischen Fall erklärt die erste Hauptkomponente (PC1) etwa 23.4% der Gesamtvarianz, die zweite Hauptkomponente (PC2) etwa 12.6% usw. Basierend auf diesen Daten kann beurteilt werden, wie viel Varianz jede Hauptkomponente in den Daten erklärt und wie wichtig jede Hauptkomponente für die Repräsentation der Daten ist."
]
},
{
"cell_type": "code",
"execution_count": 18,
"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)"
]
},
{
"cell_type": "markdown",
"id": "7798ac52-c736-4598-951a-0901918b3a21",
"metadata": {},
"source": [
"#### Interpretation\n",
"Die Werte der Spalte \"Contribution\" zeigen die Stärke des Beitrags jedes Merkmals zur ersten Hauptkomponente. Merkmale mit größeren Beträgen haben eine größere Bedeutung für die erste Hauptkomponente und tragen mehr zur Variation der Daten bei. \n",
"In diesem spezifischen Fall tragen beispielsweise die Merkmale \"exang_1\", \"thal_7.0\" und \"cp_4\" am stärksten zur ersten Hauptkomponente bei, während \"thal_3.0\", \"slope_1\" und \"exang_0\" die stärksten negativen Beiträge haben.\n",
"Darauf basierend kann analysiert werden, welche Merkmale die größte Bedeutung für die erste (wichtigste) Hauptkomponente haben und somit die größte Variation in den Daten erklären. Das kann helfen, die wichtigsten Merkmale zu identifizieren, die die gegebenen Datenstrukturen beeinflussen.\n",
"\n",
"Hier würde das bedeuten, dass 'exang_1' (existing exercised induced angina), 'thal_7' (reversable effect caused by thalassemia) und cp_4 (asymptomatic type of chest pain) einen potenziell größeren Einfluss auf die Zielvariable haben, als andere Merkmale."
]
}
],
"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.9"
}
},
"nbformat": 4,
"nbformat_minor": 5
}