AIM-PIbd-31-Masenkin-M-S/lab_3/lab3.ipynb
2024-11-02 06:10:19 +04:00

448 KiB
Raw Permalink Blame History

Датасет: Tesla Insider Trading.

Описание датасета:

Датасет представляет собой выборку операций с ценными бумагами компании Tesla, совершённых инсайдерами, и является частью более крупного проекта "Insider Trading S&P500 Inside Info". Данные охватывают транзакции с участием крупных акционеров и должностных лиц компании, включая такие операции, как покупка, продажа и опционы, начиная с 10 ноября 2021 года и до 27 июля 2022 года.


Анализ сведений:

Проблемная область: Проблемная область данного датасета касается анализа инсайдерских сделок в публичных компаниях, а также их влияния на ценообразование акций. Инсайдерские транзакции, совершаемые людьми с доступом к непубличной информации (такими как руководители, крупные акционеры или члены совета директоров), могут быть индикаторами будущих изменений стоимости акций. Исследование таких транзакций помогает понять, как информация внутри компании отражается в действиях ключевых участников, и может выявить паттерны поведения, которые влияют на рынки.

Актуальность: Анализ инсайдерских сделок становится особенно важным в условиях высокой волатильности рынка и неопределенности. Инвесторы, аналитики и компании используют такие данные, чтобы лучше понимать сигналы от крупных акционеров и должностных лиц. Действия инсайдеров, такие как покупки и продажи акций, нередко рассматриваются как индикаторы доверия к компании, что может оказывать значительное влияние на рыночные ожидания и прогнозы.

Объекты наблюдений: Объектами наблюдений в датасете являются инсайдеры компании Tesla — лица, имеющие значительное влияние на управление и информацию компании. Каждый объект характеризуется различными параметрами, включая должность, тип транзакции, количество акций и общую стоимость сделок.

Атрибуты объектов:

  • Insider Trading: ФИО лица, совершившего транзакцию.
  • Relationship: Должность или статус данного лица в компании Tesla.
  • Date: Дата завершения транзакции.
  • Transaction: Тип транзакции.
  • Cost: Цена одной акции на момент совершения транзакции.
  • Shares: Количество акций, участвующих в транзакции.
  • Value ($): Общая стоимость транзакции в долларах США.
  • Shares Total: Общее количество акций, принадлежащих этому лицу после завершения данной транзакции.
  • SEC Form 4: Дата записи транзакции в форме SEC Form 4, обязательной для отчётности о сделках инсайдеров.

Бизнес-цели:

  1. Прогнозирование цен на акции Tesla на основе действий инсайдеров: Одна из ключевых бизнес-целей состоит в создании модели для прогнозирования динамики акций Tesla, используя данные о транзакциях инсайдеров. Поскольку инсайдеры обладают глубоким знанием внутреннего состояния компании, их действия могут предсказывать изменения в стоимости акций. На основе анализа паттернов и частоты инсайдерских покупок и продаж можно разработать предсказательную модель, которая поможет инвесторам и аналитикам принимать более обоснованные решения.
  2. Анализ влияния транзакций инсайдеров на динамику цены акций Tesla для оценки краткосрочных и долгосрочных рисков: Цель исследовать, как действия инсайдеров (особенно крупных акционеров и ключевых лиц) влияют на цену акций Tesla. Выявление корреляций между объёмом, типом и частотой инсайдерских сделок и изменениями цены акций позволит оценить риски и тенденции в динамике акций.

Технические цели:

Для бизнес-цели №1:

  • Сбор и подготовка данных:
    • Очистка данных от пропусков, выбросов и дубликатов, приведение данных к единому формату.
    • Преобразование категориальных переменных (тип транзакции, должность инсайдера и др.) в числовые значения с использованием кодирования (one-hot encoding).
    • Добавление временных характеристик, таких как сезонные факторы и тренды.
    • Разделение данных на обучающую и тестовую выборки для создания и тестирования прогнозной модели.
  • Разработка и обучение модели:
    • Исследование различных алгоритмов прогнозирования, таких как линейная регрессия, деревья решений, градиентный бустинг и рекуррентные нейронные сети (RNN).
    • Проведение кросс-валидации для подбора гиперпараметров и выбора наиболее эффективного алгоритма.
    • Обучение моделей на обучающей выборке и оценка качества на тестовой выборке с помощью метрик (RMSE, MAE и др.) для измерения точности предсказаний.
  • Развертывание и мониторинг модели:
    • Разработка и настройка дашборда для отслеживания прогноза стоимости акций в реальном времени на основе новых инсайдерских данных.
    • Настройка системы уведомлений и мониторинга для оповещения о значимых транзакциях инсайдеров.
    • Регулярное обновление модели и тестирование её точности по мере поступления новых данных.

Для бизнес-цели №2:

  • Сбор и подготовка данных:
    • Очистка данных от пропусков и выбросов, приведение категориальных признаков (типы транзакций, роли инсайдеров и др.) к числовым форматам с помощью методов кодирования.
    • Интеграция с данными по историческим ценам акций Tesla, чтобы включить изменения цен в различные периоды после инсайдерских сделок.
    • Создание дополнительных признаков, например, накопительного объема акций и частоты транзакций для каждого инсайдера, чтобы выявить более глубокие взаимосвязи с ценами.
  • Разработка и обучение модели:
    • Использование методов временных рядов (ARIMA, Prophet) и регрессионных моделей для анализа корреляций между объёмом и типом инсайдерских транзакций и изменением цены акций.
    • Применение методов машинного обучения (случайный лес, градиентный бустинг) для определения ключевых факторов транзакций, наиболее значимых для изменений цен на акции.
    • Оценка моделей на тестовой выборке с использованием метрик (MAE, RMSE и др.) для точного измерения влияния и предсказательной способности.
  • Визуализация и мониторинг рисков:
    • Разработка интерактивного дашборда, отображающего связь между действиями инсайдеров и изменениями в цене акций Tesla с возможностью фильтрации по типу транзакций и роли инсайдера.
    • Настройка автоматических уведомлений о потенциальных рисках — например, при значительном росте объемов продажи акций инсайдерами и заметном снижении цены акций в краткосрочной перспективе.
    • Регулярное обновление и мониторинг модели для повышения точности прогнозов и своевременного обнаружения новых трендов.

Входные данные: Инсайдер, должность, дата, тип транзакции, количество акций, общая стоимость сделки, общее количество акций у инсайдера, дата регистрации транзакции.

Целевой признак: Cost цена одной акции на момент транзакции.


Выгрузка данных из файла в DataFrame:

In [639]:
from typing import Any
from math import ceil
import time

import pandas as pd
from pandas import DataFrame, Series
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler, StandardScaler
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.linear_model import LinearRegression
from sklearn.metrics import root_mean_squared_error, r2_score, mean_absolute_error
from sklearn.ensemble import RandomForestRegressor
from imblearn.over_sampling import SMOTE
import featuretools as ft
from featuretools.entityset.entityset import EntitySet
import matplotlib.pyplot as plt


df: DataFrame = pd.read_csv('..//static//csv//TSLA.csv')

Краткая информация о DataFrame:

In [640]:
# Краткая информация о DataFrame
df.info()

# Статистическое описание числовых столбцов
df.describe().transpose()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 156 entries, 0 to 155
Data columns (total 9 columns):
 #   Column           Non-Null Count  Dtype  
---  ------           --------------  -----  
 0   Insider Trading  156 non-null    object 
 1   Relationship     156 non-null    object 
 2   Date             156 non-null    object 
 3   Transaction      156 non-null    object 
 4   Cost             156 non-null    float64
 5   Shares           156 non-null    object 
 6   Value ($)        156 non-null    object 
 7   Shares Total     156 non-null    object 
 8   SEC Form 4       156 non-null    object 
dtypes: float64(1), object(8)
memory usage: 11.1+ KB
Out[640]:
count mean std min 25% 50% 75% max
Cost 156.0 478.785641 448.922903 0.0 50.5225 240.225 934.1075 1171.04

Конвертация данных:

In [641]:
# Преобразование типов данных
df['Insider Trading'] = df['Insider Trading'].astype('category')                             # Преобразование в категорию
df['Relationship'] = df['Relationship'].astype('category')                                   # Преобразование в категорию
df['Transaction'] = df['Transaction'].astype('category')                                     # Преобразование в категорию
df['Cost'] = pd.to_numeric(df['Cost'], errors='coerce')                                      # Преобразование в float
df['Shares'] = pd.to_numeric(df['Shares'].str.replace(',', ''), errors='coerce')             # Преобразование в float с удалением запятых
df['Value ($)'] = pd.to_numeric(df['Value ($)'].str.replace(',', ''), errors='coerce')       # Преобразование в float с удалением запятых
df['Shares Total'] = pd.to_numeric(df['Shares Total'].str.replace(',', ''), errors='coerce') # Преобразование в float с удалением запятых

print('Выборка данных:')
df.head(10)
Выборка данных:
Out[641]:
Insider Trading Relationship Date Transaction Cost Shares Value ($) Shares Total SEC Form 4
0 Kirkhorn Zachary Chief Financial Officer 2022-03-06 Sale 196.72 10455 2056775 203073 Mar 07 07:58 PM
1 Taneja Vaibhav Chief Accounting Officer 2022-03-06 Sale 195.79 2466 482718 100458 Mar 07 07:57 PM
2 Baglino Andrew D SVP Powertrain and Energy Eng. 2022-03-06 Sale 195.79 1298 254232 65547 Mar 07 08:01 PM
3 Taneja Vaibhav Chief Accounting Officer 2022-03-05 Option Exercise 0.00 7138 0 102923 Mar 07 07:57 PM
4 Baglino Andrew D SVP Powertrain and Energy Eng. 2022-03-05 Option Exercise 0.00 2586 0 66845 Mar 07 08:01 PM
5 Kirkhorn Zachary Chief Financial Officer 2022-03-05 Option Exercise 0.00 16867 0 213528 Mar 07 07:58 PM
6 Baglino Andrew D SVP Powertrain and Energy Eng. 2022-02-27 Option Exercise 20.91 10500 219555 74759 Mar 01 07:29 PM
7 Baglino Andrew D SVP Powertrain and Energy Eng. 2022-02-27 Sale 202.00 10500 2121000 64259 Mar 01 07:29 PM
8 Kirkhorn Zachary Chief Financial Officer 2022-02-06 Sale 193.00 3750 723750 196661 Feb 08 06:14 PM
9 Baglino Andrew D SVP Powertrain and Energy Eng. 2022-01-27 Option Exercise 20.91 10500 219555 74759 Jan 31 07:34 PM

Проблема пропущенных данных:

Проблема пропущенных данных — это отсутствие значений в наборе данных, что может искажать результаты анализа и статистические выводы.

Проверка на отсутствие значений, представленная ниже, показала, что DataFrame не имеет пустых значений признаков. Нет необходимости использовать методы заполнения пропущенных данных.

In [642]:
# Проверка пропущенных данных
def check_null_columns(dataframe: DataFrame) -> None:
    # Присутствуют ли пустые значения признаков
    print('Присутствуют ли пустые значения признаков в колонке:')
    print(dataframe.isnull().any(), '\n')

    # Количество пустых значений признаков
    print('Количество пустых значений признаков в колонке:')
    print(dataframe.isnull().sum(), '\n')

    # Процент пустых значений признаков
    print('Процент пустых значений признаков в колонке:')
    for column in dataframe.columns:
        null_rate: float = dataframe[column].isnull().sum() / len(dataframe) * 100
        if null_rate > 0:
            print(f"{column} процент пустых значений: {null_rate:.2f}%")
    print()
            

# Проверка пропущенных данных
check_null_columns(df)
Присутствуют ли пустые значения признаков в колонке:
Insider Trading    False
Relationship       False
Date               False
Transaction        False
Cost               False
Shares             False
Value ($)          False
Shares Total       False
SEC Form 4         False
dtype: bool 

Количество пустых значений признаков в колонке:
Insider Trading    0
Relationship       0
Date               0
Transaction        0
Cost               0
Shares             0
Value ($)          0
Shares Total       0
SEC Form 4         0
dtype: int64 

Процент пустых значений признаков в колонке:

Проблема зашумленности данных:

Зашумленность это наличие случайных ошибок или вариаций в данных, которые могут затруднить выявление истинных закономерностей. Шум может возникать из-за ошибок измерений, неправильных записей или других факторов.

Выбросы это значения, которые значительно отличаются от остальных наблюдений в наборе данных. Выбросы могут указывать на ошибки в данных или на редкие, но важные события. Их наличие может повлиять на статистические методы анализа.

Представленный ниже код помогает определить наличие выбросов в наборе данных и устранить их (при наличии), заменив значения ниже нижней границы (рассматриваемого минимума) на значения нижней границы, а значения выше верхней границы (рассматриваемого максимума) на значения верхней границы.

In [643]:
# Проверка выбросов в DataFrame
def check_outliers(dataframe: DataFrame, columns: list[str]) -> None:
    for column in columns:
        if not pd.api.types.is_numeric_dtype(dataframe[column]): # Проверяем, является ли колонка числовой
            continue
        
        Q1: float = dataframe[column].quantile(0.25) # 1-й квартиль (25%)
        Q3: float = dataframe[column].quantile(0.75) # 3-й квартиль (75%)
        IQR: float = Q3 - Q1 # Вычисляем межквартильный размах

        # Определяем границы для выбросов
        lower_bound: float = Q1 - 1.5 * IQR # Нижняя граница
        upper_bound: float = Q3 + 1.5 * IQR # Верхняя граница

        # Подсчитываем количество выбросов
        outliers: DataFrame = dataframe[(dataframe[column] < lower_bound) | (dataframe[column] > upper_bound)]
        outlier_count: int = outliers.shape[0]

        print(f"Колонка {column}:")
        print(f"\tЕсть выбросы: {'Да' if outlier_count > 0 else 'Нет'}")
        print(f"\tКоличество выбросов: {outlier_count}")
        print(f"\tМинимальное значение: {dataframe[column].min()}")
        print(f"\tМаксимальное значение: {dataframe[column].max()}")
        print(f"\t1-й квартиль (Q1): {Q1}")
        print(f"\t3-й квартиль (Q3): {Q3}\n")

# Визуализация выбросов
def visualize_outliers(dataframe: DataFrame, columns: list[str]) -> None:
    # Диаграммы размахов
    plt.figure(figsize=(15, 10))
    rows: int = ceil(len(columns) / 3)
    for index, column in enumerate(columns, 1):
        plt.subplot(rows, 3, index)
        plt.boxplot(dataframe[column], vert=True, patch_artist=True)
        plt.title(f"Диаграмма размахов для \"{column}\"")
        plt.xlabel(column)
            
    # Отображение графиков
    plt.tight_layout()
    plt.show()


# Числовые столбцы DataFrame
numeric_columns: list[str] = [
    'Cost',
    'Shares',
    'Value ($)',
    'Shares Total'
]

# Проверка наличия выбросов в колонках
print('Проверка наличия выбросов в колонках:')
check_outliers(df, numeric_columns)
visualize_outliers(df, numeric_columns)
Проверка наличия выбросов в колонках:
Колонка Cost:
	Есть выбросы: Нет
	Количество выбросов: 0
	Минимальное значение: 0.0
	Максимальное значение: 1171.04
	1-й квартиль (Q1): 50.5225
	3-й квартиль (Q3): 934.1075

Колонка Shares:
	Есть выбросы: Да
	Количество выбросов: 25
	Минимальное значение: 121
	Максимальное значение: 11920000
	1-й квартиль (Q1): 3500.0
	3-й квартиль (Q3): 301797.75

Колонка Value ($):
	Есть выбросы: Да
	Количество выбросов: 23
	Минимальное значение: 0
	Максимальное значение: 2278695421
	1-й квартиль (Q1): 271008.0
	3-й квартиль (Q3): 148713213.25

Колонка Shares Total:
	Есть выбросы: Да
	Количество выбросов: 21
	Минимальное значение: 49
	Максимальное значение: 455467432
	1-й квартиль (Q1): 25103.5
	3-й квартиль (Q3): 1507273.75

No description has been provided for this image
In [644]:
# Устранить выборсы в DataFrame
def remove_outliers(dataframe: DataFrame, columns: list[str]) -> DataFrame:
    for column in columns:
        if not pd.api.types.is_numeric_dtype(dataframe[column]): # Проверяем, является ли колонка числовой
            continue
        
        Q1: float = dataframe[column].quantile(0.25) # 1-й квартиль (25%)
        Q3: float = dataframe[column].quantile(0.75) # 3-й квартиль (75%)
        IQR: float = Q3 - Q1 # Вычисляем межквартильный размах

        # Определяем границы для выбросов
        lower_bound: float = Q1 - 1.5 * IQR # Нижняя граница
        upper_bound: float = Q3 + 1.5 * IQR # Верхняя граница

        # Устраняем выбросы:
        # Заменяем значения ниже нижней границы на нижнюю границу
        # А значения выше верхней границы  на верхнюю
        dataframe[column] = dataframe[column].apply(lambda x: lower_bound if x < lower_bound else upper_bound if x > upper_bound else x)
    
    return dataframe


# Устраняем выборсы
df: DataFrame = remove_outliers(df, numeric_columns)

# Проверка наличия выбросов в колонках
print('Проверка наличия выбросов в колонках после их устранения:')
check_outliers(df, numeric_columns)
visualize_outliers(df, numeric_columns)
Проверка наличия выбросов в колонках после их устранения:
Колонка Cost:
	Есть выбросы: Нет
	Количество выбросов: 0
	Минимальное значение: 0.0
	Максимальное значение: 1171.04
	1-й квартиль (Q1): 50.5225
	3-й квартиль (Q3): 934.1075

Колонка Shares:
	Есть выбросы: Нет
	Количество выбросов: 0
	Минимальное значение: 121.0
	Максимальное значение: 749244.375
	1-й квартиль (Q1): 3500.0
	3-й квартиль (Q3): 301797.75

Колонка Value ($):
	Есть выбросы: Нет
	Количество выбросов: 0
	Минимальное значение: 0.0
	Максимальное значение: 371376521.125
	1-й квартиль (Q1): 271008.0
	3-й квартиль (Q3): 148713213.25

Колонка Shares Total:
	Есть выбросы: Нет
	Количество выбросов: 0
	Минимальное значение: 49.0
	Максимальное значение: 3730529.125
	1-й квартиль (Q1): 25103.5
	3-й квартиль (Q3): 1507273.75

No description has been provided for this image

Разбиение набора данных на выборки:

Групповое разбиение данных это метод разделения данных на несколько групп или подмножеств на основе определенного признака или характеристики. При этом наблюдения для одного объекта должны попасть только в одну выборку.

Основные виды выборки данных:

  1. Обучающая выборка (60-80%). Обучение модели (подбор коэффициентов некоторой математической функции для аппроксимации).
  2. Контрольная выборка (10-20%). Выбор метода обучения, настройка гиперпараметров.
  3. Тестовая выборка (10-20% или 20-30%). Оценка качества модели перед передачей заказчику.

Разделим выборку данных на 3 группы и проанализируем качество распределения данных.

Стратифицированное разбиение требует, чтобы в каждом классе, по которому происходит стратификация, было минимум по два элемента, иначе метод не сможет корректно разделить данные на тренировочные, валидационные и тестовые наборы.

Чтобы решить эту проблему введём категории для значения стоимости акций. Вместо того, чтобы использовать точные значения цен для стратификации, мы создадим категории цен, основываясь на квартилях (25%, 50%, 75%) и минимальном и максимальном значении стоимости акций. Это позволит создать более крупные классы, что устранит проблему с редкими значениями.

Категории для разбиения цен:

  • Низкая цена: цены ниже первого квартиля (25%) — это значения меньше 50.52.
  • Средняя цена: цены между первым квартилем (25%) и третьим квартилем (75%) — от 50.52 до 934.11.
  • Высокая цена: цены выше третьего квартиля (75%) и до максимального значения — это значения выше 934.11.

Весь набор данных состоит из 156 объектов, из которых 78 (около 50%) относятся к категории средней цены (medium), 39 (около 25%) — к низкой цене (low), и 39 (около 25%) — к высокой цене (high).

Все выборки показывают одинаковое распределение классов, что свидетельствует о том, что данные были отобраны случайным образом и не содержат явного смещения.

Однако, несмотря на сбалансированность при разбиении данных, в целом данные обладают значительным дисбалансом между классами. Это может быть проблемой при обучении модели, так как она может иметь тенденцию игнорировать низкие или высокие цены (low или high), что следует учитывать при дальнейшем анализе и выборе методов обработки данных.

Для получения более сбалансированных выборок данных необходимо воспользоваться методами приращения (аугментации) данных. В данном случае воспользуемся методом oversampling.

In [645]:
# Функция для создания выборок
def split_stratified_into_train_val_test(
    df_input,
    stratify_colname="y",
    frac_train=0.6,
    frac_val=0.15,
    frac_test=0.25,
    random_state=None,
) -> tuple[Any, Any, Any]:
    """
    Splits a Pandas dataframe into three subsets (train, val, and test)
    following fractional ratios provided by the user, where each subset is
    stratified by the values in a specific column (that is, each subset has
    the same relative frequency of the values in the column). It performs this
    splitting by running train_test_split() twice.

    Parameters
    ----------
    df_input : Pandas dataframe
        Input dataframe to be split.
    stratify_colname : str
        The name of the column that will be used for stratification. Usually
        this column would be for the label.
    frac_train : float
    frac_val   : float
    frac_test  : float
        The ratios with which the dataframe will be split into train, val, and
        test data. The values should be expressed as float fractions and should
        sum to 1.0.
    random_state : int, None, or RandomStateInstance
        Value to be passed to train_test_split().

    Returns
    -------
    df_train, df_val, df_test :
        Dataframes containing the three splits.
    """

    if frac_train + frac_val + frac_test != 1.0:
        raise ValueError(
            "fractions %f, %f, %f do not add up to 1.0"
            % (frac_train, frac_val, frac_test)
        )

    if stratify_colname not in df_input.columns:
        raise ValueError("%s is not a column in the dataframe" % (stratify_colname))

    X: DataFrame = df_input # Contains all columns.
    y: DataFrame = df_input[
        [stratify_colname]
    ] # Dataframe of just the column on which to stratify.

    # Split original dataframe into train and temp dataframes.
    df_train, df_temp, y_train, y_temp = train_test_split(
        X, y, 
        stratify=y, 
        test_size=(1.0 - frac_train), 
        random_state=random_state
    )

    # Split the temp dataframe into val and test dataframes.
    relative_frac_test: float = frac_test / (frac_val + frac_test)
    df_val, df_test, y_val, y_test = train_test_split(
        df_temp,
        y_temp,
        stratify=y_temp,
        test_size=relative_frac_test,
        random_state=random_state,
    )

    assert len(df_input) == len(df_train) + len(df_val) + len(df_test)

    return df_train, df_val, df_test
In [646]:
# Оценка сбалансированности
def check_balance(dataframe: DataFrame, dataframe_name: str, column: str) -> None:
    counts: Series[int] = dataframe[column].value_counts()
    print(dataframe_name + ": ", dataframe.shape)
    print(f"Распределение выборки данных по классам в колонке \"{column}\":\n", counts)
    total_count: int = len(dataframe)
    for value in counts.index:
        percentage: float = counts[value] / total_count * 100
        print(f"Процент объектов класса \"{value}\": {percentage:.2f}%")
    print()
   
# Определение необходимости аугментации данных
def need_augmentation(dataframe: DataFrame,
                      column: str, 
                      first_value: Any, second_value: Any) -> bool:
    counts: Series[int] = dataframe[column].value_counts()
    ratio: float = counts[first_value] / counts[second_value]
    return ratio > 1.5 or ratio < 0.67
 
 # Визуализация сбалансированности классов
def visualize_balance(dataframe_train: DataFrame,
                      dataframe_val: DataFrame,
                      dataframe_test: DataFrame, 
                      column: str) -> None:
   fig, axes = plt.subplots(1, 3, figsize=(15, 5))

   # Обучающая выборка
   counts_train: Series[int] = dataframe_train[column].value_counts()
   axes[0].pie(counts_train, labels=counts_train.index, autopct='%1.1f%%', startangle=90)
   axes[0].set_title(f"Распределение классов \"{column}\" в обучающей выборке")

   # Контрольная выборка
   counts_val: Series[int] = dataframe_val[column].value_counts()
   axes[1].pie(counts_val, labels=counts_val.index, autopct='%1.1f%%', startangle=90)
   axes[1].set_title(f"Распределение классов \"{column}\" в контрольной выборке")

   # Тестовая выборка
   counts_test: Series[int] = dataframe_test[column].value_counts()
   axes[2].pie(counts_test, labels=counts_test.index, autopct='%1.1f%%', startangle=90)
   axes[2].set_title(f"Распределение классов \"{column}\" в тренировочной выборке")

   # Отображение графиков
   plt.tight_layout()
   plt.show()
 

