diff --git a/lab_4/lab4.1.ipynb b/lab_4/lab4.1.ipynb new file mode 100644 index 0000000..2f1598d --- /dev/null +++ b/lab_4/lab4.1.ipynb @@ -0,0 +1,6162 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Начало лабораторной\n", + "\n", + "Цены на кофе - https://www.kaggle.com/datasets/mayankanand2701/starbucks-stock-price-dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Атрибуты\n", + "\n", + "Date — Дата\n", + "\n", + "Open — Открытие\n", + "\n", + "High — Макс. цена\n", + "\n", + "Low — Мин. цена\n", + "\n", + "Close — Закрытие\n", + "\n", + "Adj Close — Скорректированная цена закрытия\n", + "\n", + "Volume — Объем торгов" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Бизнес-цели\n", + "\n", + "__1. Оценка волатильности акций:__\n", + "\n", + "\n", + "Описание: Прогнозировать волатильность акций на основе изменений в ценах открытий, максимума, минимума и объема торгов.\n", + "Целевая переменная: Разница между высокой и низкой ценой (High - Low). (среднее значение)\n", + "\n", + "__2. Прогнозирование цены закрытия акций:__\n", + "\n", + "\n", + "Описание: Оценить, какая будет цена закрытия акций Starbucks на следующий день или через несколько дней на основе исторических данных.\n", + "Целевая переменная: Цена закрытия (Close). (среднее значение)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Определение достижимого уровня качества модели для первой задачи " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Подготовка данных__" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Загрузка данных и создание целевой переменной" + ] + }, + { + "cell_type": "code", + "execution_count": 160, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Среднее значение поля 'Volume': 14704589.99726232\n", + " Date Open High Low Close Adj Close Volume \\\n", + "0 1992-06-26 0.328125 0.347656 0.320313 0.335938 0.260703 224358400 \n", + "1 1992-06-29 0.339844 0.367188 0.332031 0.359375 0.278891 58732800 \n", + "2 1992-06-30 0.367188 0.371094 0.343750 0.347656 0.269797 34777600 \n", + "3 1992-07-01 0.351563 0.359375 0.339844 0.355469 0.275860 18316800 \n", + "4 1992-07-02 0.359375 0.359375 0.347656 0.355469 0.275860 13996800 \n", + "\n", + " above_average_volume volatility \n", + "0 1 0.027343 \n", + "1 1 0.035157 \n", + "2 1 0.027344 \n", + "3 1 0.019531 \n", + "4 0 0.011719 \n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "from sklearn import set_config\n", + "\n", + "# Установим параметры для вывода\n", + "set_config(transform_output=\"pandas\")\n", + "\n", + "# Загружаем набор данных\n", + "df = pd.read_csv(\".//static//csv//Starbucks Dataset.csv\")\n", + "\n", + "# Устанавливаем случайное состояние\n", + "random_state = 42\n", + "\n", + "# Рассчитываем среднее значение объема\n", + "average_volume = df['Volume'].mean()\n", + "print(f\"Среднее значение поля 'Volume': {average_volume}\")\n", + "\n", + "# Создаем новую переменную, указывающую, превышает ли объем средний\n", + "df['above_average_volume'] = (df['Volume'] > average_volume).astype(int)\n", + "\n", + "# Рассчитываем волатильность (разницу между высокими и низкими значениями)\n", + "df['volatility'] = df['High'] - df['Low']\n", + "\n", + "# Выводим первые строки измененной таблицы для проверки\n", + "print(df.head())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Разделение набора данных на обучающую и тестовые выборки (80/20) для задачи классификации\n", + "\n", + "Целевой признак -- above_average_close" + ] + }, + { + "cell_type": "code", + "execution_count": 161, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'X_train'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
DateOpenHighLowCloseAdj CloseVolumeabove_average_volumevolatility
71592020-11-2798.48000398.98000398.27999998.66000491.604065216970000.700004
45052010-05-1413.63000013.66500013.09000013.25500010.3290992308180010.575000
4211994-02-240.7109380.7265630.6953130.6992190.542626926400000.031250
15951998-10-192.3710942.4257812.2773442.3242191.8037012128480010.148437
36762007-01-3017.59499917.68000017.26000017.28000113.4100762837220010.420000
..............................
59762016-03-1859.91000060.45000159.43000059.70000150.5623471431360001.020001
13051997-08-252.5429692.7031252.5390632.6796882.0795612820960010.164062
60852016-08-2356.16999856.54000156.00000056.40000248.101521782790000.540001
54702014-03-1737.40499937.49499936.91000037.09000030.5694101101980000.584999
57812015-06-1051.79999952.86000151.66000052.68999944.214481800360001.200001
\n", + "

6428 rows × 9 columns

