{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1 датасет. NASA - Nearest Earth Objects\n",
    "## В космосе находится бесконечное количество объектов. Некоторые из них находятся ближе, чем мы думаем. Хотя мы можем думать, что расстояние в 70 000 км не может потенциально навредить нам, но в астрономических масштабах это очень малое расстояние и может нарушить многие природные явления. Таким образом, эти объекты/астероиды могут оказаться вредными. Следовательно, разумно знать, что нас окружает и что из этого может навредить нам. Таким образом, этот набор данных составляет список сертифицированных NASA астероидов, которые классифицируются как ближайшие к Земле объекты.\n",
    "## В данном датасете объектами исследования являются околоземные объекты. В нём содержатся данные атрибуты: id, name, est_diameter_min, est_diameter_max, relative_velocity, miss_distance, orbiting_body, sentry_object, absolute_magnitude, hazardous. Цель создания данного датасета- научиться определять, опасен ли объект на орбите или же нет.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "количество колонок: 10\n",
      "колонки: id, name, est_diameter_min, est_diameter_max, relative_velocity, miss_distance, orbiting_body, sentry_object, absolute_magnitude, hazardous\n"
     ]
    }
   ],
   "source": [
    "import pandas as pd \n",
    "df = pd.read_csv(\"C://Users//annal//aim//static//csv//neo_v2.csv\")\n",
    "print('количество колонок: ' + str(df.columns.size))  \n",
    "print('колонки: ' + ', '.join(df.columns))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Получим сведения о пропущенных данных. Из вывода видно, что пропущенные данные не обнаружены."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "id                    0\n",
      "name                  0\n",
      "est_diameter_min      0\n",
      "est_diameter_max      0\n",
      "relative_velocity     0\n",
      "miss_distance         0\n",
      "orbiting_body         0\n",
      "sentry_object         0\n",
      "absolute_magnitude    0\n",
      "hazardous             0\n",
      "dtype: int64\n",
      "\n",
      "id                    False\n",
      "name                  False\n",
      "est_diameter_min      False\n",
      "est_diameter_max      False\n",
      "relative_velocity     False\n",
      "miss_distance         False\n",
      "orbiting_body         False\n",
      "sentry_object         False\n",
      "absolute_magnitude    False\n",
      "hazardous             False\n",
      "dtype: bool\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Количество пустых значений признаков\n",
    "print(df.isnull().sum())\n",
    "\n",
    "print()\n",
    "\n",
    "# Есть ли пустые значения признаков\n",
    "print(df.isnull().any())\n",
    "\n",
    "print()\n",
    "\n",
    "# Процент пустых значений признаков\n",
    "for i in df.columns:\n",
    "    null_rate = df[i].isnull().sum() / len(df) * 100\n",
    "    if null_rate > 0:\n",
    "        print(f\"{i} процент пустых значений: %{null_rate:.2f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## На сайте в карточке датасета видно, что в колонках orbiting_body и sentry_object в каждой строке одинаковое значение. Это значит, что эти колонки не являются информативными и их можно убрать."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "количество колонок: 8\n",
      "колонки: id, name, est_diameter_min, est_diameter_max, relative_velocity, miss_distance, absolute_magnitude, hazardous\n"
     ]
    }
   ],
   "source": [
    "df = df.drop(columns=['orbiting_body'])\n",
    "df = df.drop(columns=['sentry_object'])\n",
    "print('количество колонок: ' + str(df.columns.size))  \n",
    "print('колонки: ' + ', '.join(df.columns))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## При беглом осмотре данных, зашумлённости не обнаружила. Необходимо ознакомиться с данными о выбросах."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Колонка est_diameter_min:\n",
      "  Есть выбросы: Да\n",
      "  Количество выбросов: 8306\n",
      "  Минимальное значение: 0.0006089126\n",
      "  Максимальное значение: 0.32962154705\n",
      "  1-й квантиль (Q1): 0.0192555078\n",
      "  3-й квантиль (Q3): 0.1434019235\n",
      "\n",
      "Колонка est_diameter_max:\n",
      "  Есть выбросы: Да\n",
      "  Количество выбросов: 8306\n",
      "  Минимальное значение: 0.00136157\n",
      "  Максимальное значение: 0.7370561859\n",
      "  1-й квантиль (Q1): 0.0430566244\n",
      "  3-й квантиль (Q3): 0.320656449\n",
      "\n",
      "Колонка relative_velocity:\n",
      "  Есть выбросы: Да\n",
      "  Количество выбросов: 1574\n",
      "  Минимальное значение: 203.34643253\n",
      "  Максимальное значение: 114380.48061454494\n",
      "  1-й квантиль (Q1): 28619.02064490995\n",
      "  3-й квантиль (Q3): 62923.60463276395\n",
      "\n",
      "Колонка miss_distance:\n",
      "  Есть выбросы: Нет\n",
      "  Количество выбросов: 0\n",
      "  Минимальное значение: 6745.532515957\n",
      "  Максимальное значение: 74798651.4521972\n",
      "  1-й квантиль (Q1): 17210820.23576468\n",
      "  3-й квантиль (Q3): 56548996.45139917\n",
      "\n",
      "Колонка absolute_magnitude:\n",
      "  Есть выбросы: Да\n",
      "  Количество выбросов: 101\n",
      "  Минимальное значение: 14.8\n",
      "  Максимальное значение: 32.239999999999995\n",
      "  1-й квантиль (Q1): 21.34\n",
      "  3-й квантиль (Q3): 25.7\n",
      "\n"
     ]
    }
   ],
   "source": [
    "numeric_columns = ['est_diameter_min', 'est_diameter_max', 'relative_velocity', 'miss_distance', 'absolute_magnitude']\n",
    "for column in numeric_columns:\n",
    "    if pd.api.types.is_numeric_dtype(df[column]):  # Проверяем, является ли колонка числовой\n",
    "        q1 = df[column].quantile(0.25)  # Находим 1-й квантиль (Q1)\n",
    "        q3 = df[column].quantile(0.75)  # Находим 3-й квантиль (Q3)\n",
    "        iqr = q3 - q1  # Вычисляем межквантильный размах (IQR)\n",
    "\n",
    "        # Определяем границы для выбросов\n",
    "        lower_bound = q1 - 1.5 * iqr  # Нижняя граница\n",
    "        upper_bound = q3 + 1.5 * iqr  # Верхняя граница\n",
    "\n",
    "        # Подсчитываем количество выбросов\n",
    "        outliers = df[(df[column] < lower_bound) | (df[column] > upper_bound)]\n",
    "        outlier_count = outliers.shape[0]\n",
    "\n",
    "        # Устраняем выбросы: заменяем значения ниже нижней границы на саму нижнюю границу, а выше верхней — на верхнюю\n",
    "        df[column] = df[column].apply(lambda x: lower_bound if x < lower_bound else upper_bound if x > upper_bound else x)\n",
    "\n",
    "        print(f\"Колонка {column}:\")\n",
    "        print(f\"  Есть выбросы: {'Да' if outlier_count > 0 else 'Нет'}\")\n",
    "        print(f\"  Количество выбросов: {outlier_count}\")\n",
    "        print(f\"  Минимальное значение: {df[column].min()}\")\n",
    "        print(f\"  Максимальное значение: {df[column].max()}\")\n",
    "        print(f\"  1-й квантиль (Q1): {q1}\")\n",
    "        print(f\"  3-й квантиль (Q3): {q3}\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Необходимо построить диаграммы для поиска зависимостей опасности от других колонок."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 32400 characters]",
      "text/plain": [
       "<Figure size 400x800 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 32716 characters]",
      "text/plain": [
       "<Figure size 400x800 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 34088 characters]",
      "text/plain": [
       "<Figure size 400x800 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 24804 characters]",
      "text/plain": [
       "<Figure size 400x800 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 34208 characters]",
      "text/plain": [
       "<Figure size 400x800 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# Список числовых колонок, для которых мы будем строить графики\n",
    "numeric_columns = ['est_diameter_min', 'est_diameter_max', 'relative_velocity', 'miss_distance', 'absolute_magnitude']\n",
    "\n",
    "# Создание диаграмм зависимости\n",
    "for column in numeric_columns:\n",
    "    plt.figure(figsize=(4, 8))  # Установка размера графика\n",
    "    plt.scatter(df['hazardous'], df[column], alpha=0.5)  # Создаем диаграмму рассеяния\n",
    "    plt.title(f'Зависимость {column} от hazardous')\n",
    "    plt.xlabel('hazardous (0 = нет, 1 = да)')\n",
    "    plt.ylabel(column)\n",
    "    plt.xticks([0, 1])  # Установка меток по оси X\n",
    "    plt.grid()  # Добавление сетки для удобства восприятия\n",
    "    plt.show()  # Отображение графика"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Далее создадим выборки. Разбивать данные на классы стоит по критерию опасности, потому что это то, что будет предсказывать модель."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Collecting scikit-learn\n",
      "  Downloading scikit_learn-1.5.2-cp312-cp312-win_amd64.whl.metadata (13 kB)\n",
      "Requirement already satisfied: numpy>=1.19.5 in c:\\users\\annal\\aim\\.venv\\lib\\site-packages (from scikit-learn) (2.1.1)\n",
      "Collecting scipy>=1.6.0 (from scikit-learn)\n",
      "  Downloading scipy-1.14.1-cp312-cp312-win_amd64.whl.metadata (60 kB)\n",
      "Collecting joblib>=1.2.0 (from scikit-learn)\n",
      "  Downloading joblib-1.4.2-py3-none-any.whl.metadata (5.4 kB)\n",
      "Collecting threadpoolctl>=3.1.0 (from scikit-learn)\n",
      "  Downloading threadpoolctl-3.5.0-py3-none-any.whl.metadata (13 kB)\n",
      "Downloading scikit_learn-1.5.2-cp312-cp312-win_amd64.whl (11.0 MB)\n",
      "   ---------------------------------------- 0.0/11.0 MB ? eta -:--:--\n",
      "    --------------------------------------- 0.3/11.0 MB ? eta -:--:--\n",
      "   -- ------------------------------------- 0.8/11.0 MB 1.9 MB/s eta 0:00:06\n",
      "   ---- ----------------------------------- 1.3/11.0 MB 2.3 MB/s eta 0:00:05\n",
      "   -------- ------------------------------- 2.4/11.0 MB 2.9 MB/s eta 0:00:03\n",
      "   ----------- ---------------------------- 3.1/11.0 MB 3.2 MB/s eta 0:00:03\n",
      "   --------------- ------------------------ 4.2/11.0 MB 3.5 MB/s eta 0:00:02\n",
      "   ------------------ --------------------- 5.0/11.0 MB 3.6 MB/s eta 0:00:02\n",
      "   --------------------- ------------------ 6.0/11.0 MB 3.8 MB/s eta 0:00:02\n",
      "   ------------------------ --------------- 6.8/11.0 MB 3.8 MB/s eta 0:00:02\n",
      "   --------------------------- ------------ 7.6/11.0 MB 3.9 MB/s eta 0:00:01\n",
      "   ------------------------------ --------- 8.4/11.0 MB 3.9 MB/s eta 0:00:01\n",
      "   --------------------------------- ------ 9.2/11.0 MB 3.9 MB/s eta 0:00:01\n",
      "   ------------------------------------ --- 10.0/11.0 MB 3.8 MB/s eta 0:00:01\n",
      "   -------------------------------------- - 10.5/11.0 MB 3.7 MB/s eta 0:00:01\n",
      "   ---------------------------------------- 11.0/11.0 MB 3.7 MB/s eta 0:00:00\n",
      "Downloading joblib-1.4.2-py3-none-any.whl (301 kB)\n",
      "Downloading scipy-1.14.1-cp312-cp312-win_amd64.whl (44.5 MB)\n",
      "   ---------------------------------------- 0.0/44.5 MB ? eta -:--:--\n",
      "   ---------------------------------------- 0.5/44.5 MB 3.4 MB/s eta 0:00:14\n",
      "   - -------------------------------------- 1.3/44.5 MB 3.2 MB/s eta 0:00:14\n",
      "   - -------------------------------------- 2.1/44.5 MB 3.6 MB/s eta 0:00:12\n",
      "   -- ------------------------------------- 2.9/44.5 MB 3.6 MB/s eta 0:00:12\n",
      "   --- ------------------------------------ 3.7/44.5 MB 3.6 MB/s eta 0:00:12\n",
      "   ---- ----------------------------------- 4.5/44.5 MB 3.6 MB/s eta 0:00:12\n",
      "   ---- ----------------------------------- 5.2/44.5 MB 3.7 MB/s eta 0:00:11\n",
      "   ----- ---------------------------------- 6.0/44.5 MB 3.8 MB/s eta 0:00:11\n",
      "   ------ --------------------------------- 7.1/44.5 MB 3.8 MB/s eta 0:00:10\n",
      "   ------ --------------------------------- 7.6/44.5 MB 3.7 MB/s eta 0:00:10\n",
      "   ------- -------------------------------- 8.4/44.5 MB 3.7 MB/s eta 0:00:10\n",
      "   -------- ------------------------------- 9.2/44.5 MB 3.7 MB/s eta 0:00:10\n",
      "   -------- ------------------------------- 10.0/44.5 MB 3.8 MB/s eta 0:00:10\n",
      "   --------- ------------------------------ 11.0/44.5 MB 3.8 MB/s eta 0:00:09\n",
      "   ---------- ----------------------------- 11.5/44.5 MB 3.8 MB/s eta 0:00:09\n",
      "   ---------- ----------------------------- 11.8/44.5 MB 3.8 MB/s eta 0:00:09\n",
      "   ----------- ---------------------------- 13.1/44.5 MB 3.8 MB/s eta 0:00:09\n",
      "   ------------ --------------------------- 13.9/44.5 MB 3.8 MB/s eta 0:00:09\n",
      "   ------------ --------------------------- 14.4/44.5 MB 3.8 MB/s eta 0:00:09\n",
      "   ------------- -------------------------- 15.2/44.5 MB 3.7 MB/s eta 0:00:08\n",
      "   -------------- ------------------------- 16.0/44.5 MB 3.7 MB/s eta 0:00:08\n",
      "   -------------- ------------------------- 16.5/44.5 MB 3.6 MB/s eta 0:00:08\n",
      "   --------------- ------------------------ 17.0/44.5 MB 3.6 MB/s eta 0:00:08\n",
      "   --------------- ------------------------ 17.3/44.5 MB 3.6 MB/s eta 0:00:08\n",
      "   ---------------- ----------------------- 18.1/44.5 MB 3.5 MB/s eta 0:00:08\n",
      "   ---------------- ----------------------- 18.9/44.5 MB 3.5 MB/s eta 0:00:08\n",
      "   ----------------- ---------------------- 19.4/44.5 MB 3.5 MB/s eta 0:00:08\n",
      "   ------------------ --------------------- 20.2/44.5 MB 3.5 MB/s eta 0:00:07\n",
      "   ------------------ --------------------- 20.7/44.5 MB 3.5 MB/s eta 0:00:07\n",
      "   ------------------- -------------------- 21.5/44.5 MB 3.5 MB/s eta 0:00:07\n",
      "   ------------------- -------------------- 22.0/44.5 MB 3.5 MB/s eta 0:00:07\n",
      "   -------------------- ------------------- 22.8/44.5 MB 3.5 MB/s eta 0:00:07\n",
      "   -------------------- ------------------- 23.3/44.5 MB 3.5 MB/s eta 0:00:07\n",
      "   --------------------- ------------------ 23.9/44.5 MB 3.4 MB/s eta 0:00:07\n",
      "   ---------------------- ----------------- 24.6/44.5 MB 3.4 MB/s eta 0:00:06\n",
      "   ---------------------- ----------------- 25.2/44.5 MB 3.4 MB/s eta 0:00:06\n",
      "   ----------------------- ---------------- 26.0/44.5 MB 3.4 MB/s eta 0:00:06\n",
      "   ----------------------- ---------------- 26.5/44.5 MB 3.4 MB/s eta 0:00:06\n",
      "   ------------------------ --------------- 27.3/44.5 MB 3.4 MB/s eta 0:00:06\n",
      "   ------------------------- -------------- 28.0/44.5 MB 3.4 MB/s eta 0:00:05\n",
      "   ------------------------- -------------- 28.6/44.5 MB 3.4 MB/s eta 0:00:05\n",
      "   -------------------------- ------------- 29.4/44.5 MB 3.4 MB/s eta 0:00:05\n",
      "   -------------------------- ------------- 29.9/44.5 MB 3.4 MB/s eta 0:00:05\n",
      "   --------------------------- ------------ 30.7/44.5 MB 3.4 MB/s eta 0:00:05\n",
      "   ---------------------------- ----------- 31.2/44.5 MB 3.4 MB/s eta 0:00:04\n",
      "   ---------------------------- ----------- 31.7/44.5 MB 3.3 MB/s eta 0:00:04\n",
      "   ---------------------------- ----------- 32.2/44.5 MB 3.3 MB/s eta 0:00:04\n",
      "   ----------------------------- ---------- 33.3/44.5 MB 3.4 MB/s eta 0:00:04\n",
      "   ------------------------------ --------- 33.8/44.5 MB 3.4 MB/s eta 0:00:04\n",
      "   ------------------------------- -------- 34.6/44.5 MB 3.4 MB/s eta 0:00:03\n",
      "   ------------------------------- -------- 35.1/44.5 MB 3.4 MB/s eta 0:00:03\n",
      "   -------------------------------- ------- 35.9/44.5 MB 3.4 MB/s eta 0:00:03\n",
      "   -------------------------------- ------- 36.7/44.5 MB 3.4 MB/s eta 0:00:03\n",
      "   --------------------------------- ------ 37.2/44.5 MB 3.4 MB/s eta 0:00:03\n",
      "   ---------------------------------- ----- 38.3/44.5 MB 3.4 MB/s eta 0:00:02\n",
      "   ----------------------------------- ---- 39.1/44.5 MB 3.4 MB/s eta 0:00:02\n",
      "   ----------------------------------- ---- 39.8/44.5 MB 3.4 MB/s eta 0:00:02\n",
      "   ------------------------------------ --- 40.6/44.5 MB 3.4 MB/s eta 0:00:02\n",
      "   ------------------------------------- -- 41.4/44.5 MB 3.4 MB/s eta 0:00:01\n",
      "   ------------------------------------- -- 41.9/44.5 MB 3.4 MB/s eta 0:00:01\n",
      "   -------------------------------------- - 42.5/44.5 MB 3.4 MB/s eta 0:00:01\n",
      "   -------------------------------------- - 42.5/44.5 MB 3.4 MB/s eta 0:00:01\n",
      "   -------------------------------------- - 43.3/44.5 MB 3.3 MB/s eta 0:00:01\n",
      "   ---------------------------------------  43.8/44.5 MB 3.3 MB/s eta 0:00:01\n",
      "   ---------------------------------------  44.3/44.5 MB 3.3 MB/s eta 0:00:01\n",
      "   ---------------------------------------- 44.5/44.5 MB 3.3 MB/s eta 0:00:00\n",
      "Downloading threadpoolctl-3.5.0-py3-none-any.whl (18 kB)\n",
      "Installing collected packages: threadpoolctl, scipy, joblib, scikit-learn\n",
      "Successfully installed joblib-1.4.2 scikit-learn-1.5.2 scipy-1.14.1 threadpoolctl-3.5.0\n",
      "Note: you may need to restart the kernel to use updated packages.\n"
     ]
    }
   ],
   "source": [
    "pip install scikit-learn"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Функция для создания выборок\n",
    "from sklearn.model_selection import train_test_split\n",
    "\n",
    "def split_stratified_into_train_val_test(\n",
    "    df_input,\n",
    "    stratify_colname=\"y\",\n",
    "    frac_train=0.6,\n",
    "    frac_val=0.15,\n",
    "    frac_test=0.25,\n",
    "    random_state=None,\n",
    "):\n",
    "    \"\"\"\n",
    "    Splits a Pandas dataframe into three subsets (train, val, and test)\n",
    "    following fractional ratios provided by the user, where each subset is\n",
    "    stratified by the values in a specific column (that is, each subset has\n",
    "    the same relative frequency of the values in the column). It performs this\n",
    "    splitting by running train_test_split() twice.\n",
    "\n",
    "    Parameters\n",
    "    ----------\n",
    "    df_input : Pandas dataframe\n",
    "        Input dataframe to be split.\n",
    "    stratify_colname : str\n",
    "        The name of the column that will be used for stratification. Usually\n",
    "        this column would be for the label.\n",
    "    frac_train : float\n",
    "    frac_val   : float\n",
    "    frac_test  : float\n",
    "        The ratios with which the dataframe will be split into train, val, and\n",
    "        test data. The values should be expressed as float fractions and should\n",
    "        sum to 1.0.\n",
    "    random_state : int, None, or RandomStateInstance\n",
    "        Value to be passed to train_test_split().\n",
    "\n",
    "    Returns\n",
    "    -------\n",
    "    df_train, df_val, df_test :\n",
    "        Dataframes containing the three splits.\n",
    "    \"\"\"\n",
    "\n",
    "    if frac_train + frac_val + frac_test != 1.0:\n",
    "        raise ValueError(\n",
    "            \"fractions %f, %f, %f do not add up to 1.0\"\n",
    "            % (frac_train, frac_val, frac_test)\n",
    "        )\n",
    "\n",
    "    if stratify_colname not in df_input.columns:\n",
    "        raise ValueError(\"%s is not a column in the dataframe\" % (stratify_colname))\n",
    "\n",
    "    X = df_input  # Contains all columns.\n",
    "    y = df_input[\n",
    "        [stratify_colname]\n",
    "    ]  # Dataframe of just the column on which to stratify.\n",
    "\n",
    "    # Split original dataframe into train and temp dataframes.\n",
    "    df_train, df_temp, y_train, y_temp = train_test_split(\n",
    "        X, y, stratify=y, test_size=(1.0 - frac_train), random_state=random_state\n",
    "    )\n",
    "\n",
    "    # Split the temp dataframe into val and test dataframes.\n",
    "    relative_frac_test = frac_test / (frac_val + frac_test)\n",
    "    df_val, df_test, y_val, y_test = train_test_split(\n",
    "        df_temp,\n",
    "        y_temp,\n",
    "        stratify=y_temp,\n",
    "        test_size=relative_frac_test,\n",
    "        random_state=random_state,\n",
    "    )\n",
    "\n",
    "    assert len(df_input) == len(df_train) + len(df_val) + len(df_test)\n",
    "\n",
    "    return df_train, df_val, df_test"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hazardous\n",
      "False    81996\n",
      "True      8840\n",
      "Name: count, dtype: int64\n",
      "\n",
      "Обучающая выборка:  (54501, 6)\n",
      "hazardous\n",
      "False    49197\n",
      "True      5304\n",
      "Name: count, dtype: int64\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 21124 characters]",
      "text/plain": [
       "<Figure size 200x200 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Контрольная выборка:  (18167, 6)\n",
      "hazardous\n",
      "False    16399\n",
      "True      1768\n",
      "Name: count, dtype: int64\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 20856 characters]",
      "text/plain": [
       "<Figure size 200x200 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Тестовая выборка:  (18168, 6)\n",
      "hazardous\n",
      "False    16400\n",
      "True      1768\n",
      "Name: count, dtype: int64\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 19908 characters]",
      "text/plain": [
       "<Figure size 200x200 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Вывод распределения количества наблюдений по меткам (классам)\n",
    "print(df.hazardous.value_counts())\n",
    "print()\n",
    "\n",
    "\n",
    "data = df[['est_diameter_min', 'est_diameter_max', 'relative_velocity', 'miss_distance', 'absolute_magnitude', 'hazardous']].copy()\n",
    "\n",
    "df_train, df_val, df_test = split_stratified_into_train_val_test(\n",
    "   data, stratify_colname=\"hazardous\", frac_train=0.60, frac_val=0.20, frac_test=0.20\n",
    ")\n",
    "\n",
    "print(\"Обучающая выборка: \", df_train.shape)\n",
    "print(df_train.hazardous.value_counts())\n",
    "hazardous_counts = df_train['hazardous'].value_counts()\n",
    "plt.figure(figsize=(2, 2))# Установка размера графика\n",
    "plt.pie(hazardous_counts, labels=hazardous_counts.index, autopct='%1.1f%%', startangle=90)# Построение круговой диаграммы\n",
    "plt.title('Распределение классов hazardous в обучающей выборке')# Добавление заголовка\n",
    "plt.show()# Отображение графика\n",
    "\n",
    "print(\"Контрольная выборка: \", df_val.shape)\n",
    "print(df_val.hazardous.value_counts())\n",
    "hazardous_counts = df_val['hazardous'].value_counts()\n",
    "plt.figure(figsize=(2, 2))\n",
    "plt.pie(hazardous_counts, labels=hazardous_counts.index, autopct='%1.1f%%', startangle=90)\n",
    "plt.title('Распределение классов hazardous в контрольной выборке')\n",
    "plt.show()\n",
    "\n",
    "print(\"Тестовая выборка: \", df_test.shape)\n",
    "print(df_test.hazardous.value_counts())\n",
    "hazardous_counts = df_test['hazardous'].value_counts()\n",
    "plt.figure(figsize=(2, 2))\n",
    "plt.pie(hazardous_counts, labels=hazardous_counts.index, autopct='%1.1f%%', startangle=90)\n",
    "plt.title('Распределение классов hazardous в тестовой выборке')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## По диаграммам видно, что выборка плохая, слишком большое смещение классов. Проведём аугментацию данных оверсемплингом. Этот метод позволяет увеличить количество примеров меньшинства."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Collecting imblearn\n",
      "  Downloading imblearn-0.0-py2.py3-none-any.whl.metadata (355 bytes)\n",
      "Collecting imbalanced-learn (from imblearn)\n",
      "  Downloading imbalanced_learn-0.12.4-py3-none-any.whl.metadata (8.3 kB)\n",
      "Requirement already satisfied: numpy>=1.17.3 in c:\\users\\annal\\aim\\.venv\\lib\\site-packages (from imbalanced-learn->imblearn) (2.1.1)\n",
      "Requirement already satisfied: scipy>=1.5.0 in c:\\users\\annal\\aim\\.venv\\lib\\site-packages (from imbalanced-learn->imblearn) (1.14.1)\n",
      "Requirement already satisfied: scikit-learn>=1.0.2 in c:\\users\\annal\\aim\\.venv\\lib\\site-packages (from imbalanced-learn->imblearn) (1.5.2)\n",
      "Requirement already satisfied: joblib>=1.1.1 in c:\\users\\annal\\aim\\.venv\\lib\\site-packages (from imbalanced-learn->imblearn) (1.4.2)\n",
      "Requirement already satisfied: threadpoolctl>=2.0.0 in c:\\users\\annal\\aim\\.venv\\lib\\site-packages (from imbalanced-learn->imblearn) (3.5.0)\n",
      "Downloading imblearn-0.0-py2.py3-none-any.whl (1.9 kB)\n",
      "Downloading imbalanced_learn-0.12.4-py3-none-any.whl (258 kB)\n",
      "Installing collected packages: imbalanced-learn, imblearn\n",
      "Successfully installed imbalanced-learn-0.12.4 imblearn-0.0\n",
      "Note: you may need to restart the kernel to use updated packages.\n"
     ]
    }
   ],
   "source": [
    "pip install imblearn"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Обучающая выборка после oversampling:  (100481, 6)\n",
      "hazardous\n",
      "True     51284\n",
      "False    49197\n",
      "Name: count, dtype: int64\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 22260 characters]",
      "text/plain": [
       "<Figure size 200x200 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from imblearn.over_sampling import ADASYN\n",
    "\n",
    "# Создание экземпляра ADASYN\n",
    "ada = ADASYN()\n",
    "\n",
    "# Применение ADASYN\n",
    "X_resampled, y_resampled = ada.fit_resample(df_train.drop(columns=['hazardous']), df_train['hazardous'])\n",
    "\n",
    "# Создание нового DataFrame\n",
    "df_train_adasyn = pd.DataFrame(X_resampled)\n",
    "df_train_adasyn['hazardous'] = y_resampled  # Добавление целевой переменной\n",
    "\n",
    "# Вывод информации о новой выборке\n",
    "print(\"Обучающая выборка после oversampling: \", df_train_adasyn.shape)\n",
    "print(df_train_adasyn['hazardous'].value_counts())\n",
    "hazardous_counts = df_train_adasyn['hazardous'].value_counts()\n",
    "plt.figure(figsize=(2, 2))\n",
    "plt.pie(hazardous_counts, labels=hazardous_counts.index, autopct='%1.1f%%', startangle=90)\n",
    "plt.title('Распределение классов hazardous в тренировачной выборке после ADASYN')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Проведём балансировку методом андерсемплинга для уменьшения примеров большинства."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Обучающая выборка после undersampling:  (10608, 6)\n",
      "hazardous\n",
      "False    5304\n",
      "True     5304\n",
      "Name: count, dtype: int64\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 22084 characters]",
      "text/plain": [
       "<Figure size 200x200 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from imblearn.under_sampling import RandomUnderSampler\n",
    "\n",
    "rus = RandomUnderSampler()# Создание экземпляра RandomUnderSampler\n",
    "\n",
    "# Применение RandomUnderSampler\n",
    "X_resampled, y_resampled = rus.fit_resample(df_train.drop(columns=['hazardous']), df_train['hazardous'])\n",
    "\n",
    "# Создание нового DataFrame\n",
    "df_train_undersampled = pd.DataFrame(X_resampled)\n",
    "df_train_undersampled['hazardous'] = y_resampled  # Добавление целевой переменной\n",
    "\n",
    "# Вывод информации о новой выборке\n",
    "print(\"Обучающая выборка после undersampling: \", df_train_undersampled.shape)\n",
    "print(df_train_undersampled['hazardous'].value_counts())\n",
    "\n",
    "# Визуализация распределения классов\n",
    "hazardous_counts = df_train_undersampled['hazardous'].value_counts()\n",
    "plt.figure(figsize=(2, 2))\n",
    "plt.pie(hazardous_counts, labels=hazardous_counts.index, autopct='%1.1f%%', startangle=90)\n",
    "plt.title('Распределение классов hazardous в тренировочной выборке после Undersampling')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Датасет 2. Indicators of Heart Disease\n",
    "## По данным CDC, болезни сердца являются основной причиной смерти представителей большинства рас в США (афроамериканцев, американских индейцев и коренных жителей Аляски, а также белых). Около половины всех американцев (47%) имеют по крайней мере 1 из 3 основных факторов риска сердечно-сосудистых заболеваний: высокое кровяное давление, высокий уровень холестерина и курение. Другие ключевые показатели включают состояние диабета, ожирение (высокий ИМТ), недостаточную физическую активность или чрезмерное употребление алкоголя. Выявление и профилактика факторов, оказывающих наибольшее влияние на болезни сердца, очень важны в здравоохранении. В свою очередь, развитие вычислительной техники позволяет применять методы машинного обучения для обнаружения \"закономерностей\" в данных, которые могут предсказать состояние пациента.\n",
    "## В данном датасете объектами исследования являются жители США. В нём содержатся данные атрибуты: HeartDisease, BMI, Smoking, AlcoholDrinking, Stroke, PhysicalHealth, MentalHealth, DiffWalking, Sex, AgeCategory, Race, Diabetic, PhysicalActivity, GenHealth, SleepTime, Asthma, KidneyDisease, SkinCancer. Цель создания данного датасета- научиться прогнозировать сердечный приступ."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "количество колонок: 18\n",
      "колонки: HeartDisease, BMI, Smoking, AlcoholDrinking, Stroke, PhysicalHealth, MentalHealth, DiffWalking, Sex, AgeCategory, Race, Diabetic, PhysicalActivity, GenHealth, SleepTime, Asthma, KidneyDisease, SkinCancer\n"
     ]
    }
   ],
   "source": [
    "import pandas as pd \n",
    "df = pd.read_csv(\"C://Users//annal//aim//static//csv//heart.csv\")\n",
    "print('количество колонок: ' + str(df.columns.size))  \n",
    "print('колонки: ' + ', '.join(df.columns))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Получим сведения о пропущенных данных. Из вывода видно, что пропущенные данные не обнаружены."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "HeartDisease        0\n",
      "BMI                 0\n",
      "Smoking             0\n",
      "AlcoholDrinking     0\n",
      "Stroke              0\n",
      "PhysicalHealth      0\n",
      "MentalHealth        0\n",
      "DiffWalking         0\n",
      "Sex                 0\n",
      "AgeCategory         0\n",
      "Race                0\n",
      "Diabetic            0\n",
      "PhysicalActivity    0\n",
      "GenHealth           0\n",
      "SleepTime           0\n",
      "Asthma              0\n",
      "KidneyDisease       0\n",
      "SkinCancer          0\n",
      "dtype: int64\n",
      "\n",
      "HeartDisease        False\n",
      "BMI                 False\n",
      "Smoking             False\n",
      "AlcoholDrinking     False\n",
      "Stroke              False\n",
      "PhysicalHealth      False\n",
      "MentalHealth        False\n",
      "DiffWalking         False\n",
      "Sex                 False\n",
      "AgeCategory         False\n",
      "Race                False\n",
      "Diabetic            False\n",
      "PhysicalActivity    False\n",
      "GenHealth           False\n",
      "SleepTime           False\n",
      "Asthma              False\n",
      "KidneyDisease       False\n",
      "SkinCancer          False\n",
      "dtype: bool\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Количество пустых значений признаков\n",
    "print(df.isnull().sum())\n",
    "\n",
    "print()\n",
    "\n",
    "# Есть ли пустые значения признаков\n",
    "print(df.isnull().any())\n",
    "\n",
    "print()\n",
    "\n",
    "# Процент пустых значений признаков\n",
    "for i in df.columns:\n",
    "    null_rate = df[i].isnull().sum() / len(df) * 100\n",
    "    if null_rate > 0:\n",
    "        print(f\"{i} процент пустых значений: %{null_rate:.2f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## При беглом осмотре данных, зашумлённости не обнаружила. Необходимо ознакомиться с данными о выбросах."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Колонка BMI:\n",
      "  Есть выбросы: Да\n",
      "  Количество выбросов: 10396\n",
      "  Минимальное значение: 12.945\n",
      "  Максимальное значение: 42.505\n",
      "  1-й квантиль (Q1): 24.03\n",
      "  3-й квантиль (Q3): 31.42\n",
      "\n",
      "Колонка PhysicalHealth:\n",
      "  Есть выбросы: Да\n",
      "  Количество выбросов: 47146\n",
      "  Минимальное значение: 0.0\n",
      "  Максимальное значение: 5.0\n",
      "  1-й квантиль (Q1): 0.0\n",
      "  3-й квантиль (Q3): 2.0\n",
      "\n",
      "Колонка MentalHealth:\n",
      "  Есть выбросы: Да\n",
      "  Количество выбросов: 51576\n",
      "  Минимальное значение: 0.0\n",
      "  Максимальное значение: 7.5\n",
      "  1-й квантиль (Q1): 0.0\n",
      "  3-й квантиль (Q3): 3.0\n",
      "\n",
      "Колонка SleepTime:\n",
      "  Есть выбросы: Да\n",
      "  Количество выбросов: 4543\n",
      "  Минимальное значение: 3.0\n",
      "  Максимальное значение: 11.0\n",
      "  1-й квантиль (Q1): 6.0\n",
      "  3-й квантиль (Q3): 8.0\n",
      "\n"
     ]
    }
   ],
   "source": [
    "numeric_columns = ['BMI', 'PhysicalHealth', 'MentalHealth', 'AgeCategory', 'SleepTime']\n",
    "for column in numeric_columns:\n",
    "    if pd.api.types.is_numeric_dtype(df[column]):  # Проверяем, является ли колонка числовой\n",
    "        q1 = df[column].quantile(0.25)  # Находим 1-й квантиль (Q1)\n",
    "        q3 = df[column].quantile(0.75)  # Находим 3-й квантиль (Q3)\n",
    "        iqr = q3 - q1  # Вычисляем межквантильный размах (IQR)\n",
    "\n",
    "        # Определяем границы для выбросов\n",
    "        lower_bound = q1 - 1.5 * iqr  # Нижняя граница\n",
    "        upper_bound = q3 + 1.5 * iqr  # Верхняя граница\n",
    "\n",
    "        # Подсчитываем количество выбросов\n",
    "        outliers = df[(df[column] < lower_bound) | (df[column] > upper_bound)]\n",
    "        outlier_count = outliers.shape[0]\n",
    "\n",
    "        # Устраняем выбросы: заменяем значения ниже нижней границы на саму нижнюю границу, а выше верхней — на верхнюю\n",
    "        df[column] = df[column].apply(lambda x: lower_bound if x < lower_bound else upper_bound if x > upper_bound else x)\n",
    "\n",
    "        print(f\"Колонка {column}:\")\n",
    "        print(f\"  Есть выбросы: {'Да' if outlier_count > 0 else 'Нет'}\")\n",
    "        print(f\"  Количество выбросов: {outlier_count}\")\n",
    "        print(f\"  Минимальное значение: {df[column].min()}\")\n",
    "        print(f\"  Максимальное значение: {df[column].max()}\")\n",
    "        print(f\"  1-й квантиль (Q1): {q1}\")\n",
    "        print(f\"  3-й квантиль (Q3): {q3}\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Необходимо построить диаграммы для поиска зависимостей сердечного приступа(HeartDisease ) от других колонок."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 28480 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 22416 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 30428 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 30816 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 23976 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 24892 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 28496 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 32096 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 42168 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 38916 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 37604 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 30368 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 33768 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 25488 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 27136 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 32072 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 30052 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Список колонок для построения графиков\n",
    "columns = ['Stroke', 'BMI','Smoking', 'AlcoholDrinking', 'PhysicalHealth', 'MentalHealth','DiffWalking', 'Sex', 'AgeCategory',\n",
    "            'Race','Diabetic', 'PhysicalActivity', 'GenHealth', 'SleepTime', 'Asthma', 'KidneyDisease', 'SkinCancer' ]\n",
    "\n",
    "# Создание диаграмм зависимости\n",
    "for column in columns:\n",
    "    plt.figure(figsize=(8, 6))  # Установка размера графика\n",
    "    if pd.api.types.is_numeric_dtype(df[column]):  # Проверяем, является ли колонка числовой\n",
    "        # Проверяем, содержит ли колонка только два уникальных значения (0 и 1)\n",
    "        if df[column].nunique() == 2 and set(df[column].unique()).issubset({0, 1}):\n",
    "            # Если да, то строим столбчатую диаграмму\n",
    "            counts = df[column].value_counts()  # Считаем количество повторений каждого значения\n",
    "            counts.plot(kind='bar')  # Создаем столбчатую диаграмму\n",
    "            plt.title(f'Количество значений для {column}')\n",
    "            plt.xlabel(column)\n",
    "            plt.ylabel('Количество повторений')\n",
    "        else:\n",
    "            # Если колонка числовая, создаем диаграмму рассеяния\n",
    "            plt.scatter(df['HeartDisease'], df[column], alpha=0.5)  # Создаем диаграмму рассеяния\n",
    "            plt.title(f'Зависимость {column} от HeartDisease')\n",
    "            plt.xlabel('HeartDisease')\n",
    "            plt.ylabel(column)\n",
    "            plt.xticks([0, 1])  # Установка меток по оси X\n",
    "            plt.grid()  # Добавление сетки для удобства восприятия\n",
    "    else:\n",
    "        # Если колонка не числовая, строим столбчатую диаграмму\n",
    "        counts = df[column].value_counts()  # Считаем количество повторений каждого значения\n",
    "        counts.plot(kind='bar')  # Создаем столбчатую диаграмму\n",
    "        plt.title(f'Количество значений для {column}')\n",
    "        plt.xlabel(column)\n",
    "        plt.ylabel('Количество повторений')\n",
    "\n",
    "    plt.show()  # Отображение графика"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Далее создадим выборки. Разбивать данные на классы стоит по критерию сердечного приступа, потому что это то, что будет предсказывать модель."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "HeartDisease\n",
      "No     292422\n",
      "Yes     27373\n",
      "Name: count, dtype: int64\n",
      "\n",
      "Обучающая выборка:  (191877, 18)\n",
      "HeartDisease\n",
      "No     175453\n",
      "Yes     16424\n",
      "Name: count, dtype: int64\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 20708 characters]",
      "text/plain": [
       "<Figure size 200x200 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Контрольная выборка:  (63959, 18)\n",
      "HeartDisease\n",
      "No     58485\n",
      "Yes     5474\n",
      "Name: count, dtype: int64\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 20552 characters]",
      "text/plain": [
       "<Figure size 200x200 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Тестовая выборка:  (63959, 18)\n",
      "HeartDisease\n",
      "No     58484\n",
      "Yes     5475\n",
      "Name: count, dtype: int64\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 19500 characters]",
      "text/plain": [
       "<Figure size 200x200 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Вывод распределения количества наблюдений по меткам (классам)\n",
    "print(df.HeartDisease.value_counts())\n",
    "print()\n",
    "\n",
    "data = df.copy()\n",
    "\n",
    "df_train, df_val, df_test = split_stratified_into_train_val_test(\n",
    "   data, stratify_colname=\"HeartDisease\", frac_train=0.60, frac_val=0.20, frac_test=0.20\n",
    ")\n",
    "\n",
    "print(\"Обучающая выборка: \", df_train.shape)\n",
    "print(df_train.HeartDisease.value_counts())\n",
    "counts = df_train['HeartDisease'].value_counts()\n",
    "plt.figure(figsize=(2, 2))# Установка размера графика\n",
    "plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)# Построение круговой диаграммы\n",
    "plt.title('Распределение классов HeartDisease в обучающей выборке')# Добавление заголовка\n",
    "plt.show()# Отображение графика\n",
    "\n",
    "print(\"Контрольная выборка: \", df_val.shape)\n",
    "print(df_val.HeartDisease.value_counts())\n",
    "counts = df_val['HeartDisease'].value_counts()\n",
    "plt.figure(figsize=(2, 2))\n",
    "plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)\n",
    "plt.title('Распределение классов HeartDisease в контрольной выборке')\n",
    "plt.show()\n",
    "\n",
    "print(\"Тестовая выборка: \", df_test.shape)\n",
    "print(df_test.HeartDisease.value_counts())\n",
    "counts = df_test['HeartDisease'].value_counts()\n",
    "plt.figure(figsize=(2, 2))\n",
    "plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)\n",
    "plt.title('Распределение классов HeartDisease в тестовой выборке')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## По диаграммам видно, что выборка плохая, слишком большое смещение классов. Проведём аугментацию данных оверсемплингом. Этот метод позволяет увеличить количество примеров меньшинства."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Обучающая выборка после oversampling:  (351819, 51)\n",
      "HeartDisease\n",
      "Yes    176366\n",
      "No     175453\n",
      "Name: count, dtype: int64\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 32988 characters]",
      "text/plain": [
       "<Figure size 600x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from imblearn.over_sampling import ADASYN\n",
    "from sklearn.compose import ColumnTransformer\n",
    "from sklearn.preprocessing import OneHotEncoder\n",
    "\n",
    "categorical_features = ['Stroke','Smoking', 'AlcoholDrinking', 'DiffWalking', 'Sex', 'AgeCategory', 'Race', 'Diabetic', 'PhysicalActivity',\n",
    "                         'GenHealth', 'Asthma', 'KidneyDisease', 'SkinCancer']  # Ваши категориальные признаки\n",
    "numeric_features = ['BMI', 'PhysicalHealth', 'MentalHealth', 'SleepTime']  # Ваши числовые признаки\n",
    "\n",
    "# Создание пайплайна для обработки категориальных данных\n",
    "preprocessor = ColumnTransformer(\n",
    "    transformers=[\n",
    "        ('cat', OneHotEncoder(), categorical_features),  # OneHotEncoder для категориальных данных\n",
    "        ('num', 'passthrough', numeric_features)  # Оставляем числовые колонки без изменений\n",
    "    ]\n",
    ")\n",
    "\n",
    "# Создание экземпляра ADASYN\n",
    "ada = ADASYN()\n",
    "\n",
    "# Преобразование данных с помощью пайплайна\n",
    "X = preprocessor.fit_transform(df_train.drop(columns=['HeartDisease']))\n",
    "y = df_train['HeartDisease']\n",
    "\n",
    "# Применение ADASYN\n",
    "X_resampled, y_resampled = ada.fit_resample(X, y)\n",
    "\n",
    "# Создание нового DataFrame\n",
    "df_train_adasyn = pd.DataFrame(X_resampled)\n",
    "# Восстанавливаем названия столбцов для DataFrame\n",
    "ohe_columns = preprocessor.named_transformers_['cat'].get_feature_names_out(categorical_features)\n",
    "new_column_names = list(ohe_columns) + numeric_features\n",
    "df_train_adasyn.columns = new_column_names\n",
    "\n",
    "# Добавление целевой переменной\n",
    "df_train_adasyn['HeartDisease'] = y_resampled\n",
    "\n",
    "# Вывод информации о новой выборке\n",
    "print(\"Обучающая выборка после oversampling: \", df_train_adasyn.shape)\n",
    "print(df_train_adasyn['HeartDisease'].value_counts())\n",
    "\n",
    "# Визуализация\n",
    "counts = df_train_adasyn['HeartDisease'].value_counts()\n",
    "plt.figure(figsize=(6, 6))\n",
    "plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)\n",
    "plt.title('Распределение классов HeartDisease в тренировочной выборке после ADASYN')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Проведём балансировку методом андерсемплинга для уменьшения примеров большинства."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Обучающая выборка после undersampling:  (32848, 18)\n",
      "HeartDisease\n",
      "No     16424\n",
      "Yes    16424\n",
      "Name: count, dtype: int64\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 22564 characters]",
      "text/plain": [
       "<Figure size 200x200 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from imblearn.under_sampling import RandomUnderSampler\n",
    "\n",
    "rus = RandomUnderSampler()# Создание экземпляра RandomUnderSampler\n",
    "\n",
    "# Применение RandomUnderSampler\n",
    "X_resampled, y_resampled = rus.fit_resample(df_train.drop(columns=['HeartDisease']), df_train['HeartDisease'])\n",
    "\n",
    "# Создание нового DataFrame\n",
    "df_train_undersampled = pd.DataFrame(X_resampled)\n",
    "df_train_undersampled['HeartDisease'] = y_resampled  # Добавление целевой переменной\n",
    "\n",
    "# Вывод информации о новой выборке\n",
    "print(\"Обучающая выборка после undersampling: \", df_train_undersampled.shape)\n",
    "print(df_train_undersampled['HeartDisease'].value_counts())\n",
    "\n",
    "# Визуализация распределения классов\n",
    "counts = df_train_undersampled['HeartDisease'].value_counts()\n",
    "plt.figure(figsize=(2, 2))\n",
    "plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)\n",
    "plt.title('Распределение классов HeartDisease в тренировочной выборке после Undersampling')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Датасет 3. Stroke Prediction Dataset\n",
    "## По данным Всемирной организации здравоохранения (ВОЗ), инсульт является второй по значимости причиной смерти в мире, на его долю приходится примерно 11% от общего числа смертей.Этот набор данных используется для прогнозирования вероятности инсульта у пациента на основе входных параметров, таких как пол, возраст, различные заболевания и статус курения. Каждая строка в данных предоставляет актуальную информацию о пациенте.\n",
    "## В данном датасете объектами исследования являются пациенты. В нём содержатся данные атрибуты: id, gender, age, hypertension, heart_disease, ever_married, work_type, Residence_type, avg_glucose_level, bmi, smoking_status, stroke. Цель создания данного датасета- научиться прогнозировать инсульт."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "количество колонок: 12\n",
      "колонки: id, gender, age, hypertension, heart_disease, ever_married, work_type, Residence_type, avg_glucose_level, bmi, smoking_status, stroke\n"
     ]
    }
   ],
   "source": [
    "import pandas as pd \n",
    "df = pd.read_csv(\"C://Users//annal//aim//static//csv//healthcare-dataset-stroke-data.csv\")\n",
    "print('количество колонок: ' + str(df.columns.size))  \n",
    "print('колонки: ' + ', '.join(df.columns))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Получим сведения о пропущенных данных. Из вывода видно, что пропущенные данные есть в столбце bmi."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "id                     0\n",
      "gender                 0\n",
      "age                    0\n",
      "hypertension           0\n",
      "heart_disease          0\n",
      "ever_married           0\n",
      "work_type              0\n",
      "Residence_type         0\n",
      "avg_glucose_level      0\n",
      "bmi                  201\n",
      "smoking_status         0\n",
      "stroke                 0\n",
      "dtype: int64\n",
      "\n",
      "id                   False\n",
      "gender               False\n",
      "age                  False\n",
      "hypertension         False\n",
      "heart_disease        False\n",
      "ever_married         False\n",
      "work_type            False\n",
      "Residence_type       False\n",
      "avg_glucose_level    False\n",
      "bmi                   True\n",
      "smoking_status       False\n",
      "stroke               False\n",
      "dtype: bool\n",
      "\n",
      "bmi процент пустых значений: %3.93\n"
     ]
    }
   ],
   "source": [
    "# Количество пустых значений признаков\n",
    "print(df.isnull().sum())\n",
    "\n",
    "print()\n",
    "\n",
    "# Есть ли пустые значения признаков\n",
    "print(df.isnull().any())\n",
    "\n",
    "print()\n",
    "\n",
    "# Процент пустых значений признаков\n",
    "for i in df.columns:\n",
    "    null_rate = df[i].isnull().sum() / len(df) * 100\n",
    "    if null_rate > 0:\n",
    "        print(f\"{i} процент пустых значений: %{null_rate:.2f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Необходимо заполнить пропуски данными."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Замена пустых данных на медиану\n",
    "df[\"bmi\"] = df[\"bmi\"].fillna(df[\"bmi\"].median())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "id                   0\n",
      "gender               0\n",
      "age                  0\n",
      "hypertension         0\n",
      "heart_disease        0\n",
      "ever_married         0\n",
      "work_type            0\n",
      "Residence_type       0\n",
      "avg_glucose_level    0\n",
      "bmi                  0\n",
      "smoking_status       0\n",
      "stroke               0\n",
      "dtype: int64\n",
      "\n",
      "id                   False\n",
      "gender               False\n",
      "age                  False\n",
      "hypertension         False\n",
      "heart_disease        False\n",
      "ever_married         False\n",
      "work_type            False\n",
      "Residence_type       False\n",
      "avg_glucose_level    False\n",
      "bmi                  False\n",
      "smoking_status       False\n",
      "stroke               False\n",
      "dtype: bool\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Количество пустых значений признаков\n",
    "print(df.isnull().sum())\n",
    "\n",
    "print()\n",
    "\n",
    "# Есть ли пустые значения признаков\n",
    "print(df.isnull().any())\n",
    "\n",
    "print()\n",
    "\n",
    "# Процент пустых значений признаков\n",
    "for i in df.columns:\n",
    "    null_rate = df[i].isnull().sum() / len(df) * 100\n",
    "    if null_rate > 0:\n",
    "        print(f\"{i} процент пустых значений: %{null_rate:.2f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Далее необходимо ознакомиться с выбросами и усреднить их."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Колонка age:\n",
      "  Есть выбросы: Нет\n",
      "  Количество выбросов: 0\n",
      "  Минимальное значение: 0.08\n",
      "  Максимальное значение: 82.0\n",
      "  1-й квантиль (Q1): 25.0\n",
      "  3-й квантиль (Q3): 61.0\n",
      "\n",
      "Колонка avg_glucose_level:\n",
      "  Есть выбросы: Да\n",
      "  Количество выбросов: 627\n",
      "  Минимальное значение: 55.12\n",
      "  Максимальное значение: 169.35750000000002\n",
      "  1-й квантиль (Q1): 77.245\n",
      "  3-й квантиль (Q3): 114.09\n",
      "\n",
      "Колонка bmi:\n",
      "  Есть выбросы: Да\n",
      "  Количество выбросов: 126\n",
      "  Минимальное значение: 10.300000000000006\n",
      "  Максимальное значение: 46.29999999999999\n",
      "  1-й квантиль (Q1): 23.8\n",
      "  3-й квантиль (Q3): 32.8\n",
      "\n"
     ]
    }
   ],
   "source": [
    "numeric_columns = ['age', 'avg_glucose_level', 'bmi']\n",
    "for column in numeric_columns:\n",
    "    if pd.api.types.is_numeric_dtype(df[column]):  # Проверяем, является ли колонка числовой\n",
    "        q1 = df[column].quantile(0.25)  # Находим 1-й квантиль (Q1)\n",
    "        q3 = df[column].quantile(0.75)  # Находим 3-й квантиль (Q3)\n",
    "        iqr = q3 - q1  # Вычисляем межквантильный размах (IQR)\n",
    "\n",
    "        # Определяем границы для выбросов\n",
    "        lower_bound = q1 - 1.5 * iqr  # Нижняя граница\n",
    "        upper_bound = q3 + 1.5 * iqr  # Верхняя граница\n",
    "\n",
    "        # Подсчитываем количество выбросов\n",
    "        outliers = df[(df[column] < lower_bound) | (df[column] > upper_bound)]\n",
    "        outlier_count = outliers.shape[0]\n",
    "        \n",
    "        # Устраняем выбросы: заменяем значения ниже нижней границы на саму нижнюю границу, а выше верхней — на верхнюю\n",
    "        df[column] = df[column].apply(lambda x: lower_bound if x < lower_bound else upper_bound if x > upper_bound else x)\n",
    "\n",
    "        print(f\"Колонка {column}:\")\n",
    "        print(f\"  Есть выбросы: {'Да' if outlier_count > 0 else 'Нет'}\")\n",
    "        print(f\"  Количество выбросов: {outlier_count}\")\n",
    "        print(f\"  Минимальное значение: {df[column].min()}\")\n",
    "        print(f\"  Максимальное значение: {df[column].max()}\")\n",
    "        print(f\"  1-й квантиль (Q1): {q1}\")\n",
    "        print(f\"  3-й квантиль (Q3): {q3}\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Необходимо построить диаграммы для поиска зависимостей инсульта(stroke ) от других колонок."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 28576 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 30040 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 25008 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 26312 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 29088 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 36940 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 29008 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 41692 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 35116 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 38276 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Список колонок для построения графиков\n",
    "columns = ['gender', 'age', 'hypertension', 'heart_disease', 'ever_married',\n",
    "           'work_type', 'Residence_type', 'avg_glucose_level', 'bmi',\n",
    "           'smoking_status']\n",
    "\n",
    "# Создание диаграмм зависимости\n",
    "for column in columns:\n",
    "    plt.figure(figsize=(8, 6))  # Установка размера графика\n",
    "    if pd.api.types.is_numeric_dtype(df[column]):  # Проверяем, является ли колонка числовой\n",
    "        # Проверяем, содержит ли колонка только два уникальных значения (0 и 1)\n",
    "        if df[column].nunique() == 2 and set(df[column].unique()).issubset({0, 1}):\n",
    "            # Если да, то строим столбчатую диаграмму\n",
    "            counts = df[column].value_counts()  # Считаем количество повторений каждого значения\n",
    "            counts.plot(kind='bar')  # Создаем столбчатую диаграмму\n",
    "            plt.title(f'Количество значений для {column}')\n",
    "            plt.xlabel(column)\n",
    "            plt.ylabel('Количество повторений')\n",
    "        else:\n",
    "            # Если колонка числовая, создаем диаграмму рассеяния\n",
    "            plt.scatter(df['stroke'], df[column], alpha=0.5)  # Создаем диаграмму рассеяния\n",
    "            plt.title(f'Зависимость {column} от stroke')\n",
    "            plt.xlabel('stroke (0 = нет, 1 = да)')\n",
    "            plt.ylabel(column)\n",
    "            plt.xticks([0, 1])  # Установка меток по оси X\n",
    "            plt.grid()  # Добавление сетки для удобства восприятия\n",
    "    else:\n",
    "        # Если колонка не числовая, строим столбчатую диаграмму\n",
    "        counts = df[column].value_counts()  # Считаем количество повторений каждого значения\n",
    "        counts.plot(kind='bar')  # Создаем столбчатую диаграмму\n",
    "        plt.title(f'Количество значений для {column}')\n",
    "        plt.xlabel(column)\n",
    "        plt.ylabel('Количество повторений')\n",
    "\n",
    "    plt.show()  # Отображение графика"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Далее создадим выборки. Разбивать данные на классы стоит по критерию инсульта, потому что это то, что будет предсказывать модель."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "stroke\n",
      "0    4861\n",
      "1     249\n",
      "Name: count, dtype: int64\n",
      "\n",
      "Обучающая выборка:  (3066, 12)\n",
      "stroke\n",
      "0    2917\n",
      "1     149\n",
      "Name: count, dtype: int64\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 19092 characters]",
      "text/plain": [
       "<Figure size 200x200 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Контрольная выборка:  (1022, 12)\n",
      "stroke\n",
      "0    972\n",
      "1     50\n",
      "Name: count, dtype: int64\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 18996 characters]",
      "text/plain": [
       "<Figure size 200x200 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Тестовая выборка:  (1022, 12)\n",
      "stroke\n",
      "0    972\n",
      "1     50\n",
      "Name: count, dtype: int64\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 18912 characters]",
      "text/plain": [
       "<Figure size 200x200 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Вывод распределения количества наблюдений по меткам (классам)\n",
    "print(df.stroke.value_counts())\n",
    "print()\n",
    "\n",
    "data = df.copy()\n",
    "\n",
    "df_train, df_val, df_test = split_stratified_into_train_val_test(\n",
    "   data, stratify_colname=\"stroke\", frac_train=0.60, frac_val=0.20, frac_test=0.20\n",
    ")\n",
    "\n",
    "print(\"Обучающая выборка: \", df_train.shape)\n",
    "print(df_train.stroke.value_counts())\n",
    "counts = df_train['stroke'].value_counts()\n",
    "plt.figure(figsize=(2, 2))# Установка размера графика\n",
    "plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)# Построение круговой диаграммы\n",
    "plt.title('Распределение классов stroke в обучающей выборке')# Добавление заголовка\n",
    "plt.show()# Отображение графика\n",
    "\n",
    "print(\"Контрольная выборка: \", df_val.shape)\n",
    "print(df_val.stroke.value_counts())\n",
    "counts = df_val['stroke'].value_counts()\n",
    "plt.figure(figsize=(2, 2))\n",
    "plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)\n",
    "plt.title('Распределение классов stroke в контрольной выборке')\n",
    "plt.show()\n",
    "\n",
    "print(\"Тестовая выборка: \", df_test.shape)\n",
    "print(df_test.stroke.value_counts())\n",
    "counts = df_test['stroke'].value_counts()\n",
    "plt.figure(figsize=(2, 2))\n",
    "plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)\n",
    "plt.title('Распределение классов stroke в тестовой выборке')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## По диаграммам видно, что выборка плохая, слишком большое смещение классов. Проведём аугментацию данных оверсемплингом. Этот метод позволяет увеличить количество примеров меньшинства."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Обучающая выборка после oversampling:  (5807, 18)\n",
      "stroke\n",
      "0    2917\n",
      "1    2890\n",
      "Name: count, dtype: int64\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 31788 characters]",
      "text/plain": [
       "<Figure size 600x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "categorical_features = ['gender', 'ever_married', 'work_type', 'Residence_type']  # Ваши категориальные признаки\n",
    "numeric_features = ['age', 'hypertension', 'heart_disease', 'avg_glucose_level', 'bmi']  # Ваши числовые признаки\n",
    "\n",
    "# Создание пайплайна для обработки категориальных данных\n",
    "preprocessor = ColumnTransformer(\n",
    "    transformers=[\n",
    "        ('cat', OneHotEncoder(), categorical_features),  # OneHotEncoder для категориальных данных\n",
    "        ('num', 'passthrough', numeric_features)  # Оставляем числовые колонки без изменений\n",
    "    ]\n",
    ")\n",
    "\n",
    "# Создание экземпляра ADASYN\n",
    "ada = ADASYN()\n",
    "\n",
    "# Преобразование данных с помощью пайплайна\n",
    "X = preprocessor.fit_transform(df_train.drop(columns=['stroke']))\n",
    "y = df_train['stroke']\n",
    "\n",
    "# Применение ADASYN\n",
    "X_resampled, y_resampled = ada.fit_resample(X, y)\n",
    "\n",
    "# Создание нового DataFrame\n",
    "df_train_adasyn = pd.DataFrame(X_resampled)\n",
    "# Восстанавливаем названия столбцов для DataFrame\n",
    "ohe_columns = preprocessor.named_transformers_['cat'].get_feature_names_out(categorical_features)\n",
    "new_column_names = list(ohe_columns) + numeric_features\n",
    "df_train_adasyn.columns = new_column_names\n",
    "\n",
    "# Добавление целевой переменной\n",
    "df_train_adasyn['stroke'] = y_resampled\n",
    "\n",
    "# Вывод информации о новой выборке\n",
    "print(\"Обучающая выборка после oversampling: \", df_train_adasyn.shape)\n",
    "print(df_train_adasyn['stroke'].value_counts())\n",
    "\n",
    "# Визуализация\n",
    "counts = df_train_adasyn['stroke'].value_counts()\n",
    "plt.figure(figsize=(6, 6))\n",
    "plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)\n",
    "plt.title('Распределение классов Outcome в тренировачной выборке после ADASYN')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Проведём балансировку методом андерсемплинга для уменьшения примеров большинства."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Обучающая выборка после undersampling:  (298, 12)\n",
      "stroke\n",
      "0    149\n",
      "1    149\n",
      "Name: count, dtype: int64\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 21444 characters]",
      "text/plain": [
       "<Figure size 200x200 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Применение RandomUnderSampler\n",
    "X_resampled, y_resampled = rus.fit_resample(df_train.drop(columns=['stroke']), df_train['stroke'])\n",
    "\n",
    "# Создание нового DataFrame\n",
    "df_train_undersampled = pd.DataFrame(X_resampled)\n",
    "df_train_undersampled['stroke'] = y_resampled  # Добавление целевой переменной\n",
    "\n",
    "# Вывод информации о новой выборке\n",
    "print(\"Обучающая выборка после undersampling: \", df_train_undersampled.shape)\n",
    "print(df_train_undersampled['stroke'].value_counts())\n",
    "\n",
    "# Визуализация распределения классов\n",
    "counts = df_train_undersampled['stroke'].value_counts()\n",
    "plt.figure(figsize=(2, 2))\n",
    "plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)\n",
    "plt.title('Распределение классов Outcome в тренировочной выборке после Undersampling')\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