# Унитарное кодирование категориальных признаков (one-hot encoding)
df_encoded: DataFrame = pd.get_dummies(df)

# Вывод распределения количества наблюдений по меткам (классам)
print('Распределение количества наблюдений по меткам (классам):')
print(df_encoded['Cost'].value_counts(), '\n')

# Статистическое описание целевого признака
print('Статистическое описание целевого признака:')
print(df_encoded['Cost'].describe().transpose(), '\n')

# Определим границы для каждой категории стоимости акций
bins: list[float] = [df_encoded['Cost'].min() - 1, 
                     df_encoded['Cost'].quantile(0.25), 
                     df_encoded['Cost'].quantile(0.75), 
                     df_encoded['Cost'].max() + 1]
labels: list[str] = ['low', 'medium', 'high']

# Создаем новую колонку с категориями стоимости акций
df_encoded['Cost_category'] = pd.cut(df_encoded['Cost'], bins=bins, labels=labels)

# Вывод распределения количества наблюдений по меткам (классам)
print('Распределение количества наблюдений по меткам (классам):')
print(df_encoded['Cost_category'].value_counts(), '\n')

df_train, df_val, df_test = split_stratified_into_train_val_test(
   df_encoded, 
   stratify_colname="Cost_category", 
   frac_train=0.60, 
   frac_val=0.20, 
   frac_test=0.20
)

# Проверка сбалансированности выборок
print('Проверка сбалансированности выборок:')
check_balance(df_train, 'Обучающая выборка', 'Cost_category')
check_balance(df_val, 'Контрольная выборка', 'Cost_category')
check_balance(df_test, 'Тестовая выборка', 'Cost_category')

# Проверка необходимости аугментации выборок
print('Проверка необходимости аугментации выборок:')
print(f"Для обучающей выборки аугментация данных {'не ' if not need_augmentation(df_train, 'Cost_category', 'low', 'medium') else ''}требуется")
print(f"Для контрольной выборки аугментация данных {'не ' if not need_augmentation(df_val, 'Cost_category', 'low', 'medium') else ''}требуется")
print(f"Для тестовой выборки аугментация данных {'не ' if not need_augmentation(df_test, 'Cost_category', 'low', 'medium') else ''}требуется")
   
# Визуализация сбалансированности классов
visualize_balance(df_train, df_val, df_test, 'Cost_category')
Распределение количества наблюдений по меткам (классам):
Cost
0.00       18
6.24       10
62.72       8
20.91       7
52.38       4
           ..
1098.24     1
1072.22     1
1019.03     1
1048.46     1
1068.09     1
Name: count, Length: 101, dtype: int64 

Статистическое описание целевого признака:
count     156.000000
mean      478.785641
std       448.922903
min         0.000000
25%        50.522500
50%       240.225000
75%       934.107500
max      1171.040000
Name: Cost, dtype: float64 

Распределение количества наблюдений по меткам (классам):
Cost_category
medium    78
low       39
high      39
Name: count, dtype: int64 

Проверка сбалансированности выборок:
Обучающая выборка:  (93, 184)
Распределение выборки данных по классам в колонке "Cost_category":
 Cost_category
medium    47
low       23
high      23
Name: count, dtype: int64
Процент объектов класса "medium": 50.54%
Процент объектов класса "low": 24.73%
Процент объектов класса "high": 24.73%

Контрольная выборка:  (31, 184)
Распределение выборки данных по классам в колонке "Cost_category":
 Cost_category
medium    15
low        8
high       8
Name: count, dtype: int64
Процент объектов класса "medium": 48.39%
Процент объектов класса "low": 25.81%
Процент объектов класса "high": 25.81%

Тестовая выборка:  (32, 184)
Распределение выборки данных по классам в колонке "Cost_category":
 Cost_category
medium    16
low        8
high       8
Name: count, dtype: int64
Процент объектов класса "medium": 50.00%
Процент объектов класса "low": 25.00%
Процент объектов класса "high": 25.00%

Проверка необходимости аугментации выборок:
Для обучающей выборки аугментация данных требуется
Для контрольной выборки аугментация данных требуется
Для тестовой выборки аугментация данных требуется
No description has been provided for this image

Приращение данных:

Аугментация данных может быть полезна в том случае, когда имеется недостаточное количество данных и мы хотим сгенерировать новые данные на основе имеющихся, слегка модифицировав их.

Метод решения: Выборка с избытком (oversampling) копирование наблюдений или генерация новых наблюдений на основе существующих с помощью алгоритмов SMOTE и ADASYN (нахождение k-ближайших соседей).

In [647]:
# Метод приращения с избытком (oversampling)
def oversample(df: DataFrame, column: str) -> DataFrame:
    X: DataFrame = pd.get_dummies(df.drop(column, axis=1))
    y: DataFrame = df[column] # type: ignore
    
    smote = SMOTE()
    X_resampled, y_resampled = smote.fit_resample(X, y) # type: ignore
    
    df_resampled: DataFrame = pd.concat([X_resampled, y_resampled], axis=1)
    return df_resampled


# Приращение данных (oversampling)
df_train_oversampled: DataFrame = oversample(df_train, 'Cost_category')
df_val_oversampled: DataFrame = oversample(df_val, 'Cost_category')
df_test_oversampled: DataFrame = oversample(df_test, 'Cost_category')

# Проверка сбалансированности выборок
print('Проверка сбалансированности выборок после применения метода oversampling:')
check_balance(df_train_oversampled, 'Обучающая выборка', 'Cost_category')
check_balance(df_val_oversampled, 'Контрольная выборка', 'Cost_category')
check_balance(df_test_oversampled, 'Тестовая выборка', 'Cost_category')

# Проверка необходимости аугментации выборок
print('Проверка необходимости аугментации выборок после применения метода oversampling:')
print(f"Для обучающей выборки аугментация данных {'не ' if not need_augmentation(df_train_oversampled, 'Cost_category', 'low', 'medium') else ''}требуется")
print(f"Для контрольной выборки аугментация данных {'не ' if not need_augmentation(df_val_oversampled, 'Cost_category', 'low', 'medium') else ''}требуется")
print(f"Для тестовой выборки аугментация данных {'не ' if not need_augmentation(df_test_oversampled, 'Cost_category', 'low', 'medium') else ''}требуется")
   