\n", + "
" + ], + "text/plain": [ + " Date Open High Low Close Adj Close \\\n", + "7159 2020-11-27 98.480003 98.980003 98.279999 98.660004 91.604065 \n", + "4505 2010-05-14 13.630000 13.665000 13.090000 13.255000 10.329099 \n", + "421 1994-02-24 0.710938 0.726563 0.695313 0.699219 0.542626 \n", + "1595 1998-10-19 2.371094 2.425781 2.277344 2.324219 1.803701 \n", + "3676 2007-01-30 17.594999 17.680000 17.260000 17.280001 13.410076 \n", + "... ... ... ... ... ... ... \n", + "5976 2016-03-18 59.910000 60.450001 59.430000 59.700001 50.562347 \n", + "1305 1997-08-25 2.542969 2.703125 2.539063 2.679688 2.079561 \n", + "6085 2016-08-23 56.169998 56.540001 56.000000 56.400002 48.101521 \n", + "5470 2014-03-17 37.404999 37.494999 36.910000 37.090000 30.569410 \n", + "5781 2015-06-10 51.799999 52.860001 51.660000 52.689999 44.214481 \n", + "\n", + " Volume above_average_volume volatility \n", + "7159 2169700 0 0.700004 \n", + "4505 23081800 1 0.575000 \n", + "421 9264000 0 0.031250 \n", + "1595 21284800 1 0.148437 \n", + "3676 28372200 1 0.420000 \n", + "... ... ... ... \n", + "5976 14313600 0 1.020001 \n", + "1305 28209600 1 0.164062 \n", + "6085 7827900 0 0.540001 \n", + "5470 11019800 0 0.584999 \n", + "5781 8003600 0 1.200001 \n", + "\n", + "[6428 rows x 9 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "'y_train'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
above_average_volume
71590
45051
4210
15951
36761
......
59760
13051
60850
54700
57810
\n", + "

6428 rows × 1 columns

\n", + "
" + ], + "text/plain": [ + " above_average_volume\n", + "7159 0\n", + "4505 1\n", + "421 0\n", + "1595 1\n", + "3676 1\n", + "... ...\n", + "5976 0\n", + "1305 1\n", + "6085 0\n", + "5470 0\n", + "5781 0\n", + "\n", + "[6428 rows x 1 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "'X_test'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
DateOpenHighLowCloseAdj CloseVolumeabove_average_volumevolatility
3121993-09-210.7460940.7539060.7265630.7343750.569909805120000.027343
61182016-10-1053.52999953.59999853.27000053.29999945.457634722430000.329998
17751999-07-083.1328133.1406253.0468753.0781252.3887674310400010.093750
66212018-10-0956.83000259.70000156.81000157.70999951.2570652485570012.890000
43632009-10-2010.39000010.47500010.19000010.2650007.9661101184500000.285000
..............................
44722010-03-2912.31500012.38500012.14500012.3050009.5492431371800000.240000
59442016-02-0260.66000060.90000260.18000060.70000151.409283940740000.720002
68392019-08-2296.58999696.84999895.69999796.48999887.342232514620001.150001
271992-08-050.4257810.4257810.4023440.4101560.318300951680000.023437
39022007-12-2010.07500010.28000010.02500010.2650007.9661102299620010.255000
\n", + "

1608 rows × 9 columns

\n", + "
" + ], + "text/plain": [ + " Date Open High Low Close Adj Close \\\n", + "312 1993-09-21 0.746094 0.753906 0.726563 0.734375 0.569909 \n", + "6118 2016-10-10 53.529999 53.599998 53.270000 53.299999 45.457634 \n", + "1775 1999-07-08 3.132813 3.140625 3.046875 3.078125 2.388767 \n", + "6621 2018-10-09 56.830002 59.700001 56.810001 57.709999 51.257065 \n", + "4363 2009-10-20 10.390000 10.475000 10.190000 10.265000 7.966110 \n", + "... ... ... ... ... ... ... \n", + "4472 2010-03-29 12.315000 12.385000 12.145000 12.305000 9.549243 \n", + "5944 2016-02-02 60.660000 60.900002 60.180000 60.700001 51.409283 \n", + "6839 2019-08-22 96.589996 96.849998 95.699997 96.489998 87.342232 \n", + "27 1992-08-05 0.425781 0.425781 0.402344 0.410156 0.318300 \n", + "3902 2007-12-20 10.075000 10.280000 10.025000 10.265000 7.966110 \n", + "\n", + " Volume above_average_volume volatility \n", + "312 8051200 0 0.027343 \n", + "6118 7224300 0 0.329998 \n", + "1775 43104000 1 0.093750 \n", + "6621 24855700 1 2.890000 \n", + "4363 11845000 0 0.285000 \n", + "... ... ... ... \n", + "4472 13718000 0 0.240000 \n", + "5944 9407400 0 0.720002 \n", + "6839 5146200 0 1.150001 \n", + "27 9516800 0 0.023437 \n", + "3902 22996200 1 0.255000 \n", + "\n", + "[1608 rows x 9 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "'y_test'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
above_average_volume
3120
61180
17751
66211
43630
......
44720
59440
68390
270
39021
\n", + "

1608 rows × 1 columns

\n", + "
" + ], + "text/plain": [ + " above_average_volume\n", + "312 0\n", + "6118 0\n", + "1775 1\n", + "6621 1\n", + "4363 0\n", + "... ...\n", + "4472 0\n", + "5944 0\n", + "6839 0\n", + "27 0\n", + "3902 1\n", + "\n", + "[1608 rows x 1 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from typing import Tuple\n", + "import pandas as pd\n", + "from pandas import DataFrame\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", + ") -> Tuple[DataFrame, DataFrame, DataFrame, DataFrame, DataFrame, DataFrame]:\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", + " if stratify_colname not in df_input.columns:\n", + " raise ValueError(\"%s is not a column in the dataframe\" % (stratify_colname))\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", + " # 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", + " if frac_val <= 0:\n", + " assert len(df_input) == len(df_train) + len(df_temp)\n", + " return df_train, pd.DataFrame(), df_temp, y_train, pd.DataFrame(), y_temp\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", + " assert len(df_input) == len(df_train) + len(df_val) + len(df_test)\n", + " return df_train, df_val, df_test, y_train, y_val, y_test\n", + "\n", + "X_train, X_val, X_test, y_train, y_val, y_test = split_stratified_into_train_val_test(\n", + " df, stratify_colname=\"above_average_volume\", frac_train=0.80, frac_val=0, frac_test=0.20, random_state=random_state\n", + ")\n", + "\n", + "display(\"X_train\", X_train)\n", + "display(\"y_train\", y_train)\n", + "\n", + "display(\"X_test\", X_test)\n", + "display(\"y_test\", y_test)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Формирование конвейера для классификации данных\n", + "\n", + "preprocessing_num -- конвейер для обработки числовых данных: заполнение пропущенных значений и стандартизация\n", + "\n", + "preprocessing_cat -- конвейер для обработки категориальных данных: заполнение пропущенных данных и унитарное кодирование\n", + "\n", + "features_preprocessing -- трансформер для предобработки признаков\n", + "\n", + "features_engineering -- трансформер для конструирования признаков\n", + "\n", + "drop_columns -- трансформер для удаления колонок\n", + "\n", + "pipeline_end -- основной конвейер предобработки данных и конструирования признаков" + ] + }, + { + "cell_type": "code", + "execution_count": 162, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from sklearn.base import BaseEstimator, TransformerMixin\n", + "from sklearn.compose import ColumnTransformer\n", + "from sklearn.discriminant_analysis import StandardScaler\n", + "from sklearn.impute import SimpleImputer\n", + "from sklearn.pipeline import Pipeline\n", + "from sklearn.preprocessing import OneHotEncoder\n", + "\n", + "class StarbucksFeatures(BaseEstimator, TransformerMixin):\n", + " def __init__(self):\n", + " pass\n", + " def fit(self, X, y=None):\n", + " return self\n", + " def transform(self, X, y=None):\n", + " X[\"Length_to_Width_Ratio\"] = X[\"x\"] / X[\"y\"]\n", + " return X\n", + " def get_feature_names_out(self, features_in):\n", + " return np.append(features_in, [\"Length_to_Width_Ratio\"], axis=0)\n", + " \n", + "\n", + "columns_to_drop = [\"Date\"]\n", + "num_columns = [\"Close\", \"Open\", \"Adj Close\", \"High\", \"Low\", \"Volume\", \"above_average_volume\"]\n", + "cat_columns = []\n", + "\n", + "num_imputer = SimpleImputer(strategy=\"median\")\n", + "num_scaler = StandardScaler()\n", + "preprocessing_num = Pipeline(\n", + " [\n", + " (\"imputer\", num_imputer),\n", + " (\"scaler\", num_scaler),\n", + " ]\n", + ")\n", + "\n", + "cat_imputer = SimpleImputer(strategy=\"constant\", fill_value=\"unknown\")\n", + "cat_encoder = OneHotEncoder(handle_unknown=\"ignore\", sparse_output=False, drop=\"first\")\n", + "preprocessing_cat = Pipeline(\n", + " [\n", + " (\"imputer\", cat_imputer),\n", + " (\"encoder\", cat_encoder),\n", + " ]\n", + ")\n", + "\n", + "features_preprocessing = ColumnTransformer(\n", + " verbose_feature_names_out=False,\n", + " transformers=[\n", + " (\"prepocessing_num\", preprocessing_num, num_columns),\n", + " (\"prepocessing_cat\", preprocessing_cat, cat_columns),\n", + " ],\n", + " remainder=\"passthrough\"\n", + ")\n", + "\n", + "\n", + "drop_columns = ColumnTransformer(\n", + " verbose_feature_names_out=False,\n", + " transformers=[\n", + " (\"drop_columns\", \"drop\", columns_to_drop),\n", + " ],\n", + " remainder=\"passthrough\",\n", + ")\n", + "\n", + "features_postprocessing = ColumnTransformer(\n", + " verbose_feature_names_out=False,\n", + " transformers=[\n", + " (\"prepocessing_cat\", preprocessing_cat, [\"Cabin_type\"]),\n", + " ],\n", + " remainder=\"passthrough\",\n", + ")\n", + "\n", + "pipeline_end = Pipeline(\n", + " [\n", + " (\"features_preprocessing\", features_preprocessing),\n", + " (\"drop_columns\", drop_columns),\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Демонстрация работы конвейера__" + ] + }, + { + "cell_type": "code", + "execution_count": 163, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
CloseOpenAdj CloseHighLowVolumeabove_average_volumevolatility
71592.0521222.0475532.0570552.0358002.068394-1.046507-0.7338500.700004
4505-0.493609-0.482248-0.509368-0.485819-0.4938410.7089381.3626770.575000
421-0.867869-0.867429-0.818396-0.868235-0.866632-0.450983-0.7338500.031250
1595-0.819432-0.817932-0.778575-0.818012-0.8190500.5580911.3626770.148437
3676-0.373633-0.364031-0.412080-0.367150-0.3684211.1530361.3626770.420000
...........................
59760.8908120.8975890.7610790.8969850.899914-0.027099-0.7338501.020001
1305-0.808836-0.812807-0.769864-0.809815-0.8111781.1393861.3626770.164062
60850.7924460.7860810.6833730.7814190.796750-0.571535-0.7338500.540001
54700.2168580.2266030.1297610.2185140.222586-0.303594-0.7338500.584999
57810.6818590.6557900.5606320.6726510.666218-0.556786-0.7338501.200001
\n", + "

6428 rows × 8 columns

\n", + "
" + ], + "text/plain": [ + " Close Open Adj Close High Low Volume \\\n", + "7159 2.052122 2.047553 2.057055 2.035800 2.068394 -1.046507 \n", + "4505 -0.493609 -0.482248 -0.509368 -0.485819 -0.493841 0.708938 \n", + "421 -0.867869 -0.867429 -0.818396 -0.868235 -0.866632 -0.450983 \n", + "1595 -0.819432 -0.817932 -0.778575 -0.818012 -0.819050 0.558091 \n", + "3676 -0.373633 -0.364031 -0.412080 -0.367150 -0.368421 1.153036 \n", + "... ... ... ... ... ... ... \n", + "5976 0.890812 0.897589 0.761079 0.896985 0.899914 -0.027099 \n", + "1305 -0.808836 -0.812807 -0.769864 -0.809815 -0.811178 1.139386 \n", + "6085 0.792446 0.786081 0.683373 0.781419 0.796750 -0.571535 \n", + "5470 0.216858 0.226603 0.129761 0.218514 0.222586 -0.303594 \n", + "5781 0.681859 0.655790 0.560632 0.672651 0.666218 -0.556786 \n", + "\n", + " above_average_volume volatility \n", + "7159 -0.733850 0.700004 \n", + "4505 1.362677 0.575000 \n", + "421 -0.733850 0.031250 \n", + "1595 1.362677 0.148437 \n", + "3676 1.362677 0.420000 \n", + "... ... ... \n", + "5976 -0.733850 1.020001 \n", + "1305 1.362677 0.164062 \n", + "6085 -0.733850 0.540001 \n", + "5470 -0.733850 0.584999 \n", + "5781 -0.733850 1.200001 \n", + "\n", + "[6428 rows x 8 columns]" + ] + }, + "execution_count": 163, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "preprocessing_result = pipeline_end.fit_transform(X_train)\n", + "preprocessed_df = pd.DataFrame(\n", + " preprocessing_result,\n", + " columns=pipeline_end.get_feature_names_out(),\n", + ")\n", + "\n", + "preprocessed_df" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Формирование набора моделей для классификации\n", + "\n", + "logistic -- логистическая регрессия\n", + "\n", + "ridge -- гребневая регрессия\n", + "\n", + "decision_tree -- дерево решений\n", + "\n", + "knn -- k-ближайших соседей\n", + "\n", + "naive_bayes -- наивный Байесовский классификатор\n", + "\n", + "gradient_boosting -- метод градиентного бустинга (набор деревьев решений)\n", + "\n", + "random_forest -- метод случайного леса (набор деревьев решений)\n", + "\n", + "mlp -- многослойный персептрон (нейронная сеть)" + ] + }, + { + "cell_type": "code", + "execution_count": 164, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn import ensemble, linear_model, naive_bayes, neighbors, neural_network, tree\n", + "\n", + "class_models = {\n", + " \"logistic\": {\"model\": linear_model.LogisticRegression()},\n", + " # \"ridge\": {\"model\": linear_model.RidgeClassifierCV(cv=5, class_weight=\"balanced\")},\n", + " \"ridge\": {\"model\": linear_model.LogisticRegression(penalty=\"l2\", class_weight=\"balanced\")},\n", + " \"decision_tree\": {\n", + " \"model\": tree.DecisionTreeClassifier(max_depth=7, random_state=random_state)\n", + " },\n", + " \"knn\": {\"model\": neighbors.KNeighborsClassifier(n_neighbors=7)},\n", + " \"naive_bayes\": {\"model\": naive_bayes.GaussianNB()},\n", + " \"gradient_boosting\": {\n", + " \"model\": ensemble.GradientBoostingClassifier(n_estimators=210)\n", + " },\n", + " \"random_forest\": {\n", + " \"model\": ensemble.RandomForestClassifier(\n", + " max_depth=11, class_weight=\"balanced\", random_state=random_state\n", + " )\n", + " },\n", + " \"mlp\": {\n", + " \"model\": neural_network.MLPClassifier(\n", + " hidden_layer_sizes=(7,),\n", + " max_iter=500,\n", + " early_stopping=True,\n", + " random_state=random_state,\n", + " )\n", + " },\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Обучение моделей на обучающем наборе данных и оценка на тестовом" + ] + }, + { + "cell_type": "code", + "execution_count": 165, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model: logistic\n", + "Model: ridge\n", + "Model: decision_tree\n", + "Model: knn\n", + "Model: naive_bayes\n", + "Model: gradient_boosting\n", + "Model: random_forest\n", + "Model: mlp\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "from sklearn import metrics\n", + "\n", + "for model_name in class_models.keys():\n", + " print(f\"Model: {model_name}\")\n", + " model = class_models[model_name][\"model\"]\n", + "\n", + " model_pipeline = Pipeline([(\"pipeline\", pipeline_end), (\"model\", model)])\n", + " model_pipeline = model_pipeline.fit(X_train, y_train.values.ravel())\n", + "\n", + " y_train_predict = model_pipeline.predict(X_train)\n", + " y_test_probs = model_pipeline.predict_proba(X_test)[:, 1]\n", + " y_test_predict = np.where(y_test_probs > 0.5, 1, 0)\n", + "\n", + " class_models[model_name][\"pipeline\"] = model_pipeline\n", + " class_models[model_name][\"probs\"] = y_test_probs\n", + " class_models[model_name][\"preds\"] = y_test_predict\n", + "\n", + " class_models[model_name][\"Precision_train\"] = metrics.precision_score(\n", + " y_train, y_train_predict\n", + " )\n", + " class_models[model_name][\"Precision_test\"] = metrics.precision_score(\n", + " y_test, y_test_predict\n", + " )\n", + " class_models[model_name][\"Recall_train\"] = metrics.recall_score(\n", + " y_train, y_train_predict\n", + " )\n", + " class_models[model_name][\"Recall_test\"] = metrics.recall_score(\n", + " y_test, y_test_predict\n", + " )\n", + " class_models[model_name][\"Accuracy_train\"] = metrics.accuracy_score(\n", + " y_train, y_train_predict\n", + " )\n", + " class_models[model_name][\"Accuracy_test\"] = metrics.accuracy_score(\n", + " y_test, y_test_predict\n", + " )\n", + " class_models[model_name][\"ROC_AUC_test\"] = metrics.roc_auc_score(\n", + " y_test, y_test_probs\n", + " )\n", + " class_models[model_name][\"F1_train\"] = metrics.f1_score(y_train, y_train_predict)\n", + " class_models[model_name][\"F1_test\"] = metrics.f1_score(y_test, y_test_predict)\n", + " class_models[model_name][\"MCC_test\"] = metrics.matthews_corrcoef(\n", + " y_test, y_test_predict\n", + " )\n", + " class_models[model_name][\"Cohen_kappa_test\"] = metrics.cohen_kappa_score(\n", + " y_test, y_test_predict\n", + " )\n", + " class_models[model_name][\"Confusion_matrix\"] = metrics.confusion_matrix(\n", + " y_test, y_test_predict\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Сводная таблица оценок качества для использованных моделей классификации" + ] + }, + { + "cell_type": "code", + "execution_count": 159, + "metadata": {}, + "outputs": [ + { + "ename": "KeyboardInterrupt", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[159], line 13\u001b[0m\n\u001b[0;32m 10\u001b[0m disp\u001b[38;5;241m.\u001b[39max_\u001b[38;5;241m.\u001b[39mset_title(key)\n\u001b[0;32m 12\u001b[0m plt\u001b[38;5;241m.\u001b[39msubplots_adjust(top\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m1\u001b[39m, bottom\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m0\u001b[39m, hspace\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m0.4\u001b[39m, wspace\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m0.1\u001b[39m)\n\u001b[1;32m---> 13\u001b[0m \u001b[43mplt\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mshow\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\matplotlib\\pyplot.py:612\u001b[0m, in \u001b[0;36mshow\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m 568\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m 569\u001b[0m \u001b[38;5;124;03mDisplay all open figures.\u001b[39;00m\n\u001b[0;32m 570\u001b[0m \n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 609\u001b[0m \u001b[38;5;124;03mexplicitly there.\u001b[39;00m\n\u001b[0;32m 610\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m 611\u001b[0m _warn_if_gui_out_of_main_thread()\n\u001b[1;32m--> 612\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43m_get_backend_mod\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mshow\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\matplotlib_inline\\backend_inline.py:90\u001b[0m, in \u001b[0;36mshow\u001b[1;34m(close, block)\u001b[0m\n\u001b[0;32m 88\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m 89\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m figure_manager \u001b[38;5;129;01min\u001b[39;00m Gcf\u001b[38;5;241m.\u001b[39mget_all_fig_managers():\n\u001b[1;32m---> 90\u001b[0m \u001b[43mdisplay\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 91\u001b[0m \u001b[43m \u001b[49m\u001b[43mfigure_manager\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcanvas\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfigure\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 92\u001b[0m \u001b[43m \u001b[49m\u001b[43mmetadata\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m_fetch_figure_metadata\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfigure_manager\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcanvas\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfigure\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 93\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 94\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[0;32m 95\u001b[0m show\u001b[38;5;241m.\u001b[39m_to_draw \u001b[38;5;241m=\u001b[39m []\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\IPython\\core\\display_functions.py:298\u001b[0m, in \u001b[0;36mdisplay\u001b[1;34m(include, exclude, metadata, transient, display_id, raw, clear, *objs, **kwargs)\u001b[0m\n\u001b[0;32m 296\u001b[0m publish_display_data(data\u001b[38;5;241m=\u001b[39mobj, metadata\u001b[38;5;241m=\u001b[39mmetadata, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m 297\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m--> 298\u001b[0m format_dict, md_dict \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mformat\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minclude\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43minclude\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mexclude\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mexclude\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 299\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m format_dict:\n\u001b[0;32m 300\u001b[0m \u001b[38;5;66;03m# nothing to display (e.g. _ipython_display_ took over)\u001b[39;00m\n\u001b[0;32m 301\u001b[0m \u001b[38;5;28;01mcontinue\u001b[39;00m\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\IPython\\core\\formatters.py:182\u001b[0m, in \u001b[0;36mDisplayFormatter.format\u001b[1;34m(self, obj, include, exclude)\u001b[0m\n\u001b[0;32m 180\u001b[0m md \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[0;32m 181\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m--> 182\u001b[0m data \u001b[38;5;241m=\u001b[39m \u001b[43mformatter\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 183\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m:\n\u001b[0;32m 184\u001b[0m \u001b[38;5;66;03m# FIXME: log the exception\u001b[39;00m\n\u001b[0;32m 185\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\decorator.py:232\u001b[0m, in \u001b[0;36mdecorate..fun\u001b[1;34m(*args, **kw)\u001b[0m\n\u001b[0;32m 230\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m kwsyntax:\n\u001b[0;32m 231\u001b[0m args, kw \u001b[38;5;241m=\u001b[39m fix(args, kw, sig)\n\u001b[1;32m--> 232\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mcaller\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mextras\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkw\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\IPython\\core\\formatters.py:226\u001b[0m, in \u001b[0;36mcatch_format_error\u001b[1;34m(method, self, *args, **kwargs)\u001b[0m\n\u001b[0;32m 224\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"show traceback on failed format call\"\"\"\u001b[39;00m\n\u001b[0;32m 225\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m--> 226\u001b[0m r \u001b[38;5;241m=\u001b[39m \u001b[43mmethod\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 227\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mNotImplementedError\u001b[39;00m:\n\u001b[0;32m 228\u001b[0m \u001b[38;5;66;03m# don't warn on NotImplementedErrors\u001b[39;00m\n\u001b[0;32m 229\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_check_return(\u001b[38;5;28;01mNone\u001b[39;00m, args[\u001b[38;5;241m0\u001b[39m])\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\IPython\\core\\formatters.py:343\u001b[0m, in \u001b[0;36mBaseFormatter.__call__\u001b[1;34m(self, obj)\u001b[0m\n\u001b[0;32m 341\u001b[0m \u001b[38;5;28;01mpass\u001b[39;00m\n\u001b[0;32m 342\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m--> 343\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mprinter\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 344\u001b[0m \u001b[38;5;66;03m# Finally look for special method names\u001b[39;00m\n\u001b[0;32m 345\u001b[0m method \u001b[38;5;241m=\u001b[39m get_real_method(obj, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mprint_method)\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\IPython\\core\\pylabtools.py:170\u001b[0m, in \u001b[0;36mprint_figure\u001b[1;34m(fig, fmt, bbox_inches, base64, **kwargs)\u001b[0m\n\u001b[0;32m 167\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mmatplotlib\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mbackend_bases\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m FigureCanvasBase\n\u001b[0;32m 168\u001b[0m FigureCanvasBase(fig)\n\u001b[1;32m--> 170\u001b[0m \u001b[43mfig\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcanvas\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mprint_figure\u001b[49m\u001b[43m(\u001b[49m\u001b[43mbytes_io\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkw\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 171\u001b[0m data \u001b[38;5;241m=\u001b[39m bytes_io\u001b[38;5;241m.\u001b[39mgetvalue()\n\u001b[0;32m 172\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m fmt \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124msvg\u001b[39m\u001b[38;5;124m'\u001b[39m:\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\matplotlib\\backend_bases.py:2175\u001b[0m, in \u001b[0;36mFigureCanvasBase.print_figure\u001b[1;34m(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)\u001b[0m\n\u001b[0;32m 2172\u001b[0m \u001b[38;5;66;03m# we do this instead of `self.figure.draw_without_rendering`\u001b[39;00m\n\u001b[0;32m 2173\u001b[0m \u001b[38;5;66;03m# so that we can inject the orientation\u001b[39;00m\n\u001b[0;32m 2174\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mgetattr\u001b[39m(renderer, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m_draw_disabled\u001b[39m\u001b[38;5;124m\"\u001b[39m, nullcontext)():\n\u001b[1;32m-> 2175\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfigure\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdraw\u001b[49m\u001b[43m(\u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 2176\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m bbox_inches:\n\u001b[0;32m 2177\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m bbox_inches \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mtight\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\matplotlib\\artist.py:95\u001b[0m, in \u001b[0;36m_finalize_rasterization..draw_wrapper\u001b[1;34m(artist, renderer, *args, **kwargs)\u001b[0m\n\u001b[0;32m 93\u001b[0m \u001b[38;5;129m@wraps\u001b[39m(draw)\n\u001b[0;32m 94\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mdraw_wrapper\u001b[39m(artist, renderer, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[1;32m---> 95\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43mdraw\u001b[49m\u001b[43m(\u001b[49m\u001b[43martist\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 96\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m renderer\u001b[38;5;241m.\u001b[39m_rasterizing:\n\u001b[0;32m 97\u001b[0m renderer\u001b[38;5;241m.\u001b[39mstop_rasterizing()\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\matplotlib\\artist.py:72\u001b[0m, in \u001b[0;36mallow_rasterization..draw_wrapper\u001b[1;34m(artist, renderer)\u001b[0m\n\u001b[0;32m 69\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m artist\u001b[38;5;241m.\u001b[39mget_agg_filter() \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m 70\u001b[0m renderer\u001b[38;5;241m.\u001b[39mstart_filter()\n\u001b[1;32m---> 72\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mdraw\u001b[49m\u001b[43m(\u001b[49m\u001b[43martist\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 73\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[0;32m 74\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m artist\u001b[38;5;241m.\u001b[39mget_agg_filter() \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\matplotlib\\figure.py:3162\u001b[0m, in \u001b[0;36mFigure.draw\u001b[1;34m(self, renderer)\u001b[0m\n\u001b[0;32m 3159\u001b[0m \u001b[38;5;66;03m# ValueError can occur when resizing a window.\u001b[39;00m\n\u001b[0;32m 3161\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mpatch\u001b[38;5;241m.\u001b[39mdraw(renderer)\n\u001b[1;32m-> 3162\u001b[0m \u001b[43mmimage\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_draw_list_compositing_images\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 3163\u001b[0m \u001b[43m \u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43martists\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msuppressComposite\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 3165\u001b[0m renderer\u001b[38;5;241m.\u001b[39mclose_group(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mfigure\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m 3166\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\matplotlib\\image.py:132\u001b[0m, in \u001b[0;36m_draw_list_compositing_images\u001b[1;34m(renderer, parent, artists, suppress_composite)\u001b[0m\n\u001b[0;32m 130\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m not_composite \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m has_images:\n\u001b[0;32m 131\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m a \u001b[38;5;129;01min\u001b[39;00m artists:\n\u001b[1;32m--> 132\u001b[0m \u001b[43ma\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdraw\u001b[49m\u001b[43m(\u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 133\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m 134\u001b[0m \u001b[38;5;66;03m# Composite any adjacent images together\u001b[39;00m\n\u001b[0;32m 135\u001b[0m image_group \u001b[38;5;241m=\u001b[39m []\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\matplotlib\\artist.py:72\u001b[0m, in \u001b[0;36mallow_rasterization..draw_wrapper\u001b[1;34m(artist, renderer)\u001b[0m\n\u001b[0;32m 69\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m artist\u001b[38;5;241m.\u001b[39mget_agg_filter() \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m 70\u001b[0m renderer\u001b[38;5;241m.\u001b[39mstart_filter()\n\u001b[1;32m---> 72\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mdraw\u001b[49m\u001b[43m(\u001b[49m\u001b[43martist\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 73\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[0;32m 74\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m artist\u001b[38;5;241m.\u001b[39mget_agg_filter() \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\matplotlib\\axes\\_base.py:3137\u001b[0m, in \u001b[0;36m_AxesBase.draw\u001b[1;34m(self, renderer)\u001b[0m\n\u001b[0;32m 3134\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m artists_rasterized:\n\u001b[0;32m 3135\u001b[0m _draw_rasterized(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfigure, artists_rasterized, renderer)\n\u001b[1;32m-> 3137\u001b[0m \u001b[43mmimage\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_draw_list_compositing_images\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 3138\u001b[0m \u001b[43m \u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43martists\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfigure\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msuppressComposite\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 3140\u001b[0m renderer\u001b[38;5;241m.\u001b[39mclose_group(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124maxes\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m 3141\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mstale \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mFalse\u001b[39;00m\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\matplotlib\\image.py:132\u001b[0m, in \u001b[0;36m_draw_list_compositing_images\u001b[1;34m(renderer, parent, artists, suppress_composite)\u001b[0m\n\u001b[0;32m 130\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m not_composite \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m has_images:\n\u001b[0;32m 131\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m a \u001b[38;5;129;01min\u001b[39;00m artists:\n\u001b[1;32m--> 132\u001b[0m \u001b[43ma\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdraw\u001b[49m\u001b[43m(\u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 133\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m 134\u001b[0m \u001b[38;5;66;03m# Composite any adjacent images together\u001b[39;00m\n\u001b[0;32m 135\u001b[0m image_group \u001b[38;5;241m=\u001b[39m []\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\matplotlib\\artist.py:72\u001b[0m, in \u001b[0;36mallow_rasterization..draw_wrapper\u001b[1;34m(artist, renderer)\u001b[0m\n\u001b[0;32m 69\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m artist\u001b[38;5;241m.\u001b[39mget_agg_filter() \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m 70\u001b[0m renderer\u001b[38;5;241m.\u001b[39mstart_filter()\n\u001b[1;32m---> 72\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mdraw\u001b[49m\u001b[43m(\u001b[49m\u001b[43martist\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 73\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[0;32m 74\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m artist\u001b[38;5;241m.\u001b[39mget_agg_filter() \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\matplotlib\\axis.py:1423\u001b[0m, in \u001b[0;36mAxis.draw\u001b[1;34m(self, renderer)\u001b[0m\n\u001b[0;32m 1420\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m\n\u001b[0;32m 1421\u001b[0m renderer\u001b[38;5;241m.\u001b[39mopen_group(\u001b[38;5;18m__name__\u001b[39m, gid\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mget_gid())\n\u001b[1;32m-> 1423\u001b[0m ticks_to_draw \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_update_ticks\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 1424\u001b[0m tlb1, tlb2 \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_get_ticklabel_bboxes(ticks_to_draw, renderer)\n\u001b[0;32m 1426\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m tick \u001b[38;5;129;01min\u001b[39;00m ticks_to_draw:\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\matplotlib\\axis.py:1302\u001b[0m, in \u001b[0;36mAxis._update_ticks\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 1300\u001b[0m major_locs \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mget_majorticklocs()\n\u001b[0;32m 1301\u001b[0m major_labels \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmajor\u001b[38;5;241m.\u001b[39mformatter\u001b[38;5;241m.\u001b[39mformat_ticks(major_locs)\n\u001b[1;32m-> 1302\u001b[0m major_ticks \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget_major_ticks\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mlen\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mmajor_locs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 1303\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m tick, loc, label \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mzip\u001b[39m(major_ticks, major_locs, major_labels):\n\u001b[0;32m 1304\u001b[0m tick\u001b[38;5;241m.\u001b[39mupdate_position(loc)\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\matplotlib\\axis.py:1670\u001b[0m, in \u001b[0;36mAxis.get_major_ticks\u001b[1;34m(self, numticks)\u001b[0m\n\u001b[0;32m 1666\u001b[0m numticks \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlen\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mget_majorticklocs())\n\u001b[0;32m 1668\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmajorTicks) \u001b[38;5;241m<\u001b[39m numticks:\n\u001b[0;32m 1669\u001b[0m \u001b[38;5;66;03m# Update the new tick label properties from the old.\u001b[39;00m\n\u001b[1;32m-> 1670\u001b[0m tick \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_get_tick\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmajor\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m)\u001b[49m\n\u001b[0;32m 1671\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmajorTicks\u001b[38;5;241m.\u001b[39mappend(tick)\n\u001b[0;32m 1672\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_copy_tick_props(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmajorTicks[\u001b[38;5;241m0\u001b[39m], tick)\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\matplotlib\\axis.py:1598\u001b[0m, in \u001b[0;36mAxis._get_tick\u001b[1;34m(self, major)\u001b[0m\n\u001b[0;32m 1594\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mNotImplementedError\u001b[39;00m(\n\u001b[0;32m 1595\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mThe Axis subclass \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__class__\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__name__\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m must define \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 1596\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m_tick_class or reimplement _get_tick()\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 1597\u001b[0m tick_kw \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_major_tick_kw \u001b[38;5;28;01mif\u001b[39;00m major \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_minor_tick_kw\n\u001b[1;32m-> 1598\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_tick_class\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43maxes\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmajor\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmajor\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mtick_kw\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\matplotlib\\axis.py:456\u001b[0m, in \u001b[0;36mYTick.__init__\u001b[1;34m(self, *args, **kwargs)\u001b[0m\n\u001b[0;32m 455\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[1;32m--> 456\u001b[0m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__init__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 457\u001b[0m \u001b[38;5;66;03m# x in axes coords, y in data coords\u001b[39;00m\n\u001b[0;32m 458\u001b[0m ax \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39maxes\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\matplotlib\\axis.py:170\u001b[0m, in \u001b[0;36mTick.__init__\u001b[1;34m(self, axes, loc, size, width, color, tickdir, pad, labelsize, labelcolor, labelfontfamily, zorder, gridOn, tick1On, tick2On, label1On, label2On, major, labelrotation, grid_color, grid_linestyle, grid_linewidth, grid_alpha, **kwargs)\u001b[0m\n\u001b[0;32m 159\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mtick2line \u001b[38;5;241m=\u001b[39m mlines\u001b[38;5;241m.\u001b[39mLine2D(\n\u001b[0;32m 160\u001b[0m [], [],\n\u001b[0;32m 161\u001b[0m color\u001b[38;5;241m=\u001b[39mcolor, linestyle\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mnone\u001b[39m\u001b[38;5;124m\"\u001b[39m, zorder\u001b[38;5;241m=\u001b[39mzorder, visible\u001b[38;5;241m=\u001b[39mtick2On,\n\u001b[0;32m 162\u001b[0m markeredgecolor\u001b[38;5;241m=\u001b[39mcolor, markersize\u001b[38;5;241m=\u001b[39msize, markeredgewidth\u001b[38;5;241m=\u001b[39mwidth,\n\u001b[0;32m 163\u001b[0m )\n\u001b[0;32m 164\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mgridline \u001b[38;5;241m=\u001b[39m mlines\u001b[38;5;241m.\u001b[39mLine2D(\n\u001b[0;32m 165\u001b[0m [], [],\n\u001b[0;32m 166\u001b[0m color\u001b[38;5;241m=\u001b[39mgrid_color, alpha\u001b[38;5;241m=\u001b[39mgrid_alpha, visible\u001b[38;5;241m=\u001b[39mgridOn,\n\u001b[0;32m 167\u001b[0m linestyle\u001b[38;5;241m=\u001b[39mgrid_linestyle, linewidth\u001b[38;5;241m=\u001b[39mgrid_linewidth, marker\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[0;32m 168\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mgrid_kw,\n\u001b[0;32m 169\u001b[0m )\n\u001b[1;32m--> 170\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mgridline\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget_path\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241m.\u001b[39m_interpolation_steps \u001b[38;5;241m=\u001b[39m \\\n\u001b[0;32m 171\u001b[0m GRIDLINE_INTERPOLATION_STEPS\n\u001b[0;32m 172\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mlabel1 \u001b[38;5;241m=\u001b[39m mtext\u001b[38;5;241m.\u001b[39mText(\n\u001b[0;32m 173\u001b[0m np\u001b[38;5;241m.\u001b[39mnan, np\u001b[38;5;241m.\u001b[39mnan,\n\u001b[0;32m 174\u001b[0m fontsize\u001b[38;5;241m=\u001b[39mlabelsize, color\u001b[38;5;241m=\u001b[39mlabelcolor, visible\u001b[38;5;241m=\u001b[39mlabel1On,\n\u001b[0;32m 175\u001b[0m fontfamily\u001b[38;5;241m=\u001b[39mlabelfontfamily, rotation\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_labelrotation[\u001b[38;5;241m1\u001b[39m])\n\u001b[0;32m 176\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mlabel2 \u001b[38;5;241m=\u001b[39m mtext\u001b[38;5;241m.\u001b[39mText(\n\u001b[0;32m 177\u001b[0m np\u001b[38;5;241m.\u001b[39mnan, np\u001b[38;5;241m.\u001b[39mnan,\n\u001b[0;32m 178\u001b[0m fontsize\u001b[38;5;241m=\u001b[39mlabelsize, color\u001b[38;5;241m=\u001b[39mlabelcolor, visible\u001b[38;5;241m=\u001b[39mlabel2On,\n\u001b[0;32m 179\u001b[0m fontfamily\u001b[38;5;241m=\u001b[39mlabelfontfamily, rotation\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_labelrotation[\u001b[38;5;241m1\u001b[39m])\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\matplotlib\\lines.py:1037\u001b[0m, in \u001b[0;36mLine2D.get_path\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 1035\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Return the `~matplotlib.path.Path` associated with this line.\"\"\"\u001b[39;00m\n\u001b[0;32m 1036\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_invalidy \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_invalidx:\n\u001b[1;32m-> 1037\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrecache\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 1038\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_path\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\matplotlib\\lines.py:683\u001b[0m, in \u001b[0;36mLine2D.recache\u001b[1;34m(self, always)\u001b[0m\n\u001b[0;32m 680\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m 681\u001b[0m y \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_y\n\u001b[1;32m--> 683\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_xy \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mcolumn_stack(\u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbroadcast_arrays\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43my\u001b[49m\u001b[43m)\u001b[49m)\u001b[38;5;241m.\u001b[39mastype(\u001b[38;5;28mfloat\u001b[39m)\n\u001b[0;32m 684\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_x, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_y \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_xy\u001b[38;5;241m.\u001b[39mT \u001b[38;5;66;03m# views\u001b[39;00m\n\u001b[0;32m 686\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_subslice \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mFalse\u001b[39;00m\n", + "File \u001b[1;32mc:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\numpy\\lib\\_stride_tricks_impl.py:560\u001b[0m, in \u001b[0;36mbroadcast_arrays\u001b[1;34m(subok, *args)\u001b[0m\n\u001b[0;32m 556\u001b[0m args \u001b[38;5;241m=\u001b[39m [np\u001b[38;5;241m.\u001b[39marray(_m, copy\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, subok\u001b[38;5;241m=\u001b[39msubok) \u001b[38;5;28;01mfor\u001b[39;00m _m \u001b[38;5;129;01min\u001b[39;00m args]\n\u001b[0;32m 558\u001b[0m shape \u001b[38;5;241m=\u001b[39m _broadcast_shape(\u001b[38;5;241m*\u001b[39margs)\n\u001b[1;32m--> 560\u001b[0m result \u001b[38;5;241m=\u001b[39m [array \u001b[38;5;28;01mif\u001b[39;00m array\u001b[38;5;241m.\u001b[39mshape \u001b[38;5;241m==\u001b[39m shape\n\u001b[0;32m 561\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m _broadcast_to(array, shape, subok\u001b[38;5;241m=\u001b[39msubok, readonly\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mFalse\u001b[39;00m)\n\u001b[0;32m 562\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m array \u001b[38;5;129;01min\u001b[39;00m args]\n\u001b[0;32m 563\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mtuple\u001b[39m(result)\n", + "\u001b[1;31mKeyboardInterrupt\u001b[0m: " + ] + } + ], + "source": [ + "from sklearn.metrics import ConfusionMatrixDisplay\n", + "import matplotlib.pyplot as plt\n", + "\n", + "_, ax = plt.subplots(int(len(class_models) / 2), 2, figsize=(12, 10), sharex=False, sharey=False)\n", + "for index, key in enumerate(class_models.keys()):\n", + " c_matrix = class_models[key][\"Confusion_matrix\"]\n", + " disp = ConfusionMatrixDisplay(\n", + " confusion_matrix=c_matrix, display_labels=[\"Less\", \"More\"]\n", + " ).plot(ax=ax.flat[index])\n", + " disp.ax_.set_title(key)\n", + "\n", + "plt.subplots_adjust(top=1, bottom=0, hspace=0.4, wspace=0.1)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1045: Это количество истинных положительных диагнозов (True Positives), где модель правильно определила объекты как \"More\".\n", + "\n", + "563: Это количество ложных отрицательных диагнозов (False Negatives), где модель неправильно определила объекты, которые на самом деле принадлежат к классу \"More\", отнесёнными к классу \"Less\".\n", + "\n", + "Исходя из значений True Positives и False Negatives, можно сказать, что модель имеет высокую точность при предсказании класса \"More\". Однако, высокий уровень ложных отрицательных результатов (563) указывает на то, что существует значительное количество примеров, которые модель пропускает. Это может означать, что в некоторых случаях она не распознаёт объекты, которые должны быть классифицированы как \"More\".\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Точность, полнота, верность (аккуратность), F-мера" + ] + }, + { + "cell_type": "code", + "execution_count": 166, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
 Precision_trainPrecision_testRecall_trainRecall_testAccuracy_trainAccuracy_testF1_trainF1_test
logistic1.0000001.0000001.0000001.0000001.0000001.0000001.0000001.000000
ridge1.0000001.0000001.0000001.0000001.0000001.0000001.0000001.000000
decision_tree1.0000001.0000001.0000001.0000001.0000001.0000001.0000001.000000
knn1.0000001.0000001.0000001.0000001.0000001.0000001.0000001.000000
naive_bayes1.0000001.0000001.0000001.0000001.0000001.0000001.0000001.000000
gradient_boosting1.0000001.0000001.0000001.0000001.0000001.0000001.0000001.000000
random_forest1.0000001.0000001.0000001.0000001.0000001.0000001.0000001.000000
mlp1.0000001.0000000.9942220.9946710.9979780.9981340.9971030.997329
\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 166, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "class_metrics = pd.DataFrame.from_dict(class_models, \"index\")[\n", + " [\n", + " \"Precision_train\",\n", + " \"Precision_test\",\n", + " \"Recall_train\",\n", + " \"Recall_test\",\n", + " \"Accuracy_train\",\n", + " \"Accuracy_test\",\n", + " \"F1_train\",\n", + " \"F1_test\",\n", + " ]\n", + "]\n", + "class_metrics.sort_values(\n", + " by=\"Accuracy_test\", ascending=False\n", + ").style.background_gradient(\n", + " cmap=\"plasma\",\n", + " low=0.3,\n", + " high=1,\n", + " subset=[\"Accuracy_train\", \"Accuracy_test\", \"F1_train\", \"F1_test\"],\n", + ").background_gradient(\n", + " cmap=\"viridis\",\n", + " low=1,\n", + " high=0.3,\n", + " subset=[\n", + " \"Precision_train\",\n", + " \"Precision_test\",\n", + " \"Recall_train\",\n", + " \"Recall_test\",\n", + " ],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Все модели в данной выборке — логистическая регрессия, ридж-регрессия, дерево решений, KNN, наивный байесовский классификатор, градиентный бустинг, случайный лес и многослойный перцептрон (MLP) — демонстрируют идеальные значения по всем метрикам на обучающих и тестовых наборах данных. Это достигается, поскольку все модели показали значения, равные 1.0 для Precision, Recall, Accuracy и F1-меры, что указывает на то, что модель безошибочно классифицирует все примеры.\n", + "\n", + "Модель MLP, хотя и имеет немного более низкие значения Recall (0.994) и F1-на тестовом наборе (0.997) по сравнению с другими, по-прежнему остается высокоэффективной. Тем не менее, она не снижает показатели классификации до такого уровня, что может вызвать обеспокоенность, и остается на уровне, близком к идеальному." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "ROC-кривая, каппа Коэна, коэффициент корреляции Мэтьюса" + ] + }, + { + "cell_type": "code", + "execution_count": 167, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
 Accuracy_testF1_testROC_AUC_testCohen_kappa_testMCC_test
logistic1.0000001.0000001.0000001.0000001.000000
ridge1.0000001.0000001.0000001.0000001.000000
decision_tree1.0000001.0000001.0000001.0000001.000000
knn1.0000001.0000001.0000001.0000001.000000
naive_bayes1.0000001.0000001.0000001.0000001.000000
gradient_boosting1.0000001.0000001.0000001.0000001.000000
random_forest1.0000001.0000001.0000001.0000001.000000
mlp0.9981340.9973291.0000000.9958950.995904
\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 167, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "class_metrics = pd.DataFrame.from_dict(class_models, \"index\")[\n", + " [\n", + " \"Accuracy_test\",\n", + " \"F1_test\",\n", + " \"ROC_AUC_test\",\n", + " \"Cohen_kappa_test\",\n", + " \"MCC_test\",\n", + " ]\n", + "]\n", + "class_metrics.sort_values(by=\"ROC_AUC_test\", ascending=False).style.background_gradient(\n", + " cmap=\"plasma\",\n", + " low=0.3,\n", + " high=1,\n", + " subset=[\n", + " \"ROC_AUC_test\",\n", + " \"MCC_test\",\n", + " \"Cohen_kappa_test\",\n", + " ],\n", + ").background_gradient(\n", + " cmap=\"viridis\",\n", + " low=1,\n", + " high=0.3,\n", + " subset=[\n", + " \"Accuracy_test\",\n", + " \"F1_test\",\n", + " ],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Все модели, включая логистическую регрессию, ридж-регрессию, дерево решений, KNN, наивный байесовский классификатор, градиентный бустинг и случайный лес, продемонстрировали идеальные значения по всем метрикам: Accuracy, F1, ROC AUC, Cohen's Kappa и MCC, достигнув максимальных значений, равных 1. Это подчеркивает их эффективность в контексте анализа и классификации данных.\n", + "\n", + "Модель MLP, хотя и показала очень высокие результаты, несколько уступает конкурентам по показателям Accuracy (0.998) и F1 (0.997). Несмотря на это, она достигает оптимального значения ROC AUC (1.000), что указывает на ее способность к выделению классов. Показатели Cohen's Kappa (0.996) и MCC (0.996) также находятся на высоком уровне, что говорит о хорошей согласованности и строгости классификации." + ] + }, + { + "cell_type": "code", + "execution_count": 168, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'logistic'" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "best_model = str(class_metrics.sort_values(by=\"MCC_test\", ascending=False).iloc[0].name)\n", + "\n", + "display(best_model)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Вывод данных с ошибкой предсказания для оценки" + ] + }, + { + "cell_type": "code", + "execution_count": 169, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Error items count: 0'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
DatePredictedOpenHighLowCloseAdj CloseVolumeabove_average_volumevolatility
\n", + "
" + ], + "text/plain": [ + "Empty DataFrame\n", + "Columns: [Date, Predicted, Open, High, Low, Close, Adj Close, Volume, above_average_volume, volatility]\n", + "Index: []" + ] + }, + "execution_count": 169, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "preprocessing_result = pipeline_end.transform(X_test)\n", + "preprocessed_df = pd.DataFrame(\n", + " preprocessing_result,\n", + " columns=pipeline_end.get_feature_names_out(),\n", + ")\n", + "\n", + "y_pred = class_models[best_model][\"preds\"]\n", + "\n", + "error_index = y_test[y_test[\"above_average_volume\"] != y_pred].index.tolist()\n", + "display(f\"Error items count: {len(error_index)}\")\n", + "\n", + "error_predicted = pd.Series(y_pred, index=y_test.index).loc[error_index]\n", + "error_df = X_test.loc[error_index].copy()\n", + "error_df.insert(loc=1, column=\"Predicted\", value=error_predicted)\n", + "error_df.sort_index()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Пример использования обученной модели (конвейера) для предсказания" + ] + }, + { + "cell_type": "code", + "execution_count": 170, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
DateOpenHighLowCloseAdj CloseVolumeabove_average_volumevolatility
66212018-10-0956.83000259.70000156.81000157.70999951.2570652485570012.89
\n", + "
" + ], + "text/plain": [ + " Date Open High Low Close Adj Close \\\n", + "6621 2018-10-09 56.830002 59.700001 56.810001 57.709999 51.257065 \n", + "\n", + " Volume above_average_volume volatility \n", + "6621 24855700 1 2.89 " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
CloseOpenAdj CloseHighLowVolumeabove_average_volumevolatility
66210.8314940.8057590.7830160.8748180.8211130.8578471.3626772.89
\n", + "
" + ], + "text/plain": [ + " Close Open Adj Close High Low Volume \\\n", + "6621 0.831494 0.805759 0.783016 0.874818 0.821113 0.857847 \n", + "\n", + " above_average_volume volatility \n", + "6621 1.362677 2.89 " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "'predicted: 1 (proba: [9.31850788e-04 9.99068149e-01])'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "'real: 1'" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "model = class_models[best_model][\"pipeline\"]\n", + "\n", + "example_id = 6621\n", + "test = pd.DataFrame(X_test.loc[example_id, :]).T\n", + "test_preprocessed = pd.DataFrame(preprocessed_df.loc[example_id, :]).T\n", + "display(test)\n", + "display(test_preprocessed)\n", + "result_proba = model.predict_proba(test)[0]\n", + "result = model.predict(test)[0]\n", + "real = int(y_test.loc[example_id].values[0])\n", + "display(f\"predicted: {result} (proba: {result_proba})\")\n", + "display(f\"real: {real}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Подбор гиперпараметров методом поиска по сетке" + ] + }, + { + "cell_type": "code", + "execution_count": 171, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "c:\\Users\\a3012\\AIM-PIbd-31-Zhirnova-A-E\\aimenv\\Lib\\site-packages\\numpy\\ma\\core.py:2881: RuntimeWarning: invalid value encountered in cast\n", + " _data = np.array(data, dtype=dtype, copy=copy,\n" + ] + }, + { + "data": { + "text/plain": [ + "{'model__criterion': 'gini',\n", + " 'model__max_depth': 5,\n", + " 'model__max_features': 'sqrt',\n", + " 'model__n_estimators': 10}" + ] + }, + "execution_count": 171, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from sklearn.model_selection import GridSearchCV\n", + "\n", + "optimized_model_type = \"random_forest\"\n", + "\n", + "random_forest_model = class_models[optimized_model_type][\"pipeline\"]\n", + "\n", + "param_grid = {\n", + " \"model__n_estimators\": [10, 50, 100],\n", + " \"model__max_features\": [\"sqrt\", \"log2\"],\n", + " \"model__max_depth\": [5, 7, 10],\n", + " \"model__criterion\": [\"gini\", \"entropy\"],\n", + "}\n", + "\n", + "gs_optomizer = GridSearchCV(\n", + " estimator=random_forest_model, param_grid=param_grid, n_jobs=-1\n", + ")\n", + "gs_optomizer.fit(X_train, y_train.values.ravel())\n", + "gs_optomizer.best_params_" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Обучение модели с новыми гиперпараметрами__" + ] + }, + { + "cell_type": "code", + "execution_count": 172, + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'numeric_features' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[172], line 10\u001b[0m\n\u001b[0;32m 6\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01msklearn\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m metrics\n\u001b[0;32m 8\u001b[0m \u001b[38;5;66;03m# Определение трансформера (пример)\u001b[39;00m\n\u001b[0;32m 9\u001b[0m pipeline_end \u001b[38;5;241m=\u001b[39m ColumnTransformer([\n\u001b[1;32m---> 10\u001b[0m (\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnumeric\u001b[39m\u001b[38;5;124m'\u001b[39m, StandardScaler(), \u001b[43mnumeric_features\u001b[49m), \u001b[38;5;66;03m# numeric_features - это список числовых признаков\u001b[39;00m\n\u001b[0;32m 11\u001b[0m \u001b[38;5;66;03m# Добавьте другие трансформеры, если требуется\u001b[39;00m\n\u001b[0;32m 12\u001b[0m ])\n\u001b[0;32m 14\u001b[0m \u001b[38;5;66;03m# Объявление модели\u001b[39;00m\n\u001b[0;32m 15\u001b[0m optimized_model \u001b[38;5;241m=\u001b[39m RandomForestClassifier(\n\u001b[0;32m 16\u001b[0m random_state\u001b[38;5;241m=\u001b[39mrandom_state,\n\u001b[0;32m 17\u001b[0m criterion\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mgini\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 20\u001b[0m n_estimators\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m10\u001b[39m,\n\u001b[0;32m 21\u001b[0m )\n", + "\u001b[1;31mNameError\u001b[0m: name 'numeric_features' is not defined" + ] + } + ], + "source": [ + "from sklearn.pipeline import Pipeline\n", + "from sklearn.preprocessing import StandardScaler\n", + "from sklearn.compose import ColumnTransformer\n", + "from sklearn.ensemble import RandomForestClassifier\n", + "import numpy as np\n", + "from sklearn import metrics\n", + "\n", + "# Определение трансформера (пример)\n", + "pipeline_end = ColumnTransformer([\n", + " ('numeric', StandardScaler(), numeric_features), # numeric_features - это список числовых признаков\n", + " # Добавьте другие трансформеры, если требуется\n", + "])\n", + "\n", + "# Объявление модели\n", + "optimized_model = RandomForestClassifier(\n", + " random_state=random_state,\n", + " criterion=\"gini\",\n", + " max_depth=5,\n", + " max_features=\"sqrt\",\n", + " n_estimators=10,\n", + ")\n", + "\n", + "# Создание пайплайна с корректными шагами\n", + "result = {}\n", + "\n", + "result[\"pipeline\"] = Pipeline([\n", + " (\"pipeline\", pipeline_end),\n", + " (\"model\", optimized_model)\n", + "]).fit(X_train, y_train.values.ravel())\n", + "\n", + "# Прогнозирование и расчет метрик\n", + "result[\"train_preds\"] = result[\"pipeline\"].predict(X_train)\n", + "result[\"probs\"] = result[\"pipeline\"].predict_proba(X_test)[:, 1]\n", + "result[\"preds\"] = np.where(result[\"probs\"] > 0.5, 1, 0)\n", + "\n", + "# Метрики для оценки модели\n", + "result[\"Precision_train\"] = metrics.precision_score(y_train, result[\"train_preds\"])\n", + "result[\"Precision_test\"] = metrics.precision_score(y_test, result[\"preds\"])\n", + "result[\"Recall_train\"] = metrics.recall_score(y_train, result[\"train_preds\"])\n", + "result[\"Recall_test\"] = metrics.recall_score(y_test, result[\"preds\"])\n", + "result[\"Accuracy_train\"] = metrics.accuracy_score(y_train, result[\"train_preds\"])\n", + "result[\"Accuracy_test\"] = metrics.accuracy_score(y_test, result[\"preds\"])\n", + "result[\"ROC_AUC_test\"] = metrics.roc_auc_score(y_test, result[\"probs\"])\n", + "result[\"F1_train\"] = metrics.f1_score(y_train, result[\"train_preds\"])\n", + "result[\"F1_test\"] = metrics.f1_score(y_test, result[\"preds\"])\n", + "result[\"MCC_test\"] = metrics.matthews_corrcoef(y_test, result[\"preds\"])\n", + "result[\"Cohen_kappa_test\"] = metrics.cohen_kappa_score(y_test, result[\"preds\"])\n", + "result[\"Confusion_matrix\"] = metrics.confusion_matrix(y_test, result[\"preds\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Формирование данных для оценки старой и новой версии модели" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "optimized_metrics = pd.DataFrame(columns=list(result.keys()))\n", + "optimized_metrics.loc[len(optimized_metrics)] = pd.Series(\n", + " data=class_models[optimized_model_type]\n", + ")\n", + "optimized_metrics.loc[len(optimized_metrics)] = pd.Series(\n", + " data=result\n", + ")\n", + "optimized_metrics.insert(loc=0, column=\"Name\", value=[\"Old\", \"New\"])\n", + "optimized_metrics = optimized_metrics.set_index(\"Name\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Оценка параметров старой и новой модели" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
 Precision_trainPrecision_testRecall_trainRecall_testAccuracy_trainAccuracy_testF1_trainF1_test
Name        
Old1.0000001.0000001.0000001.0000001.0000001.0000001.0000001.000000
New1.0000001.0000001.0000001.0000001.0000001.0000001.0000001.000000
\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 260, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "optimized_metrics[\n", + " [\n", + " \"Precision_train\",\n", + " \"Precision_test\",\n", + " \"Recall_train\",\n", + " \"Recall_test\",\n", + " \"Accuracy_train\",\n", + " \"Accuracy_test\",\n", + " \"F1_train\",\n", + " \"F1_test\",\n", + " ]\n", + "].style.background_gradient(\n", + " cmap=\"plasma\",\n", + " low=0.3,\n", + " high=1,\n", + " subset=[\"Accuracy_train\", \"Accuracy_test\", \"F1_train\", \"F1_test\"],\n", + ").background_gradient(\n", + " cmap=\"viridis\",\n", + " low=1,\n", + " high=0.3,\n", + " subset=[\n", + " \"Precision_train\",\n", + " \"Precision_test\",\n", + " \"Recall_train\",\n", + " \"Recall_test\",\n", + " ],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Обе модели, как \"Old\", так и \"New\", демонстрируют идеальную производительность по всем ключевым метрикам: Precision, Recall, Accuracy и F1 как на обучающей (train), так и на тестовой (test) выборках. Все значения равны 1.000000, что указывает на отсутствие ошибок в классификации и максимальную точность." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
 Accuracy_testF1_testROC_AUC_testCohen_kappa_testMCC_test
Name     
Old1.0000001.0000001.0000001.0000001.000000
New1.0000001.0000001.0000001.0000001.000000
\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 261, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "optimized_metrics[\n", + " [\n", + " \"Accuracy_test\",\n", + " \"F1_test\",\n", + " \"ROC_AUC_test\",\n", + " \"Cohen_kappa_test\",\n", + " \"MCC_test\",\n", + " ]\n", + "].style.background_gradient(\n", + " cmap=\"plasma\",\n", + " low=0.3,\n", + " high=1,\n", + " subset=[\n", + " \"ROC_AUC_test\",\n", + " \"MCC_test\",\n", + " \"Cohen_kappa_test\",\n", + " ],\n", + ").background_gradient(\n", + " cmap=\"viridis\",\n", + " low=1,\n", + " high=0.3,\n", + " subset=[\n", + " \"Accuracy_test\",\n", + " \"F1_test\",\n", + " ],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Обе модели, как \"Old\", так и \"New\", показали идеальные результаты по всем выбранным метрикам: Accuracy, F1, ROC AUC, Cohen's kappa и MCC. Все метрики имеют значение 1.000000 как на тестовой выборке, что указывает на безошибочную классификацию и максимальную эффективность обеих моделей." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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nWvp4ACgzrO4LSQ6OtptOVuYNW3aVGlpaW7rUlNTdeDAAUlSYmKiJKmgoMBtn4KCAte2xMREHT582G17VVWVCgsLXfvYAbkGALWIXDPF4yJt0aJFeu655zRgwAAFBwe71rdv3147duywdHAAAN/o2rWrdu7c6bZu165dSklJkXTyZuvExEStWrXKtb2kpEQbN25URkaGJCkjI0NFRUXasmWLa5/33ntPTqdTXbp0qYWzMIdcA4DA52+55nG74zfffKPWrVuftt7pdKqystKSQQEA/seL98GcdhwPjB49WldccYWmTJmifv366cMPP9Rzzz2n5557TtLJpx6OGjVKjz76qC666CLXo4qTkpJcT05MTU1Vz549NXz4cM2ePVuVlZXKyclR//79ffpkx58j1wCgFpFrpnhcpKWlpen99993VZ2nvPTSS0pPT7dsYAAASYbj5GLFcTzw61//Wq+++qruu+8+TZ48WS1atNC0adM0YMAA1z7jx4/X8ePHNWLECBUVFalbt25asWKF610ykrR48WLl5OTouuuuU1BQkG699VZNnz7d+/OxELkGALWIXDPF4yJt0qRJys7O1jfffCOn06lXXnlFO3fu1KJFi7R8+XLLBwgA8I0bb7xRN95441m3OxwOTZ48WZMnTz7rPnFxcT5/cfUvIdcAoG7wp1zz+J60W265Ra+//rreffddRUZGatKkSdq+fbtef/11XX/99TUxRgCoswzDugVnRq4BQO0h18zxeCZNkq688kqtXLnS6rEAAH7OR737dQ25BgC1hFwz5byKNEnavHmztm/fLulkP3+nTp0sGxQAALWNXAMA2IXHRdrXX3+t2267TevXr1dsbKwkqaioSFdccYWWLl2qCy+80OoxAkDd5aMbrOsScg0AahG5ZorH96T94Q9/UGVlpbZv367CwkIVFhZq+/btcjqd+sMf/lATYwSAOsthWLfgzMg1AKg95Jo5Hs+krVmzRhs2bFCbNm1c69q0aaMZM2boyiuvtHRwAADUNHINAGA3HhdpycnJZ3y5Z3V1ta1eTgoAAYEbrGscuQYAtYhcM8Xjdscnn3xSd911lzZv3uxat3nzZt1zzz3661//aungAKDOO9W7b8WCMyLXAKAWkWummJpJa9iwoRyOH38Qx48fV5cuXRQScvLbq6qqFBISoqFDh6pPnz41MlAAAKxC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s2bNUqtWrfTUU09Jktq0aaPPPvtMTzzxhEc/mwceeMD17+bNm2vs2LFaunSpW5iVlZVp0aJFuuCCCyRJM2bMUO/evfXUU08pMTFRDz74oJ566inXz6RFixb64osv9OyzzxJmAFADyLWzI9cAcyjS4DPLly9XgwYNVFlZKafTqd///vd66KGHXNvbtWvn1q//ySefaM+ePYqKinI7TllZmfbu3avi4mJ9++236tKli2tbSEiIOnfufFpryClbt25VcHCwunfvbnrce/bs0YkTJ3T99de7ra+oqFB6erokafv27W7jkOQKPk+88MILmj59uvbu3avS0lJVVVUpOjrabZ9mzZq5guzU5zidTu3cuVNRUVHau3evhg0bpuHDh7v2qaqqUkxMjMfjgQ8YsuiKo/eHAHBu5NovI9dArplDkQafueaaazRr1iyFhoYqKSlJISHuv46RkZFuX5eWlqpTp06udoefatKkyXmN4VSbhydKS0slSW+88YZbiEgn70ewSl5engYMGKCHH35YWVlZiomJ0dKlS11XMT0Z65w5c04L1+DgYMvGihpEWwjgN8i1cyPXIIlcM4kiDT4TGRmp1q1bm96/Y8eOeuGFFxQfH3/aVbdTmjZtqo0bN+qqq66SdPLK2pYtW9SxY8cz7t+uXTs5nU6tWbPG1RbyU6eueFZXV7vWpaWlKSwsTAcOHDjrlcrU1FTXzeKnfPDBB798kj+xYcMGpaSk6P7773et++qrr07b78CBAzp06JCSkpJcnxMUFKQ2bdooISFBSUlJ2rdvnwYMGODR5wMAPEOunRu5BpjH0x3hNwYMGKDGjRvrlltu0fvvv6/9+/dr9erVuvvuu/X1119Lku655x49/vjjWrZsmXbs2KE777zznO+Cad68ubKzszV06FAtW7bMdcwXX3xRkpSSkiKHw6Hly5fryJEjKi0tVVRUlMaOHavRo0dr4cKF2rt3rz766CPNmDFDCxculCT96U9/0u7duzVu3Djt3LlTS5Ys0YIFCzw634suukgHDhzQ0qVLtXfvXk2fPl2vvvrqafuFh4crOztbn3zyid5//33dfffd6tevnxITEyVJDz/8sHJzczV9+nTt2rVLn332mebPn6+nn37ao/HAR5xO6xYAtkKukWt1ErlmCkUa/Eb9+vW1du1aNWvWTH379lVqaqqGDRumsrIy1xXIP//5zxo0aJCys7OVkZGhqKgo/eY3vznncWfNmqXf/va3uvPOO9W2bVsNHz5cx48flyRdcMEFevjhhzVhwgQlJCQoJydHkvTII49o4sSJys3NVWpqqnr27Kk33nhDLVq0kHSyn/7ll1/WsmXL1L59e82ePVtTpkzx6HxvvvlmjR49Wjk5OerQoYM2bNigiRMnnrZf69at1bdvX91www3q0aOHLr30UrdHEf/hD3/Q3LlzNX/+fLVr107du3fXggULXGMFAPgGuUauAWfjMM525ykAwGdKSkoUExOjzCbDFBLk/Qtvq5wVevfI8youLj5rWxUAADWFXPMM96QBgJ1xgzUAIJCQa6bQ7ggAAAAANsJMGgDYmdOQJS+DcQb2FUcAgJ8g10yhSAMAGzMMpwzD+ydYWXEMAAC8Ra6ZQ7sjAAAAANgIM2kAYGeGYU1LR4DfYA0A8BPkmikUaQBgZ4ZFvfsBHmYAAD9BrplCuyMAAAAA2AgzaQBgZ06n5LDg5ugAv8EaAOAnyDVTKNIAwM5oCwEABBJyzRTaHQEAAADARphJAwAbM5xOGRa0hQT6+2QAAP6BXDOHmTQAAAAAsBFm0gDAzujdBwAEEnLNFIo0ALAzpyE5CDMAQIAg10yh3REAAAAAbISZNACwM8OQZMX7ZAL7iiMAwE+Qa6ZQpAGAjRlOQ4YFbSFGgIcZAMA/kGvm0O4IAAAAADZCkQYAdmY4rVvOw8yZM9W8eXOFh4erS5cu+vDDDy0+QQBAnUKumUKRBgA2ZjgNyxZPvfDCCxozZowefPBBffTRR2rfvr2ysrJ0+PDhGjhTAEBdQK6ZQ5EGADijp59+WsOHD9eQIUOUlpam2bNnq379+po3b56vhwYAgMf8Kdco0gDAznzUFlJRUaEtW7YoMzPTtS4oKEiZmZnKy8uz+iwBAHUFuWYKT3cEABurUqVkwQOsqlQpSSopKXFbHxYWprCwsNP2/+6771RdXa2EhAS39QkJCdqxY4f3AwIA1EnkmjkUaQBgQ6GhoUpMTNS6/DctO2aDBg2UnJzstu7BBx/UQw89ZNlnAABwJuSaZyjSAMCGwsPDtX//flVUVFh2TMMw5HA43Nad6WqjJDVu3FjBwcEqKChwW19QUKDExETLxgQAqBvINc9QpAGATYWHhys8PNwnnx0aGqpOnTpp1apV6tOnjyTJ6XRq1apVysnJ8cmYAAD+jVwzjyINAHBGY8aMUXZ2tjp37qzLLrtM06ZN0/HjxzVkyBBfDw0AAI/5U65RpAEAzuj//b//pyNHjmjSpEnKz89Xhw4dtGLFitNuugYAwB/4U645DMOw4PkqAAAAAAAr8J40AAAAALARijQAAAAAsBGKNAAAAACwEYo0AAAAALARijQAAAAAsBGKNAAAAACwEYo0AAAAALARijQAAAAAsBGKNAAAAACwEYo0AAAAALARijQAAAAAsBGKNAAAAACwkf8PqFGlVOVqGGoAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "_, ax = plt.subplots(1, 2, figsize=(10, 4), sharex=False, sharey=False\n", + ")\n", + "\n", + "for index in range(0, len(optimized_metrics)):\n", + " c_matrix = optimized_metrics.iloc[index][\"Confusion_matrix\"]\n", + " disp = ConfusionMatrixDisplay(\n", + " confusion_matrix=c_matrix, display_labels=[\"Less\", \"More\"]\n", + " ).plot(ax=ax.flat[index])\n", + "\n", + "plt.subplots_adjust(top=1, bottom=0, hspace=0.4, wspace=0.3)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "В желтом квадрате мы видим значение 1049, что обозначает количество правильно классифицированных объектов, отнесенных к классу \"Less\". Это свидетельствует о том, что модель успешно идентифицирует объекты этого класса, минимизируя количество ложных положительных срабатываний.\n", + "\n", + "В зеленом квадрате значение 558 указывает на количество правильно классифицированных объектов, отнесенных к классу \"More\". Это также является показателем высокой точности модели в определении объектов данного класса." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Определение достижимого уровня качества модели для второй задачи (добавляю конвейер для решения задачи регрессии)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Подготовка данных__" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Загрузка данных и создание целевой переменной" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Среднее значение поля 'Close': 30.058856538825285\n", + " Date Open High Low Close Adj Close Volume \\\n", + "0 1992-06-26 0.328125 0.347656 0.320313 0.335938 0.260703 224358400 \n", + "1 1992-06-29 0.339844 0.367188 0.332031 0.359375 0.278891 58732800 \n", + "2 1992-06-30 0.367188 0.371094 0.343750 0.347656 0.269797 34777600 \n", + "3 1992-07-01 0.351563 0.359375 0.339844 0.355469 0.275860 18316800 \n", + "4 1992-07-02 0.359375 0.359375 0.347656 0.355469 0.275860 13996800 \n", + "\n", + " above_average_close Close_Next_Day \n", + "0 0 0.359375 \n", + "1 0 0.347656 \n", + "2 0 0.355469 \n", + "3 0 0.355469 \n", + "4 0 0.355469 \n", + "Статистическое описание DataFrame:\n", + " Open High Low Close Adj Close \\\n", + "count 8035.000000 8035.000000 8035.000000 8035.000000 8035.000000 \n", + "mean 30.048051 30.345221 29.745172 30.052733 26.667480 \n", + "std 33.613031 33.904070 33.312079 33.613521 31.724640 \n", + "min 0.328125 0.347656 0.320313 0.335938 0.260703 \n", + "25% 4.391563 4.531250 4.304844 4.399219 3.413997 \n", + "50% 13.325000 13.485000 13.150000 13.330000 10.352452 \n", + "75% 55.250000 55.715000 54.829999 55.254999 47.461098 \n", + "max 126.080002 126.320000 124.809998 126.059998 118.010414 \n", + "\n", + " Volume above_average_close Close_Next_Day \n", + "count 8.035000e+03 8035.000000 8035.000000 \n", + "mean 1.470584e+07 0.347480 30.062556 \n", + "std 1.340058e+07 0.476199 33.616368 \n", + "min 1.504000e+06 0.000000 0.347656 \n", + "25% 7.818550e+06 0.000000 4.403125 \n", + "50% 1.170240e+07 0.000000 13.330000 \n", + "75% 1.778850e+07 1.000000 55.274999 \n", + "max 5.855088e+08 1.000000 126.059998 \n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "from sklearn import set_config\n", + "\n", + "set_config(transform_output=\"pandas\")\n", + "\n", + "# Загрузка данных о ценах акций Starbucks из CSV файла\n", + "df = pd.read_csv(\".//static//csv//Starbucks Dataset.csv\")\n", + "\n", + "# Опция для настройки генерации случайных чисел (если это нужно для других частей кода)\n", + "random_state = 42\n", + "\n", + "# Вычисление среднего значения поля \"Close\"\n", + "average_close = df['Close'].mean()\n", + "print(f\"Среднее значение поля 'Close': {average_close}\")\n", + "\n", + "# Создание новой колонки, указывающей, выше или ниже среднего значение цена закрытия\n", + "df['above_average_close'] = (df['Close'] > average_close).astype(int)\n", + "\n", + "# Создание целевой переменной для прогнозирования (цена закрытия на следующий день)\n", + "df['Close_Next_Day'] = df['Close'].shift(-1)\n", + "\n", + "# Удаление последней строки, где нет значения для следующего дня\n", + "df.dropna(inplace=True)\n", + "\n", + "# Вывод DataFrame с новой колонкой\n", + "print(df.head())\n", + "\n", + "# Примерный анализ данных\n", + "print(\"Статистическое описание DataFrame:\")\n", + "print(df.describe())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Разделение набора данных на обучающую и тестовые выборки (80/20) для задачи классификации\n", + "\n", + "Целевой признак -- above_average_close" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'X_train'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
DateOpenHighLowCloseAdj CloseVolumeabove_average_closeClose_Next_Day
24842002-05-065.8675005.8975005.6375005.6650004.3962991054520005.700000
15761998-09-221.8828131.9257811.8671881.9023441.4763064208000002.058594
65952018-08-3152.45999953.70999952.45000153.45000147.47341510892800153.529999
74122021-11-30109.550003111.089996109.050003109.639999103.48156094833001108.660004
74132021-12-01110.959999113.349998108.550003108.660004102.55661876185001111.419998
..............................
55192014-05-2736.32000036.88999936.27000036.83000230.46682010100400136.634998
45312010-06-2214.03500014.24000013.57500013.61500010.60963320533200013.660000
5351994-08-090.9062500.9218750.8906250.8984380.697229779520000.906250
7871995-08-081.1835941.1992191.1757811.1835940.9185231084800001.187500
79872024-03-1591.59999892.01999790.09999890.12000389.44142218133600191.010002
\n", + "

6428 rows × 9 columns

\n", + "
" + ], + "text/plain": [ + " Date Open High Low Close Adj Close \\\n", + "2484 2002-05-06 5.867500 5.897500 5.637500 5.665000 4.396299 \n", + "1576 1998-09-22 1.882813 1.925781 1.867188 1.902344 1.476306 \n", + "6595 2018-08-31 52.459999 53.709999 52.450001 53.450001 47.473415 \n", + "7412 2021-11-30 109.550003 111.089996 109.050003 109.639999 103.481560 \n", + "7413 2021-12-01 110.959999 113.349998 108.550003 108.660004 102.556618 \n", + "... ... ... ... ... ... ... \n", + "5519 2014-05-27 36.320000 36.889999 36.270000 36.830002 30.466820 \n", + "4531 2010-06-22 14.035000 14.240000 13.575000 13.615000 10.609633 \n", + "535 1994-08-09 0.906250 0.921875 0.890625 0.898438 0.697229 \n", + "787 1995-08-08 1.183594 1.199219 1.175781 1.183594 0.918523 \n", + "7987 2024-03-15 91.599998 92.019997 90.099998 90.120003 89.441422 \n", + "\n", + " Volume above_average_close Close_Next_Day \n", + "2484 10545200 0 5.700000 \n", + "1576 42080000 0 2.058594 \n", + "6595 10892800 1 53.529999 \n", + "7412 9483300 1 108.660004 \n", + "7413 7618500 1 111.419998 \n", + "... ... ... ... \n", + "5519 10100400 1 36.634998 \n", + "4531 20533200 0 13.660000 \n", + "535 7795200 0 0.906250 \n", + "787 10848000 0 1.187500 \n", + "7987 18133600 1 91.010002 \n", + "\n", + "[6428 rows x 9 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "'y_train'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
above_average_close
24840
15760
65951
74121
74131
......
55191
45310
5350
7870
79871
\n", + "

6428 rows × 1 columns

\n", + "
" + ], + "text/plain": [ + " above_average_close\n", + "2484 0\n", + "1576 0\n", + "6595 1\n", + "7412 1\n", + "7413 1\n", + "... ...\n", + "5519 1\n", + "4531 0\n", + "535 0\n", + "787 0\n", + "7987 1\n", + "\n", + "[6428 rows x 1 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "'X_test'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
DateOpenHighLowCloseAdj CloseVolumeabove_average_closeClose_Next_Day
50222012-06-0126.55500027.03000126.0200026.07500120.96061717456400026.950001
31102004-10-2812.89500013.21250012.7775013.21250010.25350612049600013.220000
29312004-02-129.3175009.3250009.205009.2450007.174544862360009.175000
68632019-09-2690.83999691.15000289.5000089.80000381.2864915026400188.370003
51472012-11-3025.70999926.00499925.5200025.93499921.01618211997400025.895000
..............................
29472004-03-089.4775009.5850009.342509.3650007.2676691432240009.382500
7841995-08-031.2304691.2304691.187501.2031250.9336801327040001.195313
41642009-01-065.0250005.1800004.975005.1100003.9655941760980004.995000
4551994-04-140.8046880.8281250.781250.8046880.624475599040000.785156
33352005-09-2011.62500011.77500011.5025011.5400008.95557013312000011.667500
\n", + "

1607 rows × 9 columns

\n", + "
" + ], + "text/plain": [ + " Date Open High Low Close Adj Close \\\n", + "5022 2012-06-01 26.555000 27.030001 26.02000 26.075001 20.960617 \n", + "3110 2004-10-28 12.895000 13.212500 12.77750 13.212500 10.253506 \n", + "2931 2004-02-12 9.317500 9.325000 9.20500 9.245000 7.174544 \n", + "6863 2019-09-26 90.839996 91.150002 89.50000 89.800003 81.286491 \n", + "5147 2012-11-30 25.709999 26.004999 25.52000 25.934999 21.016182 \n", + "... ... ... ... ... ... ... \n", + "2947 2004-03-08 9.477500 9.585000 9.34250 9.365000 7.267669 \n", + "784 1995-08-03 1.230469 1.230469 1.18750 1.203125 0.933680 \n", + "4164 2009-01-06 5.025000 5.180000 4.97500 5.110000 3.965594 \n", + "455 1994-04-14 0.804688 0.828125 0.78125 0.804688 0.624475 \n", + "3335 2005-09-20 11.625000 11.775000 11.50250 11.540000 8.955570 \n", + "\n", + " Volume above_average_close Close_Next_Day \n", + "5022 17456400 0 26.950001 \n", + "3110 12049600 0 13.220000 \n", + "2931 8623600 0 9.175000 \n", + "6863 5026400 1 88.370003 \n", + "5147 11997400 0 25.895000 \n", + "... ... ... ... \n", + "2947 14322400 0 9.382500 \n", + "784 13270400 0 1.195313 \n", + "4164 17609800 0 4.995000 \n", + "455 5990400 0 0.785156 \n", + "3335 13312000 0 11.667500 \n", + "\n", + "[1607 rows x 9 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "'y_test'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
above_average_close
50220
31100
29310
68631
51470
......
29470
7840
41640
4550
33350
\n", + "

1607 rows × 1 columns

\n", + "
" + ], + "text/plain": [ + " above_average_close\n", + "5022 0\n", + "3110 0\n", + "2931 0\n", + "6863 1\n", + "5147 0\n", + "... ...\n", + "2947 0\n", + "784 0\n", + "4164 0\n", + "455 0\n", + "3335 0\n", + "\n", + "[1607 rows x 1 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from typing import Tuple\n", + "import pandas as pd\n", + "from pandas import DataFrame\n", + "from sklearn.model_selection import train_test_split\n", + "\n", + "\n", + "def split_stratified_into_train_val_test(\n", + " df_input: DataFrame,\n", + " stratify_colname: str = \"y\",\n", + " frac_train: float = 0.6,\n", + " frac_val: float = 0.15,\n", + " frac_test: float = 0.25,\n", + " random_state: int = None,\n", + ") -> Tuple[DataFrame, DataFrame, DataFrame, DataFrame, DataFrame, DataFrame]:\n", + " \n", + "\n", + " if not (0 < frac_train < 1) or not (0 <= frac_val <= 1) or not (0 <= frac_test <= 1):\n", + " raise ValueError(\"Fractions must be between 0 and 1 and the sum must equal 1.\")\n", + " \n", + " if not (frac_train + frac_val + frac_test == 1.0):\n", + " raise ValueError(\"fractions %f, %f, %f do not add up to 1.0\" %\n", + " (frac_train, frac_val, frac_test))\n", + "\n", + " if stratify_colname not in df_input.columns:\n", + " raise ValueError(f\"{stratify_colname} is not a column in the DataFrame.\")\n", + "\n", + " X = df_input\n", + " y = df_input[[stratify_colname]]\n", + "\n", + " \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", + " if frac_val == 0:\n", + " return df_train, pd.DataFrame(), df_temp, y_train, pd.DataFrame(), y_temp\n", + "\n", + " relative_frac_test = frac_test / (frac_val + frac_test)\n", + "\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, y_train, y_val, y_test\n", + "\n", + "\n", + "X_train, X_val, X_test, y_train, y_val, y_test = split_stratified_into_train_val_test(\n", + " df, stratify_colname=\"above_average_close\", frac_train=0.80, frac_val=0.0, frac_test=0.20, random_state=random_state\n", + ")\n", + "\n", + "display(\"X_train\", X_train)\n", + "display(\"y_train\", y_train)\n", + "display(\"X_test\", X_test)\n", + "display(\"y_test\", y_test)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Формирование конвейера для классификации данных\n", + "\n", + "preprocessing_num -- конвейер для обработки числовых данных: заполнение пропущенных значений и стандартизация\n", + "\n", + "preprocessing_cat -- конвейер для обработки категориальных данных: заполнение пропущенных данных и унитарное кодирование\n", + "\n", + "features_preprocessing -- трансформер для предобработки признаков\n", + "\n", + "features_engineering -- трансформер для конструирования признаков\n", + "\n", + "drop_columns -- трансформер для удаления колонок\n", + "\n", + "pipeline_end -- основной конвейер предобработки данных и конструирования признаков" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from sklearn.base import BaseEstimator, TransformerMixin\n", + "from sklearn.compose import ColumnTransformer\n", + "from sklearn.discriminant_analysis import StandardScaler\n", + "from sklearn.impute import SimpleImputer\n", + "from sklearn.pipeline import Pipeline\n", + "from sklearn.preprocessing import OneHotEncoder\n", + "\n", + "class StarbucksFeatures(BaseEstimator, TransformerMixin):\n", + " def __init__(self):\n", + " pass\n", + " def fit(self, X, y=None):\n", + " return self\n", + " def transform(self, X, y=None):\n", + " X[\"Length_to_Width_Ratio\"] = X[\"x\"] / X[\"y\"]\n", + " return X\n", + " def get_feature_names_out(self, features_in):\n", + " return np.append(features_in, [\"Length_to_Width_Ratio\"], axis=0)\n", + " \n", + "\n", + "columns_to_drop = [\"Date\"]\n", + "num_columns = [\"Close\", \"Open\", \"Adj Close\", \"High\", \"Low\", \"Volume\", \"above_average_close\"]\n", + "cat_columns = []\n", + "\n", + "num_imputer = SimpleImputer(strategy=\"median\")\n", + "num_scaler = StandardScaler()\n", + "preprocessing_num = Pipeline(\n", + " [\n", + " (\"imputer\", num_imputer),\n", + " (\"scaler\", num_scaler),\n", + " ]\n", + ")\n", + "\n", + "cat_imputer = SimpleImputer(strategy=\"constant\", fill_value=\"unknown\")\n", + "cat_encoder = OneHotEncoder(handle_unknown=\"ignore\", sparse_output=False, drop=\"first\")\n", + "preprocessing_cat = Pipeline(\n", + " [\n", + " (\"imputer\", cat_imputer),\n", + " (\"encoder\", cat_encoder),\n", + " ]\n", + ")\n", + "\n", + "features_preprocessing = ColumnTransformer(\n", + " verbose_feature_names_out=False,\n", + " transformers=[\n", + " (\"prepocessing_num\", preprocessing_num, num_columns),\n", + " (\"prepocessing_cat\", preprocessing_cat, cat_columns),\n", + " ],\n", + " remainder=\"passthrough\"\n", + ")\n", + "\n", + "\n", + "drop_columns = ColumnTransformer(\n", + " verbose_feature_names_out=False,\n", + " transformers=[\n", + " (\"drop_columns\", \"drop\", columns_to_drop),\n", + " ],\n", + " remainder=\"passthrough\",\n", + ")\n", + "\n", + "features_postprocessing = ColumnTransformer(\n", + " verbose_feature_names_out=False,\n", + " transformers=[\n", + " (\"prepocessing_cat\", preprocessing_cat, [\"Cabin_type\"]),\n", + " ],\n", + " remainder=\"passthrough\",\n", + ")\n", + "\n", + "pipeline_end = Pipeline(\n", + " [\n", + " (\"features_preprocessing\", features_preprocessing),\n", + " (\"drop_columns\", drop_columns),\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Демонстрация работы конвейера__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
CloseOpenAdj CloseHighLowVolumeabove_average_closeClose_Next_Day
2484-0.723400-0.717267-0.700283-0.718936-0.721563-0.304340-0.7298405.700000
1576-0.835023-0.835490-0.792049-0.835755-0.8344321.970579-0.7298402.058594
65950.6942020.6651060.6535020.6873590.679824-0.2792641.37016453.529999
74122.3611482.3589322.4136702.3750592.374211-0.3809461.370164108.660004
74132.3320762.4007662.3846022.4415312.359243-0.5154721.370164111.419998
...........................
55190.2011490.1862410.1190360.1926370.195457-0.3364281.37016436.634998
4531-0.487553-0.474942-0.505016-0.473560-0.4839450.416194-0.72984013.660000
535-0.864806-0.864464-0.816533-0.865282-0.863666-0.502725-0.7298400.906250
787-0.856346-0.856235-0.809579-0.857125-0.855130-0.282496-0.7298401.187500
79871.7820631.8263661.9724311.8141591.8069210.2430871.37016491.010002
\n", + "

6428 rows × 8 columns

\n", + "
" + ], + "text/plain": [ + " Close Open Adj Close High Low Volume \\\n", + "2484 -0.723400 -0.717267 -0.700283 -0.718936 -0.721563 -0.304340 \n", + "1576 -0.835023 -0.835490 -0.792049 -0.835755 -0.834432 1.970579 \n", + "6595 0.694202 0.665106 0.653502 0.687359 0.679824 -0.279264 \n", + "7412 2.361148 2.358932 2.413670 2.375059 2.374211 -0.380946 \n", + "7413 2.332076 2.400766 2.384602 2.441531 2.359243 -0.515472 \n", + "... ... ... ... ... ... ... \n", + "5519 0.201149 0.186241 0.119036 0.192637 0.195457 -0.336428 \n", + "4531 -0.487553 -0.474942 -0.505016 -0.473560 -0.483945 0.416194 \n", + "535 -0.864806 -0.864464 -0.816533 -0.865282 -0.863666 -0.502725 \n", + "787 -0.856346 -0.856235 -0.809579 -0.857125 -0.855130 -0.282496 \n", + "7987 1.782063 1.826366 1.972431 1.814159 1.806921 0.243087 \n", + "\n", + " above_average_close Close_Next_Day \n", + "2484 -0.729840 5.700000 \n", + "1576 -0.729840 2.058594 \n", + "6595 1.370164 53.529999 \n", + "7412 1.370164 108.660004 \n", + "7413 1.370164 111.419998 \n", + "... ... ... \n", + "5519 1.370164 36.634998 \n", + "4531 -0.729840 13.660000 \n", + "535 -0.729840 0.906250 \n", + "787 -0.729840 1.187500 \n", + "7987 1.370164 91.010002 \n", + "\n", + "[6428 rows x 8 columns]" + ] + }, + "execution_count": 242, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "preprocessing_result = pipeline_end.fit_transform(X_train)\n", + "preprocessed_df = pd.DataFrame(\n", + " preprocessing_result,\n", + " columns=pipeline_end.get_feature_names_out(),\n", + ")\n", + "\n", + "preprocessed_df" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Формирование набора моделей для классификации\n", + "\n", + "logistic -- логистическая регрессия\n", + "\n", + "ridge -- гребневая регрессия\n", + "\n", + "decision_tree -- дерево решений\n", + "\n", + "knn -- k-ближайших соседей\n", + "\n", + "naive_bayes -- наивный Байесовский классификатор\n", + "\n", + "gradient_boosting -- метод градиентного бустинга (набор деревьев решений)\n", + "\n", + "random_forest -- метод случайного леса (набор деревьев решений)\n", + "\n", + "mlp -- многослойный персептрон (нейронная сеть)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn import ensemble, linear_model, naive_bayes, neighbors, neural_network, tree\n", + "\n", + "class_models = {\n", + " \"logistic\": {\"model\": linear_model.LogisticRegression()},\n", + " \"ridge\": {\"model\": linear_model.RidgeClassifierCV(cv=5, class_weight=\"balanced\")},\n", + " \"ridge\": {\"model\": linear_model.LogisticRegression(penalty=\"l2\", class_weight=\"balanced\")},\n", + " \"decision_tree\": {\n", + " \"model\": tree.DecisionTreeClassifier(max_depth=7, random_state=random_state)\n", + " },\n", + " \"knn\": {\"model\": neighbors.KNeighborsClassifier(n_neighbors=7)},\n", + " \"naive_bayes\": {\"model\": naive_bayes.GaussianNB()},\n", + " \"gradient_boosting\": {\n", + " \"model\": ensemble.GradientBoostingClassifier(n_estimators=210)\n", + " },\n", + " \"random_forest\": {\n", + " \"model\": ensemble.RandomForestClassifier(\n", + " max_depth=11, class_weight=\"balanced\", random_state=random_state\n", + " )\n", + " },\n", + " \"mlp\": {\n", + " \"model\": neural_network.MLPClassifier(\n", + " hidden_layer_sizes=(7,),\n", + " max_iter=500,\n", + " early_stopping=True,\n", + " random_state=random_state,\n", + " )\n", + " },\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Обучение моделей на обучающем наборе данных и оценка на тестовом" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model: logistic\n", + "Model: ridge\n", + "Model: decision_tree\n", + "Model: knn\n", + "Model: naive_bayes\n", + "Model: gradient_boosting\n", + "Model: random_forest\n", + "Model: mlp\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "from sklearn import metrics\n", + "\n", + "for model_name in class_models.keys():\n", + " print(f\"Model: {model_name}\")\n", + " model = class_models[model_name][\"model\"]\n", + "\n", + " model_pipeline = Pipeline([(\"pipeline\", pipeline_end), (\"model\", model)])\n", + " model_pipeline = model_pipeline.fit(X_train, y_train.values.ravel())\n", + "\n", + " y_train_predict = model_pipeline.predict(X_train)\n", + " y_test_probs = model_pipeline.predict_proba(X_test)[:, 1]\n", + " y_test_predict = np.where(y_test_probs > 0.5, 1, 0)\n", + "\n", + " class_models[model_name][\"pipeline\"] = model_pipeline\n", + " class_models[model_name][\"probs\"] = y_test_probs\n", + " class_models[model_name][\"preds\"] = y_test_predict\n", + "\n", + " class_models[model_name][\"Precision_train\"] = metrics.precision_score(\n", + " y_train, y_train_predict\n", + " )\n", + " class_models[model_name][\"Precision_test\"] = metrics.precision_score(\n", + " y_test, y_test_predict\n", + " )\n", + " class_models[model_name][\"Recall_train\"] = metrics.recall_score(\n", + " y_train, y_train_predict\n", + " )\n", + " class_models[model_name][\"Recall_test\"] = metrics.recall_score(\n", + " y_test, y_test_predict\n", + " )\n", + " class_models[model_name][\"Accuracy_train\"] = metrics.accuracy_score(\n", + " y_train, y_train_predict\n", + " )\n", + " class_models[model_name][\"Accuracy_test\"] = metrics.accuracy_score(\n", + " y_test, y_test_predict\n", + " )\n", + " class_models[model_name][\"ROC_AUC_test\"] = metrics.roc_auc_score(\n", + " y_test, y_test_probs\n", + " )\n", + " class_models[model_name][\"F1_train\"] = metrics.f1_score(y_train, y_train_predict)\n", + " class_models[model_name][\"F1_test\"] = metrics.f1_score(y_test, y_test_predict)\n", + " class_models[model_name][\"MCC_test\"] = metrics.matthews_corrcoef(\n", + " y_test, y_test_predict\n", + " )\n", + " class_models[model_name][\"Cohen_kappa_test\"] = metrics.cohen_kappa_score(\n", + " y_test, y_test_predict\n", + " )\n", + " class_models[model_name][\"Confusion_matrix\"] = metrics.confusion_matrix(\n", + " y_test, y_test_predict\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Сводная таблица оценок качества для использованных моделей классификации\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Матрица неточностей__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA0cAAAQ9CAYAAACSpDaqAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdeVwU9f8H8NdwI7BcciYi4kleeGTkmZJo5vHVvv40VFDTMry/nt+8NSm7TPNIy6v0a5eZmZnkmUrknQfirXgAKgKCAsvO/P4gtjZgYXVglp3X8/GYRzKfz85+ZpN9+Z7PHIIkSRKIiIiIiIhUzkrpARAREREREZkDFkdERERERERgcURERERERASAxREREREREREAFkdEREREREQAWBwREREREREBYHFEREREREQEgMURERERERERABZHREREREREAFgc0WNau3YtBEHA1atXK2T7V69ehSAIWLt2rSzb27t3LwRBwN69e2XZHhERkaWYPXs2BEEoV19BEDB79uyKHRCRglgckUVZtmyZbAUVEREREamLjdIDICpJYGAgHj16BFtbW5Net2zZMlSvXh3R0dEG69u3b49Hjx7Bzs5OxlESERFVfdOnT8fUqVOVHgaRWWBxRGZJEAQ4ODjItj0rKytZt0dERGQJcnJy4OTkBBsb/pOQCOBpdSSjZcuW4emnn4a9vT38/f0RExODjIyMYv2WLl2K2rVrw9HREc888wx+/fVXdOzYER07dtT3Kemao5SUFAwZMgQ1atSAvb09/Pz80KtXL/11T7Vq1cKZM2ewb98+CIIAQRD02yztmqOEhAS8+OKLcHd3h5OTE5o0aYKPPvpI3g+GiIjIDBRdW3T27Fm88sorcHd3R9u2bUu85igvLw/jx4+Hl5cXXFxc0LNnT9y4caPE7e7duxctW7aEg4MDgoOD8cknn5R6HdMXX3yBFi1awNHRER4eHujfvz+Sk5MrZH+JHgcPE5AsZs+ejTlz5iA8PBwjR45EUlISli9fjsOHD+PgwYP60+OWL1+OUaNGoV27dhg/fjyuXr2K3r17w93dHTVq1DD6Hn379sWZM2cwevRo1KpVC2lpaYiLi8P169dRq1YtLFq0CKNHj4azszPefPNNAICPj0+p24uLi8NLL70EPz8/jB07Fr6+vkhMTMS2bdswduxY+T4cIiIiM/Lvf/8bdevW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4IMPPpDarly5km+9q1ev4vr16/Dy8pLex8LCAnXr1oW7uzu8vLxw8eJFhISElOr9iYhIeZjNRWM2k5x4QwYyEhISgmrVqqF79+44ePAgLl26hKioKIwaNQrXrl0DALz77rv49NNPsW3bNpw5cwbDhw8v8jkINWvWRGhoKAYNGoRt27ZJ29y8eTMAwMfHByqVCjt27MDNmzeRkZEBBwcHjBs3DmPGjMHatWtx4cIFnDhxAgsXLpQupHz77bdx7tw5jB8/HvHx8diwYQPWrFlTqv19+umncfXqVWzcuBEXLlzAggULCryA1cbGBqGhoTh16hQOHjyIUaNGoVevXvDw8AAATJs2DeHh4ViwYAHOnj2LP//8E6tXr8bcuXNLNR4iIqJHMZuZzVSO5L7oicrOwxd9lqb/xo0bYsCAAaJatWpCo9GIWrVqiSFDhoi0tDQhRO5Fnu+++67QarXCyclJjB07VgwYMKDQiz6FEOLBgwdizJgxwtPTU6jVauHn5ydWrVol9U+fPl14eHgIlUolQkNDhRC5F6rOnz9f1K1bV1hbWwtXV1cRHBws9u/fL73up59+En5+fkKj0Yg2bdqIVatWlfqiz/Hjx4uqVasKe3t78cYbb4h58+YJR0dHqX/KlCmicePGYvHixcLLy0vY2NiI119/XaSkpBhtd/369aJJkyZCrVYLZ2dn0bZtW7F161YhBC/6JCKiXMzmgjGbyVyohCjkSj0iIiIiIiIF4Wl1REREREREYHFEREREREQEgMURERERERERABZHREREREREAFgcERERERERAWBxREREREREBIDFEREREREREQAWR0RERERERABYHBEREREREQFgcURERERERASAxREREREREREAFkdEREREREQAWBwREREREREBYHFEREREREQEgMURldILL7yAF154QfYxNGjQQNYxEBERlTWVSoWpU6dKP69ZswYqlQqXL1+WbUxFiYqKgkqlwpYtW+QeSqlcvnwZKpUKa9askXsoZAZYHBERERERAOD+/fuYOnUqoqKi5B6KyW3YsAHz58+Xexhk5qzkHgBVLHv27JF7CERERIrUv39/9O7dGxqNpsze4/79+5g2bRoAyH6miKlt2LABf/31F0aPHm3U7uPjgwcPHsDa2lqegZFZYXFEpaJWq+UeAhERkdkyGAzQ6XSwsbEx+bYtLS1h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iz///BOrV6/G3LlzSzUeIiKiRzGbmc1UjuS+6InKzsMXfZam/8aNG2LAgAGiWrVqQqPRiFq1aokhQ4aItLQ0IUTuRZ7vvvuu0Gq1wsnJSYwdO1YMGDCg0Is+hRDiwYMHYsyYMcLT01Oo1Wrh5+cnVq1aJfVPnz5deHh4CJVKJUJDQ4UQuReqzp8/X9StW1dYW1sLV1dXERwcLPbv3y+97qeffhJ+fn5Co9GINm3aiFWrVpX6os/x48eLqlWrCnt7e/HGG2+IefPmCUdHR6l/ypQponHjxmLx4sXCy8tL2NjYiNdff12kpKQYbXf9+vWiSZMmQq1WC2dnZ9G2bVuxdetWIQQv+iQiolzM5oIxm8lcqIQo5Eo9IiIiIiIiBeFpdURERERERGBxREREREREBIDFEREREREREQAWR0RERERERABYHBEREREREQFgcURERERERASAxREREREREREAFkdEREREREQAWBwREREREREBYHFEREREREQEgMURERERERERABZHREREREREAFgcERERERERAWBxREREREREBIDFET0hlUqFqVOnyj2MQn399deoV68erK2t4eTkJPdwiIiIKpXLly9DpVJhzZo1cg+FyCRYHFGldebMGYSFhaF27dpYsWIFli9fLveQ8rl+/TqmTp2K2NhYuYdCREREpHhWcg+AqKxERUXBYDDgiy++gJ+fn9zDKdD169cxbdo01KxZE02aNJF7OERERESKxpmjSubevXtyD8FsJCcnA4BJT6e7f/++ybZFREREROaFxVEFNnXqVKhUKvz999/o27cvnJ2d0bp1a/zxxx8ICwtDrVq1YGNjAw8PDwwaNAi3b98u8PXnz59HWFgYnJyc4OjoiIEDB+YrArKysjBmzBi4urrCwcEB3bp1w7Vr1woc18mTJ9G5c2dotVrY29ujQ4cO+O2334zWWbNmDVQqFQ4dOoRRo0bB1dUVTk5OeOutt6DT6ZCamooBAwbA2dkZzs7OmDBhAoQQJf5satasiSlTpgAAXF1d810btXjxYjzzzDPQaDTw8vLCiBEjkJqaarSNF154AQ0aNMDx48fRtm1bVKlSBe+//770eUyZMgV+fn7QaDTw9vbGhAkTkJWVZbSNyMhItG7dGk5OTrC3t0fdunWlbURFReHZZ58FAAwcOBAqlYrnbRMRUbnL+3vg7Nmz6Nev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SJjN6mA2kzU4qVeWkZLOnDmDffv2wdPTs1rfpUuX0LdvXzz77LNo37494uLi0LdvX7z88svw9fWFn58fEhISEBcXhz59+iA2NhaDBw9GcHCwCnti25o2bYr+/ftj7dq1EEKgf//+aNKkibH/0qVLKC8vR48ePYxtzs7O6Nq1KzIzM022FR0dbfzzxYsXUVxcjD59+pisU1ZWZnJaABERNR7M5obBbCYlsTiyE0VFRXjhhRfwl7/8pVpfcHAwHB0dkZaWhsOHD+Prr7/GBx98gDfeeANHjx5FixYtkJKSgokTJ2LPnj34/PPP8eabbyItLQ3du3dXYW9s26hRozB+/HgAwPLlyx95Ox4eHsY/FxUVAQB27dqFZs2amayn0+ke+TOIiEg9zOaGw2wmpfCaIzvRuXNnnDt3Do899hgiIiJMlqp/6JIkoUePHpg3bx5OnToFFxcXbNu2zbiNqKgozJo1C4cPH0a7du2wceNGtXbHpvXr1w9lZWUoLy9HXFycSV+rVq3g4uKC9PR0Y1t5eTmOHTuGyMjIGrcZGRkJnU6H7Ozsan9/oaGhVtsXIiKyHmZzw2E2k1I4c9QIFRQU4PTp0yZtY8eOxapVqzBkyBBMnz4dfn5+uHjxIjZt2oRPPvkEx48fx969e9G3b18EBATg6NGjuHXrFtq2bYsrV67g448/xosvvoiQkBBkZWXhxx9/xIgRI9TZQRvn6OhonIZ3dHQ06fPw8MC4ceMwbdo0+Pn5ISwsDIsXL0ZxcTFGjx5d4za9vLwwdepUJCUlQZZl9OzZEwUFBUhPT4der0d8fLxV94mIiOqH2awuZjMphcVRI7R///5q57qOHj0a6enpmDFjBvr27YvS0lKEh4ejX79+cHBwgF6vx7fffoulS5eisLAQ4eHheO+99/Dcc88hLy8PFy5cwLp163D79m0EBwcjMTER//u//6vSHto+vV5fY9+iRYsgyzKGDx+Ou3fvIjo6Gl999RV8fX3NbnPBggVo2rQpkpOTcfnyZfj4+KBz5854/fXXlR4+EREpjNmsPmYzKUESQgi1B0FERERERKQ2XnNEREREREQEFkdEREREREQAWBwREREREREBYHFEREREREQEgMURERERERERABZHREREREREAFgcERERERERAWBxREREREREBIDFESkkISEBAwcONL5+6qmnMHny5AYf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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from sklearn.metrics import ConfusionMatrixDisplay\n", + "import matplotlib.pyplot as plt\n", + "\n", + "_, ax = plt.subplots(int(len(class_models) / 2), 2, figsize=(12, 10), sharex=False, sharey=False)\n", + "for index, key in enumerate(class_models.keys()):\n", + " c_matrix = class_models[key][\"Confusion_matrix\"]\n", + " disp = ConfusionMatrixDisplay(\n", + " confusion_matrix=c_matrix, display_labels=[\"Less\", \"More\"]\n", + " ).plot(ax=ax.flat[index])\n", + " disp.ax_.set_title(key)\n", + "\n", + "plt.subplots_adjust(top=1, bottom=0, hspace=0.4, wspace=0.1)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Значение 1049 в желтом квадрате представляет собой количество объектов, относимых к классу \"Less\", которые модель правильно классифицировала. Это свидетельствует о высоком уровне точности в идентификации этого класса.\n", + "Значение 558 в зеленом квадрате указывает на количество правильно классифицированных объектов класса \"More\". Хотя это также является положительным результатом, мы можем заметить, что он ниже, чем для класса \"Less\".\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Точность, полнота, верность (аккуратность), F-мера__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
 Precision_trainPrecision_testRecall_trainRecall_testAccuracy_trainAccuracy_testF1_trainF1_test
logistic1.0000001.0000001.0000001.0000001.0000001.0000001.0000001.000000
ridge1.0000001.0000001.0000001.0000001.0000001.0000001.0000001.000000
knn1.0000001.0000001.0000001.0000001.0000001.0000001.0000001.000000
naive_bayes1.0000001.0000001.0000001.0000001.0000001.0000001.0000001.000000
mlp1.0000001.0000001.0000001.0000001.0000001.0000001.0000001.000000
random_forest1.0000001.0000001.0000001.0000001.0000001.0000001.0000001.000000
decision_tree1.0000001.0000001.0000000.9982081.0000000.9993781.0000000.999103
gradient_boosting1.0000001.0000001.0000000.9982081.0000000.9993781.0000000.999103
\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 247, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "class_metrics = pd.DataFrame.from_dict(class_models, \"index\")[\n", + " [\n", + " \"Precision_train\",\n", + " \"Precision_test\",\n", + " \"Recall_train\",\n", + " \"Recall_test\",\n", + " \"Accuracy_train\",\n", + " \"Accuracy_test\",\n", + " \"F1_train\",\n", + " \"F1_test\",\n", + " ]\n", + "]\n", + "class_metrics.sort_values(\n", + " by=\"Accuracy_test\", ascending=False\n", + ").style.background_gradient(\n", + " cmap=\"plasma\",\n", + " low=0.3,\n", + " high=1,\n", + " subset=[\"Accuracy_train\", \"Accuracy_test\", \"F1_train\", \"F1_test\"],\n", + ").background_gradient(\n", + " cmap=\"viridis\",\n", + " low=1,\n", + " high=0.3,\n", + " subset=[\n", + " \"Precision_train\",\n", + " \"Precision_test\",\n", + " \"Recall_train\",\n", + " \"Recall_test\",\n", + " ],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Все модели, включая логистическую регрессию, ридж-регрессию, KNN, наивный байесовский классификатор, многослойную перцептронную сеть, случайный лес, дерево решений и градиентный бустинг, демонстрируют 100% точность (1.000000) на обучающей выборке.\n", + "Это указывает на то, что модели смогли полностью подстроиться под обучающие данные, что может стремительно указывать на возможное переобучение.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__ROC-кривая, каппа Коэна, коэффициент корреляции Мэтьюса__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
 Accuracy_testF1_testROC_AUC_testCohen_kappa_testMCC_test
logistic1.0000001.0000001.0000001.0000001.000000
ridge1.0000001.0000001.0000001.0000001.000000
knn1.0000001.0000001.0000001.0000001.000000
naive_bayes1.0000001.0000001.0000001.0000001.000000
random_forest1.0000001.0000001.0000001.0000001.000000
gradient_boosting0.9993780.9991031.0000000.9986270.998628
mlp1.0000001.0000001.0000001.0000001.000000
decision_tree0.9993780.9991030.9991040.9986270.998628
\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 248, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "class_metrics = pd.DataFrame.from_dict(class_models, \"index\")[\n", + " [\n", + " \"Accuracy_test\",\n", + " \"F1_test\",\n", + " \"ROC_AUC_test\",\n", + " \"Cohen_kappa_test\",\n", + " \"MCC_test\",\n", + " ]\n", + "]\n", + "class_metrics.sort_values(by=\"ROC_AUC_test\", ascending=False).style.background_gradient(\n", + " cmap=\"plasma\",\n", + " low=0.3,\n", + " high=1,\n", + " subset=[\n", + " \"ROC_AUC_test\",\n", + " \"MCC_test\",\n", + " \"Cohen_kappa_test\",\n", + " ],\n", + ").background_gradient(\n", + " cmap=\"viridis\",\n", + " low=1,\n", + " high=0.3,\n", + " subset=[\n", + " \"Accuracy_test\",\n", + " \"F1_test\",\n", + " ],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Почти все модели, включая логистическую регрессию, ридж-регрессию, KNN, наивный байесовский классификатор, случайный лес и многослойную перцептронную сеть, достигли показателя ROC AUC равного 1.000000. Это говорит о том, что они идеально разделяют классы.\n", + "Градиентный бустинг и дерево решений немного уступили в значениях ROC AUC, составив 0.999378, что говорит о высокой, но не идеальной способности к классификации." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'logistic'" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "best_model = str(class_metrics.sort_values(by=\"MCC_test\", ascending=False).iloc[0].name)\n", + "\n", + "display(best_model)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Вывод данных с ошибкой предсказания для оценки" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Error items count: 0'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
DatePredictedOpenHighLowCloseAdj CloseVolumeabove_average_closeClose_Next_Day
\n", + "
" + ], + "text/plain": [ + "Empty DataFrame\n", + "Columns: [Date, Predicted, Open, High, Low, Close, Adj Close, Volume, above_average_close, Close_Next_Day]\n", + "Index: []" + ] + }, + "execution_count": 250, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "preprocessing_result = pipeline_end.transform(X_test)\n", + "preprocessed_df = pd.DataFrame(\n", + " preprocessing_result,\n", + " columns=pipeline_end.get_feature_names_out(),\n", + ")\n", + "\n", + "y_pred = class_models[best_model][\"preds\"]\n", + "\n", + "error_index = y_test[y_test[\"above_average_close\"] != y_pred].index.tolist()\n", + "display(f\"Error items count: {len(error_index)}\")\n", + "\n", + "error_predicted = pd.Series(y_pred, index=y_test.index).loc[error_index]\n", + "error_df = X_test.loc[error_index].copy()\n", + "error_df.insert(loc=1, column=\"Predicted\", value=error_predicted)\n", + "error_df.sort_index()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Пример использования обученной модели (конвейера) для предсказания" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
DateOpenHighLowCloseAdj CloseVolumeabove_average_closeClose_Next_Day
68632019-09-2690.83999691.15000289.589.80000381.2864915026400188.370003
\n", + "
" + ], + "text/plain": [ + " Date Open High Low Close Adj Close Volume \\\n", + "6863 2019-09-26 90.839996 91.150002 89.5 89.800003 81.286491 5026400 \n", + "\n", + " above_average_close Close_Next_Day \n", + "6863 1 88.370003 " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
CloseOpenAdj CloseHighLowVolumeabove_average_closeClose_Next_Day
68631.772571.8038181.7161461.788571.788959-0.7024661.37016488.370003
\n", + "
" + ], + "text/plain": [ + " Close Open Adj Close High Low Volume \\\n", + "6863 1.77257 1.803818 1.716146 1.78857 1.788959 -0.702466 \n", + "\n", + " above_average_close Close_Next_Day \n", + "6863 1.370164 88.370003 " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "'predicted: 1 (proba: [0. 1.])'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "'real: 1'" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "model = class_models[best_model][\"pipeline\"]\n", + "\n", + "example_id = 6863\n", + "test = pd.DataFrame(X_test.loc[example_id, :]).T\n", + "test_preprocessed = pd.DataFrame(preprocessed_df.loc[example_id, :]).T\n", + "display(test)\n", + "display(test_preprocessed)\n", + "result_proba = model.predict_proba(test)[0]\n", + "result = model.predict(test)[0]\n", + "real = int(y_test.loc[example_id].values[0])\n", + "display(f\"predicted: {result} (proba: {result_proba})\")\n", + "display(f\"real: {real}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Подбор гиперпараметров методом поиска по сетке" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'model__criterion': 'gini',\n", + " 'model__max_depth': 5,\n", + " 'model__max_features': 'log2',\n", + " 'model__n_estimators': 10}" + ] + }, + "execution_count": 252, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from sklearn.model_selection import GridSearchCV\n", + "\n", + "optimized_model_type = \"random_forest\"\n", + "\n", + "random_forest_model = class_models[optimized_model_type][\"pipeline\"]\n", + "\n", + "param_grid = {\n", + " \"model__n_estimators\": [10, 50, 100],\n", + " \"model__max_features\": [\"sqrt\", \"log2\"],\n", + " \"model__max_depth\": [5, 7, 10],\n", + " \"model__criterion\": [\"gini\", \"entropy\"],\n", + "}\n", + "\n", + "gs_optomizer = GridSearchCV(\n", + " estimator=random_forest_model, param_grid=param_grid, n_jobs=-1\n", + ")\n", + "gs_optomizer.fit(X_train, y_train.values.ravel())\n", + "gs_optomizer.best_params_" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Обучение модели с новыми гиперпараметрами__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "optimized_model = ensemble.RandomForestClassifier(\n", + " random_state=random_state,\n", + " criterion=\"gini\",\n", + " max_depth=5,\n", + " max_features=\"log2\",\n", + " n_estimators=10,\n", + ")\n", + "\n", + "result = {}\n", + "\n", + "result[\"pipeline\"] = Pipeline([(\"pipeline\", pipeline_end), (\"model\", optimized_model)]).fit(X_train, y_train.values.ravel())\n", + "result[\"train_preds\"] = result[\"pipeline\"].predict(X_train)\n", + "result[\"probs\"] = result[\"pipeline\"].predict_proba(X_test)[:, 1]\n", + "result[\"preds\"] = np.where(result[\"probs\"] > 0.5, 1, 0)\n", + "\n", + "result[\"Precision_train\"] = metrics.precision_score(y_train, result[\"train_preds\"])\n", + "result[\"Precision_test\"] = metrics.precision_score(y_test, result[\"preds\"])\n", + "result[\"Recall_train\"] = metrics.recall_score(y_train, result[\"train_preds\"])\n", + "result[\"Recall_test\"] = metrics.recall_score(y_test, result[\"preds\"])\n", + "result[\"Accuracy_train\"] = metrics.accuracy_score(y_train, result[\"train_preds\"])\n", + "result[\"Accuracy_test\"] = metrics.accuracy_score(y_test, result[\"preds\"])\n", + "result[\"ROC_AUC_test\"] = metrics.roc_auc_score(y_test, result[\"probs\"])\n", + "result[\"F1_train\"] = metrics.f1_score(y_train, result[\"train_preds\"])\n", + "result[\"F1_test\"] = metrics.f1_score(y_test, result[\"preds\"])\n", + "result[\"MCC_test\"] = metrics.matthews_corrcoef(y_test, result[\"preds\"])\n", + "result[\"Cohen_kappa_test\"] = metrics.cohen_kappa_score(y_test, result[\"preds\"])\n", + "result[\"Confusion_matrix\"] = metrics.confusion_matrix(y_test, result[\"preds\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Формирование данных для оценки старой и новой версии модели__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "optimized_metrics = pd.DataFrame(columns=list(result.keys()))\n", + "optimized_metrics.loc[len(optimized_metrics)] = pd.Series(\n", + " data=class_models[optimized_model_type]\n", + ")\n", + "optimized_metrics.loc[len(optimized_metrics)] = pd.Series(\n", + " data=result\n", + ")\n", + "optimized_metrics.insert(loc=0, column=\"Name\", value=[\"Old\", \"New\"])\n", + "optimized_metrics = optimized_metrics.set_index(\"Name\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Оценка параметров старой и новой модели__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
 Precision_trainPrecision_testRecall_trainRecall_testAccuracy_trainAccuracy_testF1_trainF1_test
Name        
Old1.0000001.0000001.0000001.0000001.0000001.0000001.0000001.000000
New1.0000001.0000001.0000001.0000001.0000001.0000001.0000001.000000
\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 255, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "optimized_metrics[\n", + " [\n", + " \"Precision_train\",\n", + " \"Precision_test\",\n", + " \"Recall_train\",\n", + " \"Recall_test\",\n", + " \"Accuracy_train\",\n", + " \"Accuracy_test\",\n", + " \"F1_train\",\n", + " \"F1_test\",\n", + " ]\n", + "].style.background_gradient(\n", + " cmap=\"plasma\",\n", + " low=0.3,\n", + " high=1,\n", + " subset=[\"Accuracy_train\", \"Accuracy_test\", \"F1_train\", \"F1_test\"],\n", + ").background_gradient(\n", + " cmap=\"viridis\",\n", + " low=1,\n", + " high=0.3,\n", + " subset=[\n", + " \"Precision_train\",\n", + " \"Precision_test\",\n", + " \"Recall_train\",\n", + " \"Recall_test\",\n", + " ],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Как для обучающей (Precision_train), так и для тестовой (Precision_test) выборки обе модели достигли идеальных значений 1.000000. Это указывает на то, что модели очень точно классифицируют положительные образцы, не пропуская их." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
 Accuracy_testF1_testROC_AUC_testCohen_kappa_testMCC_test
Name     
Old1.0000001.0000001.0000001.0000001.000000
New1.0000001.0000001.0000001.0000001.000000
\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 256, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "optimized_metrics[\n", + " [\n", + " \"Accuracy_test\",\n", + " \"F1_test\",\n", + " \"ROC_AUC_test\",\n", + " \"Cohen_kappa_test\",\n", + " \"MCC_test\",\n", + " ]\n", + "].style.background_gradient(\n", + " cmap=\"plasma\",\n", + " low=0.3,\n", + " high=1,\n", + " subset=[\n", + " \"ROC_AUC_test\",\n", + " \"MCC_test\",\n", + " \"Cohen_kappa_test\",\n", + " ],\n", + ").background_gradient(\n", + " cmap=\"viridis\",\n", + " low=1,\n", + " high=0.3,\n", + " subset=[\n", + " \"Accuracy_test\",\n", + " \"F1_test\",\n", + " ],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Оба варианта модели продемонстрировали безупречную точность классификации, достигнув значения 1.000000. Это свидетельствует о том, что модели точно классифицировали все тестовые примеры, не допустив никаких ошибок в предсказаниях." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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nWvp4ACgzrO4LSQ6OtptOVuYNW3aVGlpaW7rUlNTdeDAAUlSYmKiJKmgoMBtn4KCAte2xMREHT582G17VVWVCgsLXfvYAbkGALWIXDPF4yJt0aJFeu655zRgwAAFBwe71rdv3147duywdHAAAN/o2rWrdu7c6bZu165dSklJkXTyZuvExEStWrXKtb2kpEQbN25URkaGJCkjI0NFRUXasmWLa5/33ntPTqdTXbp0qYWzMIdcA4DA52+55nG74zfffKPWrVuftt7pdKqystKSQQEA/seL98GcdhwPjB49WldccYWmTJmifv366cMPP9Rzzz2n5557TtLJpx6OGjVKjz76qC666CLXo4qTkpJcT05MTU1Vz549NXz4cM2ePVuVlZXKyclR//79ffpkx58j1wCgFpFrpnhcpKWlpen99993VZ2nvPTSS0pPT7dsYAAASYbj5GLFcTzw61//Wq+++qruu+8+TZ48WS1atNC0adM0YMAA1z7jx4/X8ePHNWLECBUVFalbt25asWKF610ykrR48WLl5OTouuuuU1BQkG699VZNnz7d+/OxELkGALWIXDPF4yJt0qRJys7O1jfffCOn06lXXnlFO3fu1KJFi7R8+XLLBwgA8I0bb7xRN95441m3OxwOTZ48WZMnTz7rPnFxcT5/cfUvIdcAoG7wp1zz+J60W265Ra+//rreffddRUZGatKkSdq+fbtef/11XX/99TUxRgCoswzDugVnRq4BQO0h18zxeCZNkq688kqtXLnS6rEAAH7OR737dQ25BgC1hFwz5byKNEnavHmztm/fLulkP3+nTp0sGxQAALWNXAMA2IXHRdrXX3+t2267TevXr1dsbKwkqaioSFdccYWWLl2qCy+80OoxAkDd5aMbrOsScg0AahG5ZorH96T94Q9/UGVlpbZv367CwkIVFhZq+/btcjqd+sMf/lATYwSAOsthWLfgzMg1AKg95Jo5Hs+krVmzRhs2bFCbNm1c69q0aaMZM2boyiuvtHRwAADUNHINAGA3HhdpycnJZ3y5Z3V1ta1eTgoAAYEbrGscuQYAtYhcM8Xjdscnn3xSd911lzZv3uxat3nzZt1zzz3661//aungAKDOO9W7b8WCMyLXAKAWkWummJpJa9iwoRyOH38Qx48fV5cuXRQScvLbq6qqFBISoqFDh6pPnz41MlAAAKxC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s2bNUqtWrfTUU09Jktq0aaPPPvtMTzzxhEc/mwceeMD17+bNm2vs2LFaunSpW5iVlZVp0aJFuuCCCyRJM2bMUO/evfXUU08pMTFRDz74oJ566inXz6RFixb64osv9OyzzxJmAFADyLWzI9cAcyjS4DPLly9XgwYNVFlZKafTqd///vd66KGHXNvbtWvn1q//ySefaM+ePYqKinI7TllZmfbu3avi4mJ9++236tKli2tbSEiIOnfufFpryClbt25VcHCwunfvbnrce/bs0YkTJ3T99de7ra+oqFB6erokafv27W7jkOQKPk+88MILmj59uvbu3avS0lJVVVUpOjrabZ9mzZq5guzU5zidTu3cuVNRUVHau3evhg0bpuHDh7v2qaqqUkxMjMfjgQ8YsuiKo/eHAHBu5NovI9dArplDkQafueaaazRr1iyFhoYqKSlJISHuv46RkZFuX5eWlqpTp06udoefatKkyXmN4VSbhydKS0slSW+88YZbiEgn70ewSl5engYMGKCHH35YWVlZiomJ0dKlS11XMT0Z65w5c04L1+DgYMvGihpEWwjgN8i1cyPXIIlcM4kiDT4TGRmp1q1bm96/Y8eOeuGFFxQfH3/aVbdTmjZtqo0bN+qqq66SdPLK2pYtW9SxY8cz7t+uXTs5nU6tWbPG1RbyU6eueFZXV7vWpaWlKSwsTAcOHDjrlcrU1FTXzeKnfPDBB798kj+xYcMGpaSk6P7773et++qrr07b78CBAzp06JCSkpJcnxMUFKQ2bdooISFBSUlJ2rdvnwYMGODR5wMAPEOunRu5BpjH0x3hNwYMGKDGjRvrlltu0fvvv6/9+/dr9erVuvvuu/X1119Lku655x49/vjjWrZsmXbs2KE777zznO+Cad68ubKzszV06FAtW7bMdcwXX3xRkpSSkiKHw6Hly5fryJEjKi0tVVRUlMaOHavRo0dr4cKF2rt3rz766CPNmDFDCxculCT96U9/0u7duzVu3Djt3LlTS5Ys0YIFCzw634suukgHDhzQ0qVLtXfvXk2fPl2vvvrqafuFh4crOztbn3zyid5//33dfffd6tevnxITEyVJDz/8sHJzczV9+nTt2rVLn332mebPn6+nn37ao/HAR5xO6xYAtkKukWt1ErlmCkUa/Eb9+vW1du1aNWvWTH379lVqaqqGDRumsrIy1xXIP//5zxo0aJCys7OVkZGhqKgo/eY3vznncWfNmqXf/va3uvPOO9W2bVsNHz5cx48flyRdcMEFevjhhzVhwgQlJCQoJydHkvTII49o4sSJys3NVWpqqnr27Kk33nhDLVq0kHSyn/7ll1/WsmXL1L59e82ePVtTpkzx6HxvvvlmjR49Wjk5OerQoYM2bNigiRMnnrZf69at1bdvX91www3q0aOHLr30UrdHEf/hD3/Q3LlzNX/+fLVr107du3fXggULXGMFAPgGuUauAWfjMM525ykAwGdKSkoUExOjzCbDFBLk/Qtvq5wVevfI8youLj5rWxUAADWFXPMM96QBgJ1xgzUAIJCQa6bQ7ggAAAAANsJMGgDYmdOQJS+DcQb2FUcAgJ8g10yhSAMAGzMMpwzD+ydYWXEMAAC8Ra6ZQ7sjAAAAANgIM2kAYGeGYU1LR4DfYA0A8BPkmikUaQBgZ4ZFvfsBHmYAAD9BrplCuyMAAAAA2AgzaQBgZ06n5LDg5ugAv8EaAOAnyDVTKNIAwM5oCwEABBJyzRTaHQEAAADARphJAwAbM5xOGRa0hQT6+2QAAP6BXDOHmTQAAAAAsBFm0gDAzujdBwAEEnLNFIo0ALAzpyE5CDMAQIAg10yh3REAAAAAbISZNACwM8OQZMX7ZAL7iiMAwE+Qa6ZQpAGAjRlOQ4YFbSFGgIcZAMA/kGvm0O4IAAAAADZCkQYAdmY4rVvOw8yZM9W8eXOFh4erS5cu+vDDDy0+QQBAnUKumUKRBgA2ZjgNyxZPvfDCCxozZowefPBBffTRR2rfvr2ysrJ0+PDhGjhTAEBdQK6ZQ5EGADijp59+WsOHD9eQIUOUlpam2bNnq379+po3b56vhwYAgMf8Kdco0gDAznzUFlJRUaEtW7YoMzPTtS4oKEiZmZnKy8uz+iwBAHUFuWYKT3cEABurUqVkwQOsqlQpSSopKXFbHxYWprCwsNP2/+6771RdXa2EhAS39QkJCdqxY4f3AwIA1EnkmjkUaQBgQ6GhoUpMTNS6/DctO2aDBg2UnJzstu7BBx/UQw89ZNlnAABwJuSaZyjSAMCGwsPDtX//flVUVFh2TMMw5HA43Nad6WqjJDVu3FjBwcEqKChwW19QUKDExETLxgQAqBvINc9QpAGATYWHhys8PNwnnx0aGqpOnTpp1apV6tOnjyTJ6XRq1apVysnJ8cmYAAD+jVwzjyINAHBGY8aMUXZ2tjp37qzLLrtM06ZN0/HjxzVkyBBfDw0AAI/5U65RpAEAzuj//b//pyNHjmjSpEnKz89Xhw4dtGLFitNuugYAwB/4U645DMOw4PkqAAAAAAAr8J40AAAAALARijQAAAAAsBGKNAAAAACwEYo0AAAAALARijQAAAAAsBGKNAAAAACwEYo0AAAAALARijQAAAAAsBGKNAAAAACwEYo0AAAAALARijQAAAAAsBGKNAAAAACwkf8PqFGlVOVqGGoAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "_, ax = plt.subplots(1, 2, figsize=(10, 4), sharex=False, sharey=False\n", + ")\n", + "\n", + "for index in range(0, len(optimized_metrics)):\n", + " c_matrix = optimized_metrics.iloc[index][\"Confusion_matrix\"]\n", + " disp = ConfusionMatrixDisplay(\n", + " confusion_matrix=c_matrix, display_labels=[\"Less\", \"More\"]\n", + " ).plot(ax=ax.flat[index])\n", + "\n", + "plt.subplots_adjust(top=1, bottom=0, hspace=0.4, wspace=0.3)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "В желтом квадрате мы видим значение 1049, что обозначает количество правильно классифицированных объектов, отнесенных к классу \"Less\". Это свидетельствует о том, что модель успешно идентифицирует объекты этого класса, минимизируя количество ложных положительных срабатываний.\n", + "\n", + "В зеленом квадрате значение 558 указывает на количество правильно классифицированных объектов, отнесенных к классу \"More\". Это также является показателем высокой точности модели в определении объектов данного класса." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Определение достижимого уровня качества модели для второй задачи (задача регрессии)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__2. Прогнозирование цены закрытия акций:__\n", + "\n", + "\n", + "Описание: Оценить, какая будет цена закрытия акций Starbucks на следующий день или через несколько дней на основе исторических данных.\n", + "Целевая переменная: Цена закрытия (Close). (среднее значение)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Загрузка данных и создание целевой переменной" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Среднее значение поля 'Close': 30.058856538825285\n", + " Date Open High Low Close Adj Close Volume \\\n", + "0 1992-06-26 0.328125 0.347656 0.320313 0.335938 0.260703 224358400 \n", + "1 1992-06-29 0.339844 0.367188 0.332031 0.359375 0.278891 58732800 \n", + "2 1992-06-30 0.367188 0.371094 0.343750 0.347656 0.269797 34777600 \n", + "3 1992-07-01 0.351563 0.359375 0.339844 0.355469 0.275860 18316800 \n", + "4 1992-07-02 0.359375 0.359375 0.347656 0.355469 0.275860 13996800 \n", + "\n", + " above_average_close Close_Next_Day \n", + "0 0 0.359375 \n", + "1 0 0.347656 \n", + "2 0 0.355469 \n", + "3 0 0.355469 \n", + "4 0 0.355469 \n", + "Статистическое описание DataFrame:\n", + " Open High Low Close Adj Close \\\n", + "count 8035.000000 8035.000000 8035.000000 8035.000000 8035.000000 \n", + "mean 30.048051 30.345221 29.745172 30.052733 26.667480 \n", + "std 33.613031 33.904070 33.312079 33.613521 31.724640 \n", + "min 0.328125 0.347656 0.320313 0.335938 0.260703 \n", + "25% 4.391563 4.531250 4.304844 4.399219 3.413997 \n", + "50% 13.325000 13.485000 13.150000 13.330000 10.352452 \n", + "75% 55.250000 55.715000 54.829999 55.254999 47.461098 \n", + "max 126.080002 126.320000 124.809998 126.059998 118.010414 \n", + "\n", + " Volume above_average_close Close_Next_Day \n", + "count 8.035000e+03 8035.000000 8035.000000 \n", + "mean 1.470584e+07 0.347480 30.062556 \n", + "std 1.340058e+07 0.476199 33.616368 \n", + "min 1.504000e+06 0.000000 0.347656 \n", + "25% 7.818550e+06 0.000000 4.403125 \n", + "50% 1.170240e+07 0.000000 13.330000 \n", + "75% 1.778850e+07 1.000000 55.274999 \n", + "max 5.855088e+08 1.000000 126.059998 \n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "from sklearn import set_config\n", + "\n", + "set_config(transform_output=\"pandas\")\n", + "\n", + "# Загрузка данных о ценах акций Starbucks из CSV файла\n", + "df = pd.read_csv(\".//static//csv//Starbucks Dataset.csv\")\n", + "\n", + "# Опция для настройки генерации случайных чисел (если это нужно для других частей кода)\n", + "random_state = 42\n", + "\n", + "# Вычисление среднего значения поля \"Close\"\n", + "average_close = df['Close'].mean()\n", + "print(f\"Среднее значение поля 'Close': {average_close}\")\n", + "\n", + "# Создание новой колонки, указывающей, выше или ниже среднего значение цена закрытия\n", + "df['above_average_close'] = (df['Close'] > average_close).astype(int)\n", + "\n", + "# Создание целевой переменной для прогнозирования (цена закрытия на следующий день)\n", + "df['Close_Next_Day'] = df['Close'].shift(-1)\n", + "\n", + "# Удаление последней строки, где нет значения для следующего дня\n", + "df.dropna(inplace=True)\n", + "\n", + "# Вывод DataFrame с новой колонкой\n", + "print(df.head())\n", + "\n", + "# Примерный анализ данных\n", + "print(\"Статистическое описание DataFrame:\")\n", + "print(df.describe())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Разделение набора данных на обучающую и тестовые выборки (80/20) для задачи регрессии\n", + "\n", + "Целевой признак -- above_average_close" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'X_train'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
DateOpenHighLowCloseAdj CloseVolumeClose_Next_Day
55522014-07-1439.49000239.49000239.20999939.27999932.493519456200039.445000
34222006-01-2515.34000015.38000015.09500015.18000011.780375727660015.745000
62142017-02-2856.70999957.06000156.54999956.86999948.946602875070057.139999
35012006-05-1818.22500018.25000017.96500017.99000013.9610621336600018.165001
26882003-02-265.6575005.6825005.5200005.5500004.307055167384005.772500
...........................
52262013-03-2728.43000028.47500028.10500028.45500023.144903745700028.475000
53902013-11-1840.50999840.66999840.10500040.27000033.065239831640039.959999
8601995-11-201.3554691.3671881.3281251.3320311.033717309984001.343750
76032022-09-0285.47000185.76999782.55000382.94000279.6838071033680084.519997
72702021-05-10114.570000116.089996114.209999114.300003106.5773095759500113.550003
\n", + "

6428 rows × 8 columns

\n", + "
" + ], + "text/plain": [ + " Date Open High Low Close Adj Close \\\n", + "5552 2014-07-14 39.490002 39.490002 39.209999 39.279999 32.493519 \n", + "3422 2006-01-25 15.340000 15.380000 15.095000 15.180000 11.780375 \n", + "6214 2017-02-28 56.709999 57.060001 56.549999 56.869999 48.946602 \n", + "3501 2006-05-18 18.225000 18.250000 17.965000 17.990000 13.961062 \n", + "2688 2003-02-26 5.657500 5.682500 5.520000 5.550000 4.307055 \n", + "... ... ... ... ... ... ... \n", + "5226 2013-03-27 28.430000 28.475000 28.105000 28.455000 23.144903 \n", + "5390 2013-11-18 40.509998 40.669998 40.105000 40.270000 33.065239 \n", + "860 1995-11-20 1.355469 1.367188 1.328125 1.332031 1.033717 \n", + "7603 2022-09-02 85.470001 85.769997 82.550003 82.940002 79.683807 \n", + "7270 2021-05-10 114.570000 116.089996 114.209999 114.300003 106.577309 \n", + "\n", + " Volume Close_Next_Day \n", + "5552 4562000 39.445000 \n", + "3422 7276600 15.745000 \n", + "6214 8750700 57.139999 \n", + "3501 13366000 18.165001 \n", + "2688 16738400 5.772500 \n", + "... ... ... \n", + "5226 7457000 28.475000 \n", + "5390 8316400 39.959999 \n", + "860 30998400 1.343750 \n", + "7603 10336800 84.519997 \n", + "7270 5759500 113.550003 \n", + "\n", + "[6428 rows x 8 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "'y_train'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
above_average_close
55521
34220
62141
35010
26880
......
52260
53901
8600
76031
72701
\n", + "

6428 rows × 1 columns

\n", + "
" + ], + "text/plain": [ + " above_average_close\n", + "5552 1\n", + "3422 0\n", + "6214 1\n", + "3501 0\n", + "2688 0\n", + "... ...\n", + "5226 0\n", + "5390 1\n", + "860 0\n", + "7603 1\n", + "7270 1\n", + "\n", + "[6428 rows x 1 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "'X_test'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
DateOpenHighLowCloseAdj CloseVolumeClose_Next_Day
66372018-10-3158.98000059.11999958.20999958.27000051.7544561156040058.630001
66322018-10-2458.57000059.27999957.95000158.06000151.5679401218970058.959999
73272021-07-30122.190002122.980003121.099998121.430000113.6760715712300120.370003
7301995-05-170.9375000.9414060.9023440.9101560.706323258112000.912109
15151998-06-253.2265633.3281253.2187503.2851562.549432346992003.382813
...........................
57772015-06-0451.86999952.18000051.57000051.72000143.400497623080052.189999
77192023-02-21105.500000105.949997104.709999104.779999101.7522435438000104.769997
16771999-02-172.9726563.0234382.9062502.9101562.258415177760002.933594
9211996-02-161.0312501.0546881.0156251.0312500.80029778096001.031250
3221993-10-050.8359380.8359380.8046880.8203130.63660091136000.812500
\n", + "

1607 rows × 8 columns

\n", + "
" + ], + "text/plain": [ + " Date Open High Low Close Adj Close \\\n", + "6637 2018-10-31 58.980000 59.119999 58.209999 58.270000 51.754456 \n", + "6632 2018-10-24 58.570000 59.279999 57.950001 58.060001 51.567940 \n", + "7327 2021-07-30 122.190002 122.980003 121.099998 121.430000 113.676071 \n", + "730 1995-05-17 0.937500 0.941406 0.902344 0.910156 0.706323 \n", + "1515 1998-06-25 3.226563 3.328125 3.218750 3.285156 2.549432 \n", + "... ... ... ... ... ... ... \n", + "5777 2015-06-04 51.869999 52.180000 51.570000 51.720001 43.400497 \n", + "7719 2023-02-21 105.500000 105.949997 104.709999 104.779999 101.752243 \n", + "1677 1999-02-17 2.972656 3.023438 2.906250 2.910156 2.258415 \n", + "921 1996-02-16 1.031250 1.054688 1.015625 1.031250 0.800297 \n", + "322 1993-10-05 0.835938 0.835938 0.804688 0.820313 0.636600 \n", + "\n", + " Volume Close_Next_Day \n", + "6637 11560400 58.630001 \n", + "6632 12189700 58.959999 \n", + "7327 5712300 120.370003 \n", + "730 25811200 0.912109 \n", + "1515 34699200 3.382813 \n", + "... ... ... \n", + "5777 6230800 52.189999 \n", + "7719 5438000 104.769997 \n", + "1677 17776000 2.933594 \n", + "921 7809600 1.031250 \n", + "322 9113600 0.812500 \n", + "\n", + "[1607 rows x 8 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "'y_test'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
above_average_close
66371
66321
73271
7300
15150
......
57771
77191
16770
9210
3220
\n", + "

1607 rows × 1 columns

\n", + "
" + ], + "text/plain": [ + " above_average_close\n", + "6637 1\n", + "6632 1\n", + "7327 1\n", + "730 0\n", + "1515 0\n", + "... ...\n", + "5777 1\n", + "7719 1\n", + "1677 0\n", + "921 0\n", + "322 0\n", + "\n", + "[1607 rows x 1 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from typing import Tuple\n", + "import pandas as pd\n", + "from pandas import DataFrame\n", + "from sklearn.model_selection import train_test_split\n", + "\n", + "def split_into_train_test(\n", + " df_input: DataFrame,\n", + " target_colname: str = \"above_average_close\",\n", + " frac_train: float = 0.8,\n", + " random_state: int = None,\n", + ") -> Tuple[DataFrame, DataFrame, DataFrame, DataFrame]:\n", + " \n", + " if not (0 < frac_train < 1):\n", + " raise ValueError(\"Fraction must be between 0 and 1.\")\n", + " \n", + " # Проверка наличия целевого признака\n", + " if target_colname not in df_input.columns:\n", + " raise ValueError(f\"{target_colname} is not a column in the DataFrame.\")\n", + " \n", + " # Разделяем данные на признаки и целевую переменную\n", + " X = df_input.drop(columns=[target_colname]) # Признаки\n", + " y = df_input[[target_colname]] # Целевая переменная\n", + "\n", + " # Разделяем данные на обучающую и тестовую выборки\n", + " X_train, X_test, y_train, y_test = train_test_split(\n", + " X, y,\n", + " test_size=(1.0 - frac_train),\n", + " random_state=random_state\n", + " )\n", + " \n", + " return X_train, X_test, y_train, y_test\n", + "\n", + "# Применение функции для разделения данных\n", + "X_train, X_test, y_train, y_test = split_into_train_test(\n", + " df, \n", + " target_colname=\"above_average_close\", \n", + " frac_train=0.8, \n", + " random_state=42 # Убедитесь, что вы задали нужное значение random_state\n", + ")\n", + "\n", + "# Для отображения результатов\n", + "display(\"X_train\", X_train)\n", + "display(\"y_train\", y_train)\n", + "\n", + "display(\"X_test\", X_test)\n", + "display(\"y_test\", y_test)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Формирование конвейера для решения задачи регрессии" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from sklearn.base import BaseEstimator, TransformerMixin\n", + "from sklearn.compose import ColumnTransformer\n", + "from sklearn.preprocessing import StandardScaler\n", + "from sklearn.impute import SimpleImputer\n", + "from sklearn.pipeline import Pipeline\n", + "from sklearn.preprocessing import OneHotEncoder\n", + "from sklearn.ensemble import RandomForestRegressor # Пример регрессионной модели\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.pipeline import make_pipeline\n", + "\n", + "class StarbucksFeatures(BaseEstimator, TransformerMixin):\n", + " def __init__(self):\n", + " pass\n", + " \n", + " def fit(self, X, y=None):\n", + " return self\n", + "\n", + " def transform(self, X, y=None):\n", + " X[\"Length_to_Width_Ratio\"] = X[\"x\"] / X[\"y\"]\n", + " return X\n", + "\n", + " def get_feature_names_out(self, features_in):\n", + " return np.append(features_in, [\"Length_to_Width_Ratio\"], axis=0)\n", + "\n", + "# Указываем столбцы, которые нужно удалить и обрабатывать\n", + "columns_to_drop = [\"Date\"]\n", + "num_columns = [\"Close\", \"Open\", \"Adj Close\", \"High\", \"Low\", \"Volume\"]\n", + "cat_columns = [] \n", + "\n", + "# Определяем предобработку для численных данных\n", + "num_imputer = SimpleImputer(strategy=\"median\")\n", + "num_scaler = StandardScaler()\n", + "preprocessing_num = Pipeline(\n", + " [\n", + " (\"imputer\", num_imputer),\n", + " (\"scaler\", num_scaler),\n", + " ]\n", + ")\n", + "\n", + "# Определяем предобработку для категориальных данных\n", + "cat_imputer = SimpleImputer(strategy=\"constant\", fill_value=\"unknown\")\n", + "cat_encoder = OneHotEncoder(handle_unknown=\"ignore\", sparse_output=False, drop=\"first\")\n", + "preprocessing_cat = Pipeline(\n", + " [\n", + " (\"imputer\", cat_imputer),\n", + " (\"encoder\", cat_encoder),\n", + " ]\n", + ")\n", + "\n", + "# Подготовка признаков с использованием ColumnTransformer\n", + "features_preprocessing = ColumnTransformer(\n", + " verbose_feature_names_out=False,\n", + " transformers=[\n", + " (\"preprocessing_num\", preprocessing_num, num_columns),\n", + " (\"preprocessing_cat\", preprocessing_cat, cat_columns),\n", + " ],\n", + " remainder=\"passthrough\"\n", + ")\n", + "\n", + "# Удаление нежелательных столбцов\n", + "drop_columns = ColumnTransformer(\n", + " verbose_feature_names_out=False,\n", + " transformers=[\n", + " (\"drop_columns\", \"drop\", columns_to_drop),\n", + " ],\n", + " remainder=\"passthrough\",\n", + ")\n", + "\n", + "# Постобработка признаков\n", + "features_postprocessing = ColumnTransformer(\n", + " verbose_feature_names_out=False,\n", + " transformers=[\n", + " (\"preprocessing_cat\", preprocessing_cat, [\"Cabin_type\"]), \n", + " ],\n", + " remainder=\"passthrough\",\n", + ")\n", + "\n", + "# Создание окончательного конвейера\n", + "pipeline = Pipeline(\n", + " [\n", + " (\"features_preprocessing\", features_preprocessing),\n", + " (\"drop_columns\", drop_columns),\n", + " (\"model\", RandomForestRegressor()) # Выбор модели для обучения\n", + " ]\n", + ")\n", + "\n", + "# Использование конвейера\n", + "def train_pipeline(X, y):\n", + " pipeline.fit(X, y)\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Формирование набора моделей для регрессии" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Random Forest: Mean Score = 0.9746978079010529, Standard Deviation = 0.012793762025792637\n", + "Linear Regression: Mean Score = 0.9868838982543027, Standard Deviation = 0.0041016418339485\n", + "Gradient Boosting: Mean Score = 0.9790461912830413, Standard Deviation = 0.008537795226791314\n", + "Support Vector Regression: Mean Score = -0.10833533729231568, Standard Deviation = 0.29324311707552003\n" + ] + } + ], + "source": [ + "from sklearn.linear_model import LinearRegression\n", + "from sklearn.ensemble import GradientBoostingRegressor\n", + "from sklearn.svm import SVR\n", + "from sklearn.model_selection import cross_val_score\n", + "\n", + "def train_multiple_models(X, y, models):\n", + " results = {}\n", + " for model_name, model in models.items():\n", + " # Создаем конвейер для каждой модели\n", + " model_pipeline = Pipeline(\n", + " [\n", + " (\"features_preprocessing\", features_preprocessing),\n", + " (\"drop_columns\", drop_columns),\n", + " (\"model\", model) # Используем текущую модель\n", + " ]\n", + " )\n", + " \n", + " # Обучаем модель и вычисляем кросс-валидацию\n", + " scores = cross_val_score(model_pipeline, X, y, cv=5) # 5-кратная кросс-валидация\n", + " results[model_name] = {\n", + " \"mean_score\": scores.mean(),\n", + " \"std_dev\": scores.std()\n", + " }\n", + " \n", + " return results\n", + "\n", + "models = {\n", + " \"Random Forest\": RandomForestRegressor(),\n", + " \"Linear Regression\": LinearRegression(),\n", + " \"Gradient Boosting\": GradientBoostingRegressor(),\n", + " \"Support Vector Regression\": SVR()\n", + "}\n", + "\n", + "results = train_multiple_models(X_train, y_train, models)\n", + "\n", + "# Вывод результатов\n", + "for model_name, scores in results.items():\n", + " print(f\"{model_name}: Mean Score = {scores['mean_score']}, Standard Deviation = {scores['std_dev']}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Лидирующие модели: Линейная регрессия проявила наилучшие результаты, за ней следует градиентный бустинг и Random Forest. Они продемонстрировали высокую эффективность в предсказании закрытия акций.\n", + "Проблемы SVR: Резкое отличие в результатах SVR выявляет необходимость более тщательной настройки или выбора других подходов к решению задачи, поскольку текущие параметры не обеспечили адекватного уровня прогноза." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Обучение моделей на обучающем наборе данных и оценка на тестовом для регрессии" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model: logistic\n", + "MSE (train): 0.0\n", + "MSE (test): 0.0\n", + "MAE (train): 0.0\n", + "MAE (test): 0.0\n", + "R2 (train): 1.0\n", + "R2 (test): 1.0\n", + "STD (train): 0.0\n", + "STD (test): 0.0\n", + "----------------------------------------\n", + "Model: ridge\n", + "MSE (train): 0.0\n", + "MSE (test): 0.0\n", + "MAE (train): 0.0\n", + "MAE (test): 0.0\n", + "R2 (train): 1.0\n", + "R2 (test): 1.0\n", + "STD (train): 0.0\n", + "STD (test): 0.0\n", + "----------------------------------------\n", + "Model: decision_tree\n", + "MSE (train): 0.0\n", + "MSE (test): 0.0\n", + "MAE (train): 0.0\n", + "MAE (test): 0.0\n", + "R2 (train): 1.0\n", + "R2 (test): 1.0\n", + "STD (train): 0.0\n", + "STD (test): 0.0\n", + "----------------------------------------\n", + "Model: knn\n", + "MSE (train): 0.0\n", + "MSE (test): 0.0\n", + "MAE (train): 0.0\n", + "MAE (test): 0.0\n", + "R2 (train): 1.0\n", + "R2 (test): 1.0\n", + "STD (train): 0.0\n", + "STD (test): 0.0\n", + "----------------------------------------\n", + "Model: naive_bayes\n", + "MSE (train): 0.0\n", + "MSE (test): 0.0\n", + "MAE (train): 0.0\n", + "MAE (test): 0.0\n", + "R2 (train): 1.0\n", + "R2 (test): 1.0\n", + "STD (train): 0.0\n", + "STD (test): 0.0\n", + "----------------------------------------\n", + "Model: gradient_boosting\n", + "MSE (train): 0.0\n", + "MSE (test): 0.0\n", + "MAE (train): 0.0\n", + "MAE (test): 0.0\n", + "R2 (train): 1.0\n", + "R2 (test): 1.0\n", + "STD (train): 0.0\n", + "STD (test): 0.0\n", + "----------------------------------------\n", + "Model: random_forest\n", + "MSE (train): 0.0\n", + "MSE (test): 0.0\n", + "MAE (train): 0.0\n", + "MAE (test): 0.0\n", + "R2 (train): 1.0\n", + "R2 (test): 1.0\n", + "STD (train): 0.0\n", + "STD (test): 0.0\n", + "----------------------------------------\n", + "Model: mlp\n", + "MSE (train): 0.0020224019912881146\n", + "MSE (test): 0.0018656716417910447\n", + "MAE (train): 0.0020224019912881146\n", + "MAE (test): 0.0018656716417910447\n", + "R2 (train): 0.9911106856018297\n", + "R2 (test): 0.9918005898000289\n", + "STD (train): 0.044925626111093304\n", + "STD (test): 0.04315311009783723\n", + "----------------------------------------\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "from sklearn import metrics\n", + "from sklearn.pipeline import Pipeline\n", + "\n", + "# Проверка наличия необходимых переменных\n", + "if 'class_models' not in locals():\n", + " raise ValueError(\"class_models is not defined\")\n", + "if 'X_train' not in locals() or 'X_test' not in locals() or 'y_train' not in locals() or 'y_test' not in locals():\n", + " raise ValueError(\"Train/test data is not defined\")\n", + "\n", + "\n", + "y_train = np.ravel(y_train) \n", + "y_test = np.ravel(y_test) \n", + "\n", + "# Инициализация списка для хранения результатов\n", + "results = []\n", + "\n", + "# Проход по моделям и оценка их качества\n", + "for model_name in class_models.keys():\n", + " print(f\"Model: {model_name}\")\n", + " \n", + " # Извлечение модели из словаря\n", + " model = class_models[model_name][\"model\"]\n", + " \n", + " # Создание пайплайна\n", + " model_pipeline = Pipeline([(\"pipeline\", pipeline_end), (\"model\", model)])\n", + " \n", + " # Обучение модели\n", + " model_pipeline.fit(X_train, y_train)\n", + "\n", + " # Предсказание для обучающей и тестовой выборки\n", + " y_train_predict = model_pipeline.predict(X_train)\n", + " y_test_predict = model_pipeline.predict(X_test)\n", + "\n", + " # Сохранение пайплайна и предсказаний\n", + " class_models[model_name][\"pipeline\"] = model_pipeline\n", + " class_models[model_name][\"preds\"] = y_test_predict\n", + "\n", + " # Вычисление метрик для регрессии\n", + " class_models[model_name][\"MSE_train\"] = metrics.mean_squared_error(y_train, y_train_predict)\n", + " class_models[model_name][\"MSE_test\"] = metrics.mean_squared_error(y_test, y_test_predict)\n", + " class_models[model_name][\"MAE_train\"] = metrics.mean_absolute_error(y_train, y_train_predict)\n", + " class_models[model_name][\"MAE_test\"] = metrics.mean_absolute_error(y_test, y_test_predict)\n", + " class_models[model_name][\"R2_train\"] = metrics.r2_score(y_train, y_train_predict)\n", + " class_models[model_name][\"R2_test\"] = metrics.r2_score(y_test, y_test_predict)\n", + "\n", + " # Дополнительные метрики\n", + " class_models[model_name][\"STD_train\"] = np.std(y_train - y_train_predict)\n", + " class_models[model_name][\"STD_test\"] = np.std(y_test - y_test_predict)\n", + "\n", + " # Вывод результатов для текущей модели\n", + " print(f\"MSE (train): {class_models[model_name]['MSE_train']}\")\n", + " print(f\"MSE (test): {class_models[model_name]['MSE_test']}\")\n", + " print(f\"MAE (train): {class_models[model_name]['MAE_train']}\")\n", + " print(f\"MAE (test): {class_models[model_name]['MAE_test']}\")\n", + " print(f\"R2 (train): {class_models[model_name]['R2_train']}\")\n", + " print(f\"R2 (test): {class_models[model_name]['R2_test']}\")\n", + " print(f\"STD (train): {class_models[model_name]['STD_train']}\")\n", + " print(f\"STD (test): {class_models[model_name]['STD_test']}\")\n", + " print(\"-\" * 40) # Разделитель для разных моделей" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Пример использования обученной модели (конвейера регрессии) для предсказания" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model: RandomForest\n", + "MSE (train): 0.0001403391412570006\n", + "MSE (test): 0.0006576851275668948\n", + "MAE (train): 0.0005491599253266957\n", + "MAE (test): 0.0011761045426260113\n", + "R2 (train): 0.9993811021756365\n", + "R2 (test): 0.9971008099591692\n", + "----------------------------------------\n", + "Прогноз: Цена закроется ниже среднего значения завтрашнего дня.\n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "from sklearn import metrics\n", + "from sklearn.pipeline import Pipeline\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.ensemble import RandomForestRegressor # пример модели\n", + "from sklearn.preprocessing import StandardScaler\n", + "\n", + "# 1. Загрузка данных\n", + "data = pd.read_csv(\".//static//csv//Starbucks Dataset.csv\") \n", + "data['Date'] = pd.to_datetime(data['Date'])\n", + "data.set_index('Date', inplace=True)\n", + "\n", + "# 2. Подготовка данных для прогноза\n", + "data['Close_shifted'] = data['Close'].shift(-1) # Смещение на 1 день для предсказания\n", + "data.dropna(inplace=True) # Удаление NaN, возникших из-за смещения\n", + "\n", + "# Вычисляем среднее значение закрытия\n", + "average_close = data['Close'].mean()\n", + "data['above_average_close'] = (data['Close_shifted'] > average_close).astype(int) # 1, если выше среднего, иначе 0\n", + "\n", + "# Предикторы и целевая переменная\n", + "X = data[['Open', 'High', 'Low', 'Close', 'Volume']]\n", + "y = data['above_average_close']\n", + "\n", + "\n", + "# 3. Инициализация модели и пайплайна\n", + "class_models = {\n", + " \"RandomForest\": {\n", + " \"model\": RandomForestRegressor(n_estimators=100, random_state=42),\n", + " }\n", + "}\n", + "\n", + "pipeline_end = StandardScaler() \n", + "results = []\n", + "\n", + "# 4. Обучение модели и оценка\n", + "for model_name in class_models.keys():\n", + " print(f\"Model: {model_name}\")\n", + " \n", + " model = class_models[model_name][\"model\"]\n", + " model_pipeline = Pipeline([(\"scaler\", pipeline_end), (\"model\", model)])\n", + " \n", + " # Обучение модели\n", + " model_pipeline.fit(X_train, y_train)\n", + "\n", + " # Предсказание\n", + " y_train_predict = model_pipeline.predict(X_train)\n", + " y_test_predict = model_pipeline.predict(X_test)\n", + "\n", + " # Сохранение результатов\n", + " class_models[model_name][\"preds\"] = y_test_predict\n", + "\n", + " # Вычисление метрик\n", + " class_models[model_name][\"MSE_train\"] = metrics.mean_squared_error(y_train, y_train_predict)\n", + " class_models[model_name][\"MSE_test\"] = metrics.mean_squared_error(y_test, y_test_predict)\n", + " class_models[model_name][\"MAE_train\"] = metrics.mean_absolute_error(y_train, y_train_predict)\n", + " class_models[model_name][\"MAE_test\"] = metrics.mean_absolute_error(y_test, y_test_predict)\n", + " class_models[model_name][\"R2_train\"] = metrics.r2_score(y_train, y_train_predict)\n", + " class_models[model_name][\"R2_test\"] = metrics.r2_score(y_test, y_test_predict)\n", + "\n", + " # Вывод результатов\n", + " print(f\"MSE (train): {class_models[model_name]['MSE_train']}\")\n", + " print(f\"MSE (test): {class_models[model_name]['MSE_test']}\")\n", + " print(f\"MAE (train): {class_models[model_name]['MAE_train']}\")\n", + " print(f\"MAE (test): {class_models[model_name]['MAE_test']}\")\n", + " print(f\"R2 (train): {class_models[model_name]['R2_train']}\")\n", + " print(f\"R2 (test): {class_models[model_name]['R2_test']}\")\n", + " print(\"-\" * 40)\n", + "\n", + "# Прогнозирование выше среднего для следующего дня\n", + "latest_data = X_test.iloc[-1:].copy()\n", + "predicted_above_average = model_pipeline.predict(latest_data)\n", + "predicted_above_average = 1 if predicted_above_average[0] > 0.5 else 0 # Преобразуем в бинарный выход\n", + "\n", + "if predicted_above_average == 1:\n", + " print(\"Прогноз: Цена закроется выше среднего значения завтрашнего дня.\")\n", + "else:\n", + " print(\"Прогноз: Цена закроется ниже среднего значения завтрашнего дня.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Подбор гиперпараметров методом поиска по сетке" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Fitting 5 folds for each of 36 candidates, totalling 180 fits\n", + "Лучшие параметры: {'max_depth': 10, 'min_samples_split': 10, 'n_estimators': 200}\n", + "Лучший результат (MSE): 0.6848872116583115\n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "from sklearn import metrics\n", + "from sklearn.pipeline import Pipeline\n", + "from sklearn.model_selection import train_test_split, GridSearchCV\n", + "from sklearn.ensemble import RandomForestRegressor # Используем регрессор\n", + "from sklearn.preprocessing import StandardScaler\n", + "\n", + "\n", + "# 1. Подготовка данных для прогноза\n", + "data['above_average_close'] = data['Close'].shift(-1) # Смещение на 1 день для предсказания\n", + "data.dropna(inplace=True) # Удаление NaN, возникших из-за смещения\n", + "\n", + "# Предикторы и целевая переменная\n", + "X = data[['Open', 'High', 'Low', 'Close', 'Volume']]\n", + "y = data['above_average_close'] # Целевая переменная для регрессии\n", + "\n", + "# Делим данные на обучающую и тестовую выборки\n", + "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n", + "\n", + "# 2. Создание и настройка модели случайного леса\n", + "model = RandomForestRegressor() # Изменяем на регрессор\n", + "\n", + "# Установка параметров для поиска по сетке\n", + "param_grid = {\n", + " 'n_estimators': [50, 100, 200], # Количество деревьев\n", + " 'max_depth': [None, 10, 20, 30], # Максимальная глубина дерева\n", + " 'min_samples_split': [2, 5, 10] # Минимальное количество образцов для разбиения узла\n", + "}\n", + "\n", + "# 3. Подбор гиперпараметров с помощью Grid Search\n", + "grid_search = GridSearchCV(estimator=model, param_grid=param_grid,\n", + " scoring='neg_mean_squared_error', cv=5, n_jobs=-1, verbose=2)\n", + "\n", + "# Обучение модели на тренировочных данных\n", + "grid_search.fit(X_train, y_train)\n", + "\n", + "# 4. Результаты подбора гиперпараметров\n", + "print(\"Лучшие параметры:\", grid_search.best_params_)\n", + "print(\"Лучший результат (MSE):\", -grid_search.best_score_) # Меняем знак, так как берем отрицательное значение среднеквадратичной ошибки\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Обучение модели с новыми гиперпараметрами и сравнение новых и старых данных" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Fitting 5 folds for each of 36 candidates, totalling 180 fits\n", + "Старые параметры: {'max_depth': 10, 'min_samples_split': 5, 'n_estimators': 200}\n", + "Лучший результат (MSE) на старых параметрах: 0.688662233031193\n", + "\n", + "Новые параметры: {'max_depth': 10, 'min_samples_split': 10, 'n_estimators': 200}\n", + "Лучший результат (MSE) на новых параметрах: 0.6794717145705662\n", + "Среднеквадратическая ошибка (MSE) на тестовых данных: 0.5876131198171756\n", + "Корень среднеквадратичной ошибки (RMSE) на тестовых данных: 0.7665592735184772\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA04AAAHWCAYAAABACtmGAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAABqPUlEQVR4nO3deVwWVf//8fcFyr4qgmvgvu+m4pLeuWCZuZRr5pqVmkuoqWXg8jU011LTtFKzzKXMSkvrRs19X3PLfQd3UFRQmN8f/rhuLwEvLgRBfD0fj+shc+bMmc+MA8OHc+aMyTAMQwAAAACAFNlldgAAAAAAkNWROAEAAACAFSROAAAAAGAFiRMAAAAAWEHiBAAAAABWkDgBAAAAgBUkTgAAAABgBYkTAAAAAFhB4gQAAAAAVpA4AQAAAIAVJE7AU+rYsWN65513VKRIETk5OcnDw0O1a9fWZ599ptu3b2d2eM+MNWvWyGQyyWQy6bvvvku2Tu3atWUymVSuXDmL8ri4OH322WeqXLmyPDw85OXlpbJly+rtt9/WoUOHzPXmzJlj3kdyn82bN2foMQIAAClHZgcAwHbLly9X69at5ejoqE6dOqlcuXKKi4vT+vXrNWjQIO3fv18zZ87M7DCfKU5OTpo/f746duxoUX7y5Elt3LhRTk5OSbZ57bXX9Mcff6h9+/bq0aOH7t69q0OHDmnZsmWqVauWSpUqZVF/5MiRKly4cJJ2ihUrlr4HAwAAkiBxAp4yJ06cULt27eTv769Vq1YpX7585nW9e/fW0aNHtXz58kyM8Nn08ssv69dff9Xly5fl4+NjLp8/f778/PxUvHhxXbt2zVy+bds2LVu2TKNHj9aHH35o0dbUqVN1/fr1JPt46aWXVK1atQw7BgAAkDKG6gFPmU8//VQ3b97U119/bZE0JSpWrJj69etnXjaZTHrvvff0/fffq2TJknJyclLVqlW1du1ai+1OnTqlXr16qWTJknJ2dlbu3LnVunVrnTx50qLew8PGXFxcVL58eX311VcW9bp06SI3N7ck8f34448ymUxas2aNRfmWLVvUpEkTeXp6ysXFRfXq1dOGDRss6gwfPlwmk0mXL1+2KN++fbtMJpPmzJljsf+AgACLemfOnJGzs7NMJlOS4/rjjz9Ut25dubq6yt3dXU2bNtX+/fuTxJ+S5s2by9HRUYsXL7Yonz9/vtq0aSN7e3uL8mPHjkm6P4zvYfb29sqdO3eq950aJ0+eTHGo38PnQpLq16+fbN0Hz7EkTZ8+XeXKlZOLi4tFvR9//NFqTOfOnVP37t2VP39+OTo6qnDhwurZs6fi4uKsDk98MJa9e/eqS5cu5mGrefPmVbdu3XTlyhWL/SVeP4cOHVKbNm3k4eGh3Llzq1+/frpz545F3cTvm5Qkxpd47latWiU7OzuFhIRY1Js/f75MJpOmT5/+yHNRv3591a9f36Js27Zt5mO1pn79+kmGgkrS+PHjk/0//uKLL1S2bFk5Ojoqf/786t27d5Jk/eFrwMfHR02bNtU///xjUS8zztWjrosHj/WXX35R06ZNzddY0aJFNWrUKMXHxydps1y5ctqxY4dq1aolZ2dnFS5cWDNmzLCoFxcXp5CQEFWtWlWenp5ydXVV3bp1tXr1aot6D36/LV261GLdnTt35O3tLZPJpPHjx1usO3funLp16yY/Pz85OjqqbNmy+uabb8zrHxwanNJn+PDhkmy73u/du6dRo0apaNGicnR0VEBAgD788EPFxsZa1AsICDDvx87OTnnz5lXbtm11+vTpR/6fAdkFPU7AU+a3335TkSJFVKtWrVRv8/fff2vhwoXq27evHB0d9cUXX6hJkybaunWr+Zetbdu2aePGjWrXrp0KFiyokydPavr06apfv74OHDggFxcXizYnTZokHx8fRUdH65tvvlGPHj0UEBCghg0b2nxMq1at0ksvvaSqVasqNDRUdnZ2mj17tl588UWtW7dO1atXt7nN5ISEhCT5hUGS5s2bp86dOysoKEhjx47VrVu3NH36dNWpU0e7du1KkoAlx8XFRc2bN9cPP/ygnj17SpL27Nmj/fv366uvvtLevXst6vv7+0uSvv/+e9WuXVs5clj/cRwVFZUkaTSZTDYlWe3bt9fLL78sSfr999/1ww8/pFi3VKlS+uijjyRJly9f1vvvv2+xfuHCherVq5fq16+vPn36yNXVVQcPHtQnn3xiNY7z58+revXqun79ut5++22VKlVK586d048//qhbt27phRde0Lx588z1R48eLUnmeCSZvwf++usvHT9+XF27dlXevHnNQ1X379+vzZs3J0k82rRpo4CAAIWFhWnz5s36/PPPde3aNX377bdW407Jiy++qF69eiksLEwtWrRQ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4kR5e3urVq1aatasmYKCglSlSpUk9QYMGKDt27ercuXK+r//+z9NnDhRQUFBNp2PRM7Ozho8eLBF78+DZs+erU6dOmnAgAEqWbKkWrRooW3btum5555L9XGl57kHgJRUqVJFixYt0oIFC1SuXDmFhIRo5MiRFq9YkO7/8StfvnwqV66cdu7cqaVLl5p7QSRp6tSpcnZ2lq+vr7755ht9//33Fr0iiUaNGqWPP/5YYWFhKl26tJo0aaLly5ercOHCaT4GOzs7LViwQDt27FC5cuX0/vvva9y4ccnWHTNmjMaMGaOKFStq/fr1+vXXX+Xj4yPp/miCVq1aqW3btqpRo4auXLli0fv0sJIlS2rIkCHJznLr4uKitWvX6rnnnlOrVq1UunRpde/eXXfu3LGpB6pBgwYqXry4XnjhBbVt21avvvqqhg8fblEnteceSI7JSM8n6IFn1Jw5c9S/f/9s8Xb5gIAA9e/fP8V3XWQ12encA0BWcPLkSRUuXFi7du1K8R14WU2XLl10/fp1LV26NLNDQTZGjxMAAAAAWEHiBAAAAABWMFQPAAAAAKygxwkAAAAArCBxAgAAAAArSJwAAAAAwAoSJwAAAACwgsQJAJDh7t69m9khAADwWEicAADp7ptvvtGLL76o5557Ti4uLnrzzTczOyQAAB5LjswOAACQcbp06aK5c+emuP7atWvy8vJK132+8847WrFihUaPHq1q1aopR44c8vX1Tdd9AADwpJE4AUA216RJE82ePduibOPGjXrttdfSfV/r1q3Tzz//rD179ihfvnzp3j4AAJmFoXoAkM05Ojoqb968Fp9cuXIlqffTTz+pbNmycnR0VEBAgCZMmJCkzpw5c2QymSw+lSpVMq9ftmyZypcvr7feekteXl7KlSuXunTpoqioKHOdhIQEjRw5UgULFpSjo6MqVaqkFStWmNefPHlSJpNJCxYsUK1ateTk5KRy5crp77//fuRxBgQEJInNZDKpRYsW5jorVqxQnTp15OXlpdy5c+uVV17RsWPHbNp3fHy8unfvrsKFC8vZ2VklS5bUZ599ZhFLly5dZDKZNHHiRIvyli1bymQyac6cOeayM2fOqE2bNubz1bx5c508eVKSNHz48GSPyWQyqX79+uZ9tWjRQiNGjFCePHnk4eGhd999V3FxceZ9xMbGqm/fvvL19ZWTk5Pq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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "from sklearn import metrics\n", + "from sklearn.ensemble import RandomForestRegressor\n", + "from sklearn.model_selection import train_test_split, GridSearchCV\n", + "import matplotlib.pyplot as plt\n", + "\n", + "\n", + "# 1. Настройка параметров для старых значений\n", + "old_param_grid = {\n", + " 'n_estimators': [50, 100, 200], # Количество деревьев\n", + " 'max_depth': [None, 10, 20, 30], # Максимальная глубина дерева\n", + " 'min_samples_split': [2, 5, 10] # Минимальное количество образцов для разбиения узла\n", + "}\n", + "\n", + "# Подбор гиперпараметров с помощью Grid Search для старых параметров\n", + "old_grid_search = GridSearchCV(estimator=RandomForestRegressor(), \n", + " param_grid=old_param_grid,\n", + " scoring='neg_mean_squared_error', cv=5, n_jobs=-1, verbose=2)\n", + "\n", + "# Обучение модели на тренировочных данных\n", + "old_grid_search.fit(X_train, y_train)\n", + "\n", + "# 2. Результаты подбора для старых параметров\n", + "old_best_params = old_grid_search.best_params_\n", + "old_best_mse = -old_grid_search.best_score_ # Меняем знак, так как берем отрицательное значение MSE\n", + "\n", + "# 3. Настройка параметров для новых значений\n", + "new_param_grid = {\n", + " 'n_estimators': [200],\n", + " 'max_depth': [10],\n", + " 'min_samples_split': [10]\n", + "}\n", + "\n", + "# Подбор гиперпараметров с помощью Grid Search для новых параметров\n", + "new_grid_search = GridSearchCV(estimator=RandomForestRegressor(), \n", + " param_grid=new_param_grid,\n", + " scoring='neg_mean_squared_error', cv=2)\n", + "\n", + "# Обучение модели на тренировочных данных\n", + "new_grid_search.fit(X_train, y_train)\n", + "\n", + "# 4. Результаты подбора для новых параметров\n", + "new_best_params = new_grid_search.best_params_\n", + "new_best_mse = -new_grid_search.best_score_ # Меняем знак, так как берем отрицательное значение MSE\n", + "\n", + "# 5. Обучение модели с лучшими параметрами для новых значений\n", + "model_best = RandomForestRegressor(**new_best_params)\n", + "model_best.fit(X_train, y_train)\n", + "\n", + "# Прогнозирование на тестовой выборке\n", + "y_pred = model_best.predict(X_test)\n", + "\n", + "# Оценка производительности модели\n", + "mse = metrics.mean_squared_error(y_test, y_pred)\n", + "rmse = np.sqrt(mse)\n", + "\n", + "# Вывод результатов\n", + "print(\"Старые параметры:\", old_best_params)\n", + "print(\"Лучший результат (MSE) на старых параметрах:\", old_best_mse)\n", + "print(\"\\nНовые параметры:\", new_best_params)\n", + "print(\"Лучший результат (MSE) на новых параметрах:\", new_best_mse)\n", + "print(\"Среднеквадратическая ошибка (MSE) на тестовых данных:\", mse)\n", + "print(\"Корень среднеквадратичной ошибки (RMSE) на тестовых данных:\", rmse)\n", + "\n", + "# Визуализация ошибок\n", + "plt.figure(figsize=(10, 5))\n", + "plt.bar(['Старые параметры', 'Новые параметры'], [old_best_mse, new_best_mse], color=['blue', 'orange'])\n", + "plt.xlabel('Подбор параметров')\n", + "plt.ylabel('Среднеквадратическая ошибка (MSE)')\n", + "plt.title('Сравнение MSE для старых и новых параметров')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Сравнив результаты с использованием старых и новых параметров, наблюдается, что новые параметры модели позволили добиться меньшей среднеквадратической ошибки, что указывает на более эффективное предсказание по сравнению со старыми настройками. Значение RMSE на тестовых данных также подтверждает улучшение качества модели, так как оно стало меньше и указывает на более точные прогнозы по сравнению с предыдущими настройками." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aimenv", + "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 +} diff --git a/lab_4/requirements.txt b/lab_4/requirements.txt new file mode 100644 index 0000000..b1f5df9 --- /dev/null +++ b/lab_4/requirements.txt @@ -0,0 +1,61 @@ +asttokens==2.4.1 +attrs==24.2.0 +cloudpickle==3.1.0 +colorama==0.4.6 +comm==0.2.2 +contourpy==1.3.0 +cycler==0.12.1 +debugpy==1.8.5 +decorator==5.1.1 +executing==2.1.0 +fastjsonschema==2.20.0 +featuretools==1.31.0 +fonttools==4.53.1 +holidays==0.58 +imbalanced-learn==0.12.4 +importlib_resources==6.4.5 +ipykernel==6.29.5 +ipython==8.27.0 +jedi==0.19.1 +joblib==1.4.2 +jsonschema==4.23.0 +jsonschema-specifications==2023.12.1 +jupyter_client==8.6.3 +jupyter_core==5.7.2 +kiwisolver==1.4.7 +matplotlib==3.9.2 +matplotlib-inline==0.1.7 +nbformat==5.10.4 +nest-asyncio==1.6.0 +numpy==2.1.1 +packaging==24.1 +pandas==2.2.2 +parso==0.8.4 +pillow==10.4.0 +platformdirs==4.3.6 +plotly==5.24.1 +prompt_toolkit==3.0.47 +psutil==6.0.0 +pure_eval==0.2.3 +Pygments==2.18.0 +pyparsing==3.1.4 +python-dateutil==2.9.0.post0 +pytz==2024.2 +pywin32==306 +pyzmq==26.2.0 +referencing==0.35.1 +rpds-py==0.20.0 +scikit-learn==1.5.2 +scipy==1.14.1 +seaborn==0.13.2 +setuptools==75.2.0 +six==1.16.0 +stack-data==0.6.3 +tenacity==9.0.0 +threadpoolctl==3.5.0 +tornado==6.4.1 +tqdm==4.66.5 +traitlets==5.14.3 +tzdata==2024.1 +wcwidth==0.2.13 +woodwork==0.31.0