{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "b833b51c",
      "metadata": {},
      "source": [
        "# Успеваемость учащихся (Вариант 3)\n",
        "\n",
        "Набор данных — синтетическое представление успеваемости: привычки учёбы, режим сна, социально-экономическое положение, посещаемость. Целевая переменная — **Grades** (оценки)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 42,
      "id": "50172c1c",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m25.3\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m26.0.1\u001b[0m\n",
            "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n",
            "Note: you may need to restart the kernel to use updated packages.\n"
          ]
        }
      ],
      "source": [
        "%pip -q install pandas numpy matplotlib seaborn scikit-learn"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 43,
      "id": "dc8e6237",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import pandas as pd\n",
        "import matplotlib.pyplot as plt\n",
        "import seaborn as sns\n",
        "\n",
        "from sklearn.model_selection import train_test_split\n",
        "from sklearn.preprocessing import StandardScaler\n",
        "from sklearn.pipeline import Pipeline\n",
        "from sklearn.base import clone\n",
        "from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score\n",
        "from sklearn.linear_model import LinearRegression\n",
        "from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor\n",
        "\n",
        "pd.set_option(\"display.max_columns\", 50)\n",
        "sns.set_style(\"whitegrid\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 44,
      "id": "df1da3e6",
      "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>Socioeconomic Score</th>\n",
              "      <th>Study Hours</th>\n",
              "      <th>Sleep Hours</th>\n",
              "      <th>Attendance (%)</th>\n",
              "      <th>Grades</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>0</th>\n",
              "      <td>0.95822</td>\n",
              "      <td>3.4</td>\n",
              "      <td>8.2</td>\n",
              "      <td>53.0</td>\n",
              "      <td>47.0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>1</th>\n",
              "      <td>0.85566</td>\n",
              "      <td>3.2</td>\n",
              "      <td>5.9</td>\n",
              "      <td>55.0</td>\n",
              "      <td>35.0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>2</th>\n",
              "      <td>0.68025</td>\n",
              "      <td>3.2</td>\n",
              "      <td>9.3</td>\n",
              "      <td>41.0</td>\n",
              "      <td>32.0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3</th>\n",
              "      <td>0.25936</td>\n",
              "      <td>3.2</td>\n",
              "      <td>8.2</td>\n",
              "      <td>47.0</td>\n",
              "      <td>34.0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>4</th>\n",
              "      <td>0.60447</td>\n",
              "      <td>3.8</td>\n",
              "      <td>10.0</td>\n",
              "      <td>75.0</td>\n",
              "      <td>33.0</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>"
            ],
            "text/plain": [
              "   Socioeconomic Score  Study Hours  Sleep Hours  Attendance (%)  Grades\n",
              "0              0.95822          3.4          8.2            53.0    47.0\n",
              "1              0.85566          3.2          5.9            55.0    35.0\n",
              "2              0.68025          3.2          9.3            41.0    32.0\n",
              "3              0.25936          3.2          8.2            47.0    34.0\n",
              "4              0.60447          3.8         10.0            75.0    33.0"
            ]
          },
          "execution_count": 44,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Загрузка датасета\n",
        "df = pd.read_csv(\"data.csv\")\n",
        "df.head()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "029f3bcf",
      "metadata": {},
      "source": [
        "## Очистка и предобработка\n",
        "\n",
        "- Удаляем дубликаты.\n",
        "- Проверяем пропуски и типы данных."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 45,
      "id": "cbff76d0",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Пропуски:\n",
            "Socioeconomic Score    0\n",
            "Study Hours            0\n",
            "Sleep Hours            0\n",
            "Attendance (%)         0\n",
            "Grades                 0\n",
            "dtype: int64\n",
            "\n",
            "<class 'pandas.DataFrame'>\n",
            "RangeIndex: 1388 entries, 0 to 1387\n",
            "Data columns (total 5 columns):\n",
            " #   Column               Non-Null Count  Dtype  \n",
            "---  ------               --------------  -----  \n",
            " 0   Socioeconomic Score  1388 non-null   float64\n",
            " 1   Study Hours          1388 non-null   float64\n",
            " 2   Sleep Hours          1388 non-null   float64\n",
            " 3   Attendance (%)       1388 non-null   float64\n",
            " 4   Grades               1388 non-null   float64\n",
            "dtypes: float64(5)\n",
            "memory usage: 54.3 KB\n",
            "\n",
            "Доля пропусков по столбцам:\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "Socioeconomic Score    0.0\n",
              "Study Hours            0.0\n",
              "Sleep Hours            0.0\n",
              "Attendance (%)         0.0\n",
              "Grades                 0.0\n",
              "dtype: float64"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Размер после очистки: 1388\n"
          ]
        }
      ],
      "source": [
        "df = df.drop_duplicates()\n",
        "print(\"Пропуски:\")\n",
        "print(df.isna().sum())\n",
        "print()\n",
        "df.info()\n",
        "print()\n",
        "print(\"Доля пропусков по столбцам:\")\n",
        "display(df.isna().mean().sort_values(ascending=False))\n",
        "df = df.dropna()\n",
        "print(\"Размер после очистки:\", len(df))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "abc3d8c9",
      "metadata": {},
      "source": [
        "## Задание 1. Анализ исходных данных. Постановка задачи.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 46,
      "id": "959f6247",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "image/png": "[encoded data, 24576 characters]",
            "text/plain": [
              "<Figure size 600x400 with 1 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# График 1: распределение целевой переменной (Grades)\n",
        "fig, ax = plt.subplots(figsize=(6, 4))\n",
        "ax.hist(df[\"Grades\"], bins=30, edgecolor=\"black\", alpha=0.7)\n",
        "ax.set_xlabel(\"Grades (оценки)\")\n",
        "ax.set_ylabel(\"Частота\")\n",
        "ax.set_title(\"Распределение оценок учащихся\")\n",
        "plt.tight_layout()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "67234983",
      "metadata": {},
      "source": [
        "**Описание графика 1:** по горизонтали отложены оценки (Grades), по вертикали — частота. Чаще всего встречаются оценки 30-40 баллов."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 47,
      "id": "51b06bbe",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "image/png": "[encoded data, 101632 characters]",
            "text/plain": [
              "<Figure size 1200x400 with 2 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# График 2: зависимость оценок от часов учёбы и посещаемости\n",
        "fig, axes = plt.subplots(1, 2, figsize=(12, 4))\n",
        "axes[0].scatter(df[\"Study Hours\"], df[\"Grades\"], alpha=0.4, s=15)\n",
        "axes[0].set_xlabel(\"Study Hours (часы учёбы)\")\n",
        "axes[0].set_ylabel(\"Grades\")\n",
        "axes[0].set_title(\"Зависимость оценок от часов учёбы\")\n",
        "axes[1].scatter(df[\"Attendance (%)\"], df[\"Grades\"], alpha=0.4, s=15)\n",
        "axes[1].set_xlabel(\"Attendance (%) (посещаемость)\")\n",
        "axes[1].set_ylabel(\"Grades\")\n",
        "axes[1].set_title(\"Зависимость оценок от посещаемости\")\n",
        "plt.tight_layout()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e0757c93",
      "metadata": {},
      "source": [
        "**Описание графика 2:** видно, что до 4.5 часов учёбы (предположительно в неделю) оценки ограничены 35-40 баллами, однако после 5 часов учёбы видна линейная нижня граница, например уделяя не менее 6 часов, нельзя получить балл ниже 40, а для 8 часов - 52 баллов.\n",
        "Посещаемость на оценки влияет схожим образом: до 80% большинство получают оценки ниже 40, а после видно улучшение оценок."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 48,
      "id": "a609d10f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "image/png": "[encoded data, 60028 characters]",
            "text/plain": [
              "<Figure size 600x500 with 2 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# График 3: корреляционная матрица числовых признаков и целевой переменной\n",
        "num_df = df.select_dtypes(include=[np.number])\n",
        "corr = num_df.corr()\n",
        "fig, ax = plt.subplots(figsize=(6, 5))\n",
        "sns.heatmap(corr, annot=True, fmt=\".2f\", cmap=\"coolwarm\", center=0, ax=ax, square=True)\n",
        "ax.set_title(\"Корреляции признаков и Grades\")\n",
        "plt.tight_layout()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "36894607",
      "metadata": {},
      "source": [
        "**Описание графика 3:** тепловая карта корреляций. Видимо, что сильнее всего на оценки влияет количество часов, уделяемое учёбе. Посещаемость и социально-экономические факторы также влияют, но слабее."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0ba40602",
      "metadata": {},
      "source": [
        "### Выводы о качестве датасета\n",
        "\n",
        "- **Очистка и предобработка:** выполнены удаление дубликатов и проверка пропусков.\n",
        "- **Качество:** признаки числовые, целевая переменная количественная; зависимости от факторов (часы учёбы, посещаемость, социально-экономический балл) позволяют строить регрессионную модель, т.к. данные не являются случайными.\n",
        "- **Необходимость новых переменных:** по результатам корреляций и графиков можно при необходимости ввести составной признак (например, «учебная активность» = часы учёбы × посещаемость) — это проверяется в Задании 3."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2839982a",
      "metadata": {},
      "source": [
        "## Задание 2. Построить модель прогнозирования оценки обучающегося\n",
        "\n",
        "Целевая переменная — **Grades** (количественная). Используем несколько моделей регрессии для сравнения точности на обучающей и тестовой выборках."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 49,
      "id": "7a5c03de",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Обучающая выборка: 1110 тестовая: 278\n"
          ]
        }
      ],
      "source": [
        "# Разделение на признаки и целевую переменную, train/test\n",
        "target = \"Grades\"\n",
        "feature_cols = [c for c in df.columns if c != target]\n",
        "X = df[feature_cols]\n",
        "y = df[target]\n",
        "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n",
        "print(\"Обучающая выборка:\", X_train.shape[0], \"тестовая:\", X_test.shape[0])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 50,
      "id": "463c4576",
      "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>model</th>\n",
              "      <th>split</th>\n",
              "      <th>MAE</th>\n",
              "      <th>RMSE</th>\n",
              "      <th>R2</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>0</th>\n",
              "      <td>LinearRegression</td>\n",
              "      <td>train</td>\n",
              "      <td>3.492656</td>\n",
              "      <td>4.523889</td>\n",
              "      <td>0.779763</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>1</th>\n",
              "      <td>LinearRegression</td>\n",
              "      <td>test</td>\n",
              "      <td>3.386175</td>\n",
              "      <td>4.390050</td>\n",
              "      <td>0.744667</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>2</th>\n",
              "      <td>RandomForest</td>\n",
              "      <td>train</td>\n",
              "      <td>0.363068</td>\n",
              "      <td>0.517689</td>\n",
              "      <td>0.997116</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3</th>\n",
              "      <td>RandomForest</td>\n",
              "      <td>test</td>\n",
              "      <td>0.882644</td>\n",
              "      <td>1.189566</td>\n",
              "      <td>0.981252</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>4</th>\n",
              "      <td>GradientBoosting</td>\n",
              "      <td>train</td>\n",
              "      <td>0.718789</td>\n",
              "      <td>0.933955</td>\n",
              "      <td>0.990613</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>5</th>\n",
              "      <td>GradientBoosting</td>\n",
              "      <td>test</td>\n",
              "      <td>0.943528</td>\n",
              "      <td>1.285282</td>\n",
              "      <td>0.978114</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>"
            ],
            "text/plain": [
              "              model  split       MAE      RMSE        R2\n",
              "0  LinearRegression  train  3.492656  4.523889  0.779763\n",
              "1  LinearRegression   test  3.386175  4.390050  0.744667\n",
              "2      RandomForest  train  0.363068  0.517689  0.997116\n",
              "3      RandomForest   test  0.882644  1.189566  0.981252\n",
              "4  GradientBoosting  train  0.718789  0.933955  0.990613\n",
              "5  GradientBoosting   test  0.943528  1.285282  0.978114"
            ]
          },
          "execution_count": 50,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "models = {\n",
        "    \"LinearRegression\": LinearRegression(),\n",
        "    \"RandomForest\": RandomForestRegressor(n_estimators=200, random_state=42),\n",
        "    \"GradientBoosting\": GradientBoostingRegressor(random_state=42),\n",
        "}\n",
        "\n",
        "def evaluate_models(X_tr, y_tr, X_te, y_te, model_dict):\n",
        "    \"\"\"Обучает модели, возвращает таблицу метрик на train и test.\"\"\"\n",
        "    rows = []\n",
        "    for name, model in model_dict.items():\n",
        "        pipe = Pipeline([\n",
        "            (\"scaler\", StandardScaler()),\n",
        "            (\"model\", clone(model)),\n",
        "        ])\n",
        "        pipe.fit(X_tr, y_tr)\n",
        "        for split, (X_s, y_s) in [(\"train\", (X_tr, y_tr)), (\"test\", (X_te, y_te))]:\n",
        "            pred = pipe.predict(X_s)\n",
        "            rows.append({\n",
        "                \"model\": name,\n",
        "                \"split\": split,\n",
        "                \"MAE\": mean_absolute_error(y_s, pred),\n",
        "                \"RMSE\": np.sqrt(mean_squared_error(y_s, pred)),\n",
        "                \"R2\": r2_score(y_s, pred),\n",
        "            })\n",
        "    return pd.DataFrame(rows)\n",
        "\n",
        "results = evaluate_models(X_train, y_train, X_test, y_test, models)\n",
        "results"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 51,
      "id": "4f79bc21",
      "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 tr th {\n",
              "        text-align: left;\n",
              "    }\n",
              "\n",
              "    .dataframe thead tr:last-of-type th {\n",
              "        text-align: right;\n",
              "    }\n",
              "</style>\n",
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr>\n",
              "      <th></th>\n",
              "      <th colspan=\"2\" halign=\"left\">MAE</th>\n",
              "      <th colspan=\"2\" halign=\"left\">R2</th>\n",
              "      <th colspan=\"2\" halign=\"left\">RMSE</th>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>split</th>\n",
              "      <th>test</th>\n",
              "      <th>train</th>\n",
              "      <th>test</th>\n",
              "      <th>train</th>\n",
              "      <th>test</th>\n",
              "      <th>train</th>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>model</th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>GradientBoosting</th>\n",
              "      <td>0.943528</td>\n",
              "      <td>0.718789</td>\n",
              "      <td>0.978114</td>\n",
              "      <td>0.990613</td>\n",
              "      <td>1.285282</td>\n",
              "      <td>0.933955</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>LinearRegression</th>\n",
              "      <td>3.386175</td>\n",
              "      <td>3.492656</td>\n",
              "      <td>0.744667</td>\n",
              "      <td>0.779763</td>\n",
              "      <td>4.390050</td>\n",
              "      <td>4.523889</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>RandomForest</th>\n",
              "      <td>0.882644</td>\n",
              "      <td>0.363068</td>\n",
              "      <td>0.981252</td>\n",
              "      <td>0.997116</td>\n",
              "      <td>1.189566</td>\n",
              "      <td>0.517689</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>"
            ],
            "text/plain": [
              "                       MAE                  R2                RMSE          \n",
              "split                 test     train      test     train      test     train\n",
              "model                                                                       \n",
              "GradientBoosting  0.943528  0.718789  0.978114  0.990613  1.285282  0.933955\n",
              "LinearRegression  3.386175  3.492656  0.744667  0.779763  4.390050  4.523889\n",
              "RandomForest      0.882644  0.363068  0.981252  0.997116  1.189566  0.517689"
            ]
          },
          "execution_count": 51,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Сводная таблица метрик (train / test)\n",
        "pivot_results = results.pivot_table(index=\"model\", columns=\"split\", values=[\"MAE\", \"RMSE\", \"R2\"])\n",
        "pivot_results"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 52,
      "id": "9737f501",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "image/png": "[encoded data, 36004 characters]",
            "text/plain": [
              "<Figure size 1400x400 with 3 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# Визуализация метрик на тестовой выборке\n",
        "test_results = results[results[\"split\"] == \"test\"]\n",
        "fig, axes = plt.subplots(1, 3, figsize=(14, 4))\n",
        "sns.barplot(data=test_results, x=\"model\", y=\"MAE\", ax=axes[0])\n",
        "sns.barplot(data=test_results, x=\"model\", y=\"RMSE\", ax=axes[1])\n",
        "sns.barplot(data=test_results, x=\"model\", y=\"R2\", ax=axes[2])\n",
        "axes[0].set_title(\"MAE (test)\")\n",
        "axes[1].set_title(\"RMSE (test)\")\n",
        "axes[2].set_title(\"R2 (test)\")\n",
        "plt.tight_layout()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5e66fccb",
      "metadata": {},
      "source": [
        "### Обоснование выбора метрик\n",
        "\n",
        "- **MAE** (Mean Absolute Error) — средняя абсолютная ошибка в тех же единицах, что и оценки; легко интерпретировать, устойчива к выбросам.\n",
        "- **RMSE** (Root Mean Squared Error) — корень из средней квадратичной ошибки; сильнее штрафует большие отклонения, полезна для оценки риска крупных промахов.\n",
        "- **R2** (коэффициент детерминации) — доля дисперсии целевой переменной, объяснённая моделью; 0 — модель не лучше предсказания средним, 1 — идеальное совпадение."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a909c402",
      "metadata": {},
      "source": [
        "## Задание 3. Новый признак и анализ изменения точности\n",
        "\n",
        "Формируем новый признак вручную: **Study effort** (учебная активность) = часы учёбы × посещаемость (нормализованная 0–1). Он объединяет два важных фактора успеваемости. Далее сравниваем метрики моделей до и после добавления этого признака."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 53,
      "id": "ae3ee431",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Новые признаки: ['Socioeconomic Score', 'Study Hours', 'Sleep Hours', 'Attendance (%)', 'Study_effort']\n"
          ]
        },
        {
          "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>Socioeconomic Score</th>\n",
              "      <th>Study Hours</th>\n",
              "      <th>Sleep Hours</th>\n",
              "      <th>Attendance (%)</th>\n",
              "      <th>Study_effort</th>\n",
              "      <th>Grades</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>0</th>\n",
              "      <td>0.95822</td>\n",
              "      <td>3.4</td>\n",
              "      <td>8.2</td>\n",
              "      <td>53.0</td>\n",
              "      <td>1.802</td>\n",
              "      <td>47.0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>1</th>\n",
              "      <td>0.85566</td>\n",
              "      <td>3.2</td>\n",
              "      <td>5.9</td>\n",
              "      <td>55.0</td>\n",
              "      <td>1.760</td>\n",
              "      <td>35.0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>2</th>\n",
              "      <td>0.68025</td>\n",
              "      <td>3.2</td>\n",
              "      <td>9.3</td>\n",
              "      <td>41.0</td>\n",
              "      <td>1.312</td>\n",
              "      <td>32.0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3</th>\n",
              "      <td>0.25936</td>\n",
              "      <td>3.2</td>\n",
              "      <td>8.2</td>\n",
              "      <td>47.0</td>\n",
              "      <td>1.504</td>\n",
              "      <td>34.0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>4</th>\n",
              "      <td>0.60447</td>\n",
              "      <td>3.8</td>\n",
              "      <td>10.0</td>\n",
              "      <td>75.0</td>\n",
              "      <td>2.850</td>\n",
              "      <td>33.0</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>"
            ],
            "text/plain": [
              "   Socioeconomic Score  Study Hours  Sleep Hours  Attendance (%)  \\\n",
              "0              0.95822          3.4          8.2            53.0   \n",
              "1              0.85566          3.2          5.9            55.0   \n",
              "2              0.68025          3.2          9.3            41.0   \n",
              "3              0.25936          3.2          8.2            47.0   \n",
              "4              0.60447          3.8         10.0            75.0   \n",
              "\n",
              "   Study_effort  Grades  \n",
              "0         1.802    47.0  \n",
              "1         1.760    35.0  \n",
              "2         1.312    32.0  \n",
              "3         1.504    34.0  \n",
              "4         2.850    33.0  "
            ]
          },
          "execution_count": 53,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Новый признак: учебная активность (часы учёбы × доля посещаемости)\n",
        "df_ext = df.copy()\n",
        "df_ext[\"Study_effort\"] = df_ext[\"Study Hours\"] * (df_ext[\"Attendance (%)\"] / 100.0)\n",
        "feature_cols_ext = feature_cols + [\"Study_effort\"]\n",
        "X_ext = df_ext[feature_cols_ext]\n",
        "y_ext = df_ext[target]\n",
        "X_train_ext, X_test_ext, y_train_ext, y_test_ext = train_test_split(\n",
        "    X_ext, y_ext, test_size=0.2, random_state=42\n",
        ")\n",
        "print(\"Новые признаки:\", feature_cols_ext)\n",
        "df_ext[feature_cols_ext + [target]].head()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 54,
      "id": "27f43283",
      "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>model</th>\n",
              "      <th>split</th>\n",
              "      <th>MAE</th>\n",
              "      <th>RMSE</th>\n",
              "      <th>R2</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>0</th>\n",
              "      <td>LinearRegression</td>\n",
              "      <td>train</td>\n",
              "      <td>3.236795</td>\n",
              "      <td>4.305121</td>\n",
              "      <td>0.800548</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>1</th>\n",
              "      <td>LinearRegression</td>\n",
              "      <td>test</td>\n",
              "      <td>3.217861</td>\n",
              "      <td>4.224914</td>\n",
              "      <td>0.763515</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>2</th>\n",
              "      <td>RandomForest</td>\n",
              "      <td>train</td>\n",
              "      <td>0.369018</td>\n",
              "      <td>0.529524</td>\n",
              "      <td>0.996983</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3</th>\n",
              "      <td>RandomForest</td>\n",
              "      <td>test</td>\n",
              "      <td>0.897176</td>\n",
              "      <td>1.218256</td>\n",
              "      <td>0.980337</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>4</th>\n",
              "      <td>GradientBoosting</td>\n",
              "      <td>train</td>\n",
              "      <td>0.716615</td>\n",
              "      <td>0.928923</td>\n",
              "      <td>0.990714</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>5</th>\n",
              "      <td>GradientBoosting</td>\n",
              "      <td>test</td>\n",
              "      <td>0.940395</td>\n",
              "      <td>1.281435</td>\n",
              "      <td>0.978245</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>"
            ],
            "text/plain": [
              "              model  split       MAE      RMSE        R2\n",
              "0  LinearRegression  train  3.236795  4.305121  0.800548\n",
              "1  LinearRegression   test  3.217861  4.224914  0.763515\n",
              "2      RandomForest  train  0.369018  0.529524  0.996983\n",
              "3      RandomForest   test  0.897176  1.218256  0.980337\n",
              "4  GradientBoosting  train  0.716615  0.928923  0.990714\n",
              "5  GradientBoosting   test  0.940395  1.281435  0.978245"
            ]
          },
          "execution_count": 54,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Обучаем те же модели на данных с новым признаком\n",
        "results_with_feature = evaluate_models(\n",
        "    X_train_ext, y_train_ext, X_test_ext, y_test_ext, models\n",
        ")\n",
        "results_with_feature"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 55,
      "id": "fd94163a",
      "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>MAE_before</th>\n",
              "      <th>MAE_after</th>\n",
              "      <th>RMSE_before</th>\n",
              "      <th>RMSE_after</th>\n",
              "      <th>R2_before</th>\n",
              "      <th>R2_after</th>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>model</th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>LinearRegression</th>\n",
              "      <td>3.386175</td>\n",
              "      <td>3.217861</td>\n",
              "      <td>4.390050</td>\n",
              "      <td>4.224914</td>\n",
              "      <td>0.744667</td>\n",
              "      <td>0.763515</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>RandomForest</th>\n",
              "      <td>0.882644</td>\n",
              "      <td>0.897176</td>\n",
              "      <td>1.189566</td>\n",
              "      <td>1.218256</td>\n",
              "      <td>0.981252</td>\n",
              "      <td>0.980337</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>GradientBoosting</th>\n",
              "      <td>0.943528</td>\n",
              "      <td>0.940395</td>\n",
              "      <td>1.285282</td>\n",
              "      <td>1.281435</td>\n",
              "      <td>0.978114</td>\n",
              "      <td>0.978245</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>"
            ],
            "text/plain": [
              "                  MAE_before  MAE_after  RMSE_before  RMSE_after  R2_before  \\\n",
              "model                                                                         \n",
              "LinearRegression    3.386175   3.217861     4.390050    4.224914   0.744667   \n",
              "RandomForest        0.882644   0.897176     1.189566    1.218256   0.981252   \n",
              "GradientBoosting    0.943528   0.940395     1.285282    1.281435   0.978114   \n",
              "\n",
              "                  R2_after  \n",
              "model                       \n",
              "LinearRegression  0.763515  \n",
              "RandomForest      0.980337  \n",
              "GradientBoosting  0.978245  "
            ]
          },
          "execution_count": 55,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Сравнение метрик на тесте: без нового признака vs с признаком Study_effort\n",
        "tb = results[results[\"split\"] == \"test\"].set_index(\"model\")\n",
        "ta = results_with_feature[results_with_feature[\"split\"] == \"test\"].set_index(\"model\")\n",
        "comparison = pd.DataFrame({\n",
        "    \"MAE_before\": tb[\"MAE\"],\n",
        "    \"MAE_after\": ta[\"MAE\"],\n",
        "    \"RMSE_before\": tb[\"RMSE\"],\n",
        "    \"RMSE_after\": ta[\"RMSE\"],\n",
        "    \"R2_before\": tb[\"R2\"],\n",
        "    \"R2_after\": ta[\"R2\"],\n",
        "})\n",
        "comparison"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "897f43ad",
      "metadata": {},
      "source": [
        "### Анализ изменения точности\n",
        "\n",
        "Добавление нового признака на основе уже существующих не принесло никаких изменений. Это связана с тем, что модель и без умеет учитывать все признаки, а новый столбец, полученный умножением двух других, новой информации не несёт."
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": ".venv (3.14.2)",
      "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.14.2"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