# Визуализация сбалансированности классов
visualize_balance(df_train_oversampled, df_val_oversampled, df_test_oversampled, 'Cost_category')
Проверка сбалансированности выборок после применения метода oversampling:
Обучающая выборка:  (141, 184)
Распределение выборки данных по классам в колонке "Cost_category":
 Cost_category
low       47
medium    47
high      47
Name: count, dtype: int64
Процент объектов класса "low": 33.33%
Процент объектов класса "medium": 33.33%
Процент объектов класса "high": 33.33%

Контрольная выборка:  (45, 184)
Распределение выборки данных по классам в колонке "Cost_category":
 Cost_category
low       15
medium    15
high      15
Name: count, dtype: int64
Процент объектов класса "low": 33.33%
Процент объектов класса "medium": 33.33%
Процент объектов класса "high": 33.33%

Тестовая выборка:  (48, 184)
Распределение выборки данных по классам в колонке "Cost_category":
 Cost_category
low       16
medium    16
high      16
Name: count, dtype: int64
Процент объектов класса "low": 33.33%
Процент объектов класса "medium": 33.33%
Процент объектов класса "high": 33.33%

Проверка необходимости аугментации выборок после применения метода oversampling:
Для обучающей выборки аугментация данных не требуется
Для контрольной выборки аугментация данных не требуется
Для тестовой выборки аугментация данных не требуется
No description has been provided for this image

Конструирование признаков:

Конструирование признаков (feature engineering) процесс использования знаний об особенностях решаемой задачи и предметной области для определения признаков, которые будут использованы для обучения статистической модели.

Методы конструирования признаков:

  1. Унитарное кодирование категориальных признаков (one-hot encoding) метод, который применяется для преобразования категориальных переменных в числовой формат. Каждая характеристика представляется в виде бинарного вектора, где для каждой категории выделяется отдельный признак (столбец) со значением 1 (True), если объект принадлежит этой категории, и 0 (False) в противном случае.

В данном случае преобразование категориальных признаков уже было выполнено на этапе разбиения данных на выборки. Для этого использовался метод "get_dummies" из библиотеки "pandas".

In [648]:
df_encoded.head(10)
Out[648]:
Cost Shares Value ($) Shares Total Insider Trading_Baglino Andrew D Insider Trading_DENHOLM ROBYN M Insider Trading_Kirkhorn Zachary Insider Trading_Musk Elon Insider Trading_Musk Kimbal Insider Trading_Taneja Vaibhav ... SEC Form 4_Nov 30 04:42 PM SEC Form 4_Oct 05 07:35 PM SEC Form 4_Oct 31 07:06 PM SEC Form 4_Sep 07 08:29 PM SEC Form 4_Sep 07 08:33 PM SEC Form 4_Sep 07 09:04 PM SEC Form 4_Sep 12 09:44 PM SEC Form 4_Sep 14 07:47 PM SEC Form 4_Sep 30 07:03 PM Cost_category
0 196.72 10455.0 2056775.0 203073.0 False False True False False False ... False False False False False False False False False medium
1 195.79 2466.0 482718.0 100458.0 False False False False False True ... False False False False False False False False False medium
2 195.79 1298.0 254232.0 65547.0 True False False False False False ... False False False False False False False False False medium
3 0.00 7138.0 0.0 102923.0 False False False False False True ... False False False False False False False False False low
4 0.00 2586.0 0.0 66845.0 True False False False False False ... False False False False False False False False False low
5 0.00 16867.0 0.0 213528.0 False False True False False False ... False False False False False False False False False low
6 20.91 10500.0 219555.0 74759.0 True False False False False False ... False False False False False False False False False low
7 202.00 10500.0 2121000.0 64259.0 True False False False False False ... False False False False False False False False False medium
8 193.00 3750.0 723750.0 196661.0 False False True False False False ... False False False False False False False False False medium
9 20.91 10500.0 219555.0 74759.0 True False False False False False ... False False False False False False False False False low

10 rows × 184 columns

  1. Дискретизация числовых признаков процесс преобразования непрерывных числовых значений в категориальные группы или интервалы (дискретные значения).

В данном случае преобразование числовой колонки "Cost" уже было выполнено ранее для стратифицированного разбиения исходных данных на выборки (обучающую, контрольную и тестовую). Для этого использовался метод квартильной группировки.

Границы интервалов определены следующим образом:

  • Минус 1 от минимального значения стоимости акций (чтобы включить все значения в первый интервал).
  • 25-й перцентиль (quantile(0.25)), разделяющий самый низкий 25% стоимостей от остальных.
  • 75-й перцентиль (quantile(0.75)), разделяющий 25% с наиболее высокими стоимостью акций.
  • Плюс 1 от максимального значения стоимости акций (чтобы включить максимальное значение в последний интервал).
In [649]:
print('Обучающая выборка:')
df_train_oversampled[['Cost', 'Cost_category']].head(10)
Обучающая выборка:
Out[649]:
Cost Cost_category
0 889.08 medium
1 20.91 low
2 62.72 medium
3 189.50 medium
4 1026.75 high
5 935.75 high
6 913.26 medium
7 62.72 medium
8 0.00 low
9 269.39 medium
  1. «Ручной» синтез признаков процесс создания новых признаков на основе существующих данных. Это может включать в себя комбинирование нескольких признаков, использование математических операций (например, сложение, вычитание), а также создание полиномиальных или логарифмических признаков.
In [650]:
df['Date'] = pd.to_datetime(df['Date']) # Преобразование в datetime
df['Year'] = df['Date'].dt.year         # Год
df['Quarter'] = df['Date'].dt.quarter   # Квартал
df['Month'] = df['Date'].dt.month       # Месяц

df[['Date', 'Year', 'Quarter', 'Month']].head(10)
Out[650]:
Date Year Quarter Month
0 2022-03-06 2022 1 3
1 2022-03-06 2022 1 3
2 2022-03-06 2022 1 3
3 2022-03-05 2022 1 3
4 2022-03-05 2022 1 3
5 2022-03-05 2022 1 3
6 2022-02-27 2022 1 2
7 2022-02-27 2022 1 2
8 2022-02-06 2022 1 2
9 2022-01-27 2022 1 1
  1. Масштабирование признаков на основе нормировки и стандартизации метод, который позволяет привести все числовые признаки к одинаковым или очень похожим диапазонам значений либо распределениям.

Методы масштабирования признаков:

  • Нормировка обычно применяется для равномерного распределения;
  • Стандартизация обычно применяется для нормального распределения.
In [651]:
# scaler = MinMaxScaler()
scaler = StandardScaler()

# Применяем масштабирование к выбранным признакам
df[numeric_columns] = scaler.fit_transform(df[numeric_columns])

df[numeric_columns].head(10)
Out[651]:
Cost Shares Value ($) Shares Total
0 -0.630340 -0.607179 -0.583446 -0.528366
1 -0.632418 -0.635043 -0.594307 -0.604905
2 -0.632418 -0.639117 -0.595883 -0.630945
3 -1.069956 -0.618748 -0.597637 -0.603067
4 -1.069956 -0.634624 -0.597637 -0.629977
5 -1.069956 -0.584816 -0.597637 -0.520567
6 -1.023228 -0.607022 -0.596122 -0.624074
7 -0.618541 -0.607022 -0.583003 -0.631906
8 -0.638653 -0.630565 -0.592643 -0.533148
9 -1.023228 -0.607022 -0.596122 -0.624074
  1. С применением фреймворка FeatureTools библиотека для автоматизированного создания признаков (features) из структурированных данных. Подходит для задач машинного обучения, когда нужно быстро извлекать полезные признаки из больших объемов данных.
In [664]:
df: DataFrame = pd.read_csv('..//static//csv//TSLA.csv')

# Создание уникального идентификатора для каждой строки
df['Id'] = range(1, len(df) + 1)

# Создание EntitySet
es = ft.EntitySet(id="Id")

# Добавляем таблицу с индексом
es: EntitySet = es.add_dataframe(
    dataframe_name="trades", 
    dataframe=df, 
    index="Id", 
    time_index="Date"
)

# Генерация признаков с помощью глубокого синтеза признаков
feature_matrix, feature_defs = ft.dfs(
    entityset=es, 
    target_dataframe_name='trades', 
    max_depth=1
)

# Выводим первые 10 строк сгенерированного набора признаков
feature_matrix.head(10)
d:\ULSTU\Семестр 5\AIM-PIbd-31-Masenkin-M-S\aimenv\Lib\site-packages\woodwork\type_sys\utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  pd.to_datetime(
d:\ULSTU\Семестр 5\AIM-PIbd-31-Masenkin-M-S\aimenv\Lib\site-packages\woodwork\type_sys\utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  pd.to_datetime(
d:\ULSTU\Семестр 5\AIM-PIbd-31-Masenkin-M-S\aimenv\Lib\site-packages\woodwork\type_sys\utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  pd.to_datetime(
d:\ULSTU\Семестр 5\AIM-PIbd-31-Masenkin-M-S\aimenv\Lib\site-packages\woodwork\type_sys\utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  pd.to_datetime(
d:\ULSTU\Семестр 5\AIM-PIbd-31-Masenkin-M-S\aimenv\Lib\site-packages\woodwork\type_sys\utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  pd.to_datetime(
d:\ULSTU\Семестр 5\AIM-PIbd-31-Masenkin-M-S\aimenv\Lib\site-packages\woodwork\type_sys\utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  pd.to_datetime(
d:\ULSTU\Семестр 5\AIM-PIbd-31-Masenkin-M-S\aimenv\Lib\site-packages\woodwork\type_sys\utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  pd.to_datetime(
d:\ULSTU\Семестр 5\AIM-PIbd-31-Masenkin-M-S\aimenv\Lib\site-packages\woodwork\type_sys\utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  pd.to_datetime(
d:\ULSTU\Семестр 5\AIM-PIbd-31-Masenkin-M-S\aimenv\Lib\site-packages\woodwork\type_sys\utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  pd.to_datetime(
d:\ULSTU\Семестр 5\AIM-PIbd-31-Masenkin-M-S\aimenv\Lib\site-packages\woodwork\type_sys\utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  pd.to_datetime(
d:\ULSTU\Семестр 5\AIM-PIbd-31-Masenkin-M-S\aimenv\Lib\site-packages\woodwork\type_sys\utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  pd.to_datetime(
d:\ULSTU\Семестр 5\AIM-PIbd-31-Masenkin-M-S\aimenv\Lib\site-packages\woodwork\type_sys\utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  pd.to_datetime(
d:\ULSTU\Семестр 5\AIM-PIbd-31-Masenkin-M-S\aimenv\Lib\site-packages\woodwork\type_sys\utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  pd.to_datetime(
d:\ULSTU\Семестр 5\AIM-PIbd-31-Masenkin-M-S\aimenv\Lib\site-packages\woodwork\type_sys\utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  pd.to_datetime(
Out[664]:
Insider Trading Relationship Transaction Cost DAY(Date) MONTH(Date) WEEKDAY(Date) YEAR(Date)
Id
154 Musk Elon CEO Sale 1019.03 10 11 2 2021
155 Musk Elon CEO Sale 1048.46 10 11 2 2021
156 Musk Elon CEO Sale 1068.09 10 11 2 2021
152 Musk Elon CEO Sale 1098.24 11 11 3 2021
153 Musk Elon CEO Sale 1072.22 11 11 3 2021
151 Musk Elon CEO Sale 1029.67 12 11 4 2021
148 Musk Elon CEO Option Exercise 6.24 15 11 0 2021
149 Musk Elon CEO Sale 992.72 15 11 0 2021
150 Musk Elon CEO Sale 1015.85 15 11 0 2021
145 Musk Elon CEO Option Exercise 6.24 16 11 1 2021

Оценка качества набора признаков:

Предсказательная способность: Способность набора признаков успешно прогнозировать целевую переменную. Это определяется через метрики, такие как RMSE, MAE, R², которые показывают, насколько хорошо модель использует признаки для достижения точных результатов. Для определения качества необходимо провести обучение модели на обучающей выборке и сравнить с оценкой прогнозирования на контрольной и тестовой выборках.

Скорость вычисления: Время, необходимое для обработки данных и выполнения алгоритмов машинного обучения. Признаки должны быть вычисляемыми за разумный срок, чтобы обеспечить эффективность модели, особенно при работе с большими наборами данных. Для оценки качества необходимо провести измерение времени выполнения генерации признаков и обучения модели.

Надежность: Устойчивость и воспроизводимость результатов при изменении входных данных. Надежные признаки должны давать схожие результаты независимо от случайных факторов или незначительных изменений в данных. Методы оценки: Кросс-валидация, анализ чувствительности модели к изменениям в данных.

Корреляция: Степень взаимосвязи между признаками и целевой переменной, а также между самими признаками. Высокая корреляция с целевой переменной указывает на потенциальную предсказательную силу, тогда как высокая взаимосвязь между самими признаками может приводить к многоколлинеарности и снижению эффективности модели. Методы оценки: Анализ корреляционной матрицы признаков, удаление мультиколлинеарных признаков.

Цельность: Не является производным от других признаков. Методы оценки: Проверка логической связи между признаками и целевой переменной, интерпретация результатов модели.

In [653]:
# Разбить выборку на входные данные и целевой признак
def split_dataframe(dataframe: DataFrame, column: str) -> tuple[DataFrame, DataFrame]:
    X_dataframe: DataFrame = dataframe.drop(columns=column, axis=1)
    y_dataframe: DataFrame = dataframe[column]
    
    return X_dataframe, y_dataframe


# Разбиение обучающей выборки на входные данные и целевой признак
df_train_oversampled: DataFrame = pd.get_dummies(df_train_oversampled)
X_df_train, y_df_train = split_dataframe(df_train_oversampled, "Cost")

# Разбиение контрольной выборки на входные данные и целевой признак
df_val_oversampled: DataFrame = pd.get_dummies(df_val_oversampled)
X_df_val, y_df_val = split_dataframe(df_val_oversampled, "Cost")

# Разбиение тестовой выборки на входные данные и целевой признак
df_test_oversampled: DataFrame = pd.get_dummies(df_test_oversampled)
X_df_test, y_df_test = split_dataframe(df_test_oversampled, "Cost")


# Модель линейной регрессии для обучения
model = LinearRegression()

# Начинаем отсчет времени
start_time: float = time.time()
model.fit(X_df_train, y_df_train)

# Время обучения модели
train_time: float = time.time() - start_time

# Предсказания и оценка модели
predictions = model.predict(X_df_val)
mse = root_mean_squared_error(y_df_val, predictions)

print(f'Время обучения модели: {train_time:.2f} секунд')
print(f'Среднеквадратичная ошибка: {mse:.2f}')
Время обучения модели: 0.30 секунд
Среднеквадратичная ошибка: 195.01
In [654]:
# Модель случайного леса для обучения
model = RandomForestRegressor()

# Обучение модели
model.fit(X_df_train, y_df_train)

# Предсказание и оценка
y_predictions = model.predict(X_df_test)

rmse = root_mean_squared_error(y_df_test, y_predictions)
r2 = r2_score(y_df_test, y_predictions)
mae = mean_absolute_error(y_df_test, y_predictions)

print(f"RMSE: {rmse}")
print(f"R²: {r2}")
print(f"MAE: {mae}\n")

# Кросс-валидация
scores = cross_val_score(model, X_df_train, y_df_train, cv=5, scoring='neg_mean_squared_error')
rmse_cv = (-scores.mean())**0.5
print(f"Кросс-валидация RMSE: {rmse_cv}\n")

# Анализ важности признаков
feature_importances = model.feature_importances_
feature_names = X_df_train.columns

# Проверка на переобучение
y_train_predictions = model.predict(X_df_train)

rmse_train = root_mean_squared_error(y_df_train, y_train_predictions)
r2_train = r2_score(y_df_train, y_train_predictions)
mae_train = mean_absolute_error(y_df_train, y_train_predictions)

print(f"Train RMSE: {rmse_train}")
print(f"Train R²: {r2_train}")
print(f"Train MAE: {mae_train}\n")

# Визуализация результатов
plt.figure(figsize=(10, 6))
plt.scatter(y_df_test, y_predictions, alpha=0.5)
plt.plot([y_df_test.min(), y_df_test.max()], [y_df_test.min(), y_df_test.max()], 'k--', lw=2)
plt.xlabel('Фактическая стоимость')
plt.ylabel('Прогнозируемая стоимость')
plt.title('Фактическая стоимость по сравнению с прогнозируемой')
RMSE: 89.7060467247578
R²: 0.9632863955859148
MAE: 51.21015516441333

Кросс-валидация RMSE: 148.73333849768954

Train RMSE: 49.74051197142329
Train R²: 0.9887155168427046
Train MAE: 22.617172692464216

Out[654]:
Text(0.5, 1.0, 'Фактическая стоимость по сравнению с прогнозируемой')
No description has been provided for this image

Вывод:

  1. Оценка качества модели на тестовой выборке:

    • RMSE (Корень из среднеквадратичной ошибки) на тестовой выборке составил 89.71, что указывает на среднюю ошибку в прогнозах.
    • (Коэффициент детерминации) равен 0.96, что означает, что модель объясняет 96% дисперсии данных. Это хороший показатель, указывающий на высокую объяснительную способность модели.
    • MAE (Средняя абсолютная ошибка) составила 51.21, показывая среднее абсолютное отклонение предсказаний от фактических значений.
  2. Результаты кросс-валидации:

    • RMSE кросс-валидации равен 148.73, что заметно выше значения RMSE на тестовой выборке. Это может свидетельствовать о том, что модель может быть подвержена колебаниям в зависимости от данных и, возможно, склонна к некоторому переобучению.
  3. Проверка на переобучение:

    • Метрики на обучающей выборке (RMSE = 49.74, R² = 0.99, MAE = 22.62) значительно лучше, чем на тестовой, что указывает на высокую точность на обучающих данных.
    • Разница в ошибках между обучающей и тестовой выборками говорит о возможном переобучении. Модель хорошо подходит к обучающим данным, но на тестовой выборке дает несколько более слабые результаты, что типично для моделей случайного леса, когда модель выучивает мелкие детали обучающего набора.

Итог: Модель случайного леса демонстрирует хорошее качество прогнозов с высоким коэффициентом детерминации на тестовых данных. Однако разница в ошибках на обучающей и тестовой выборках вместе с результатами кросс-валидации указывает на возможное переобучение.