AIM-PIbd-31-Masenkin-M-S/lab_2/lab2.ipynb
2024-10-17 12:31:09 +04:00

378 KiB
Raw Blame History

Датасет №1: Объекты вокруг Земли.

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

Данный набор данных представляет собой коллекцию сведений о ближайших к Земле объектах (астероидах), сертифицированных NASA. Он содержит данные, которые могут помочь идентифицировать потенциально опасные астероиды, которые могут оказать влияние на Землю или на космические миссии. Набор данных включает в себя такие ключевые характеристики астероидов, как их размер, скорость, расстояние до Земли и информация о возможной опасности столкновения.


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

Проблемная область: Основной проблемной областью является отслеживание и оценка рисков, связанных с приближением астероидов к Земле. С помощью данных о движении и характеристиках астероидов можно предсказать возможные столкновения и минимизировать угрозу для Земли, планируя превентивные действия.

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

Объекты наблюдения: Объектами наблюдения в данном наборе данных являются астероиды, классифицированные NASA как "ближайшие к Земле объекты" (Near-Earth Objects, NEO). Эти объекты могут проходить в непосредственной близости от Земли, что потенциально представляет опасность.

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

  • id: Уникальный идентификатор астероида.
  • name: Название, присвоенное астероиду NASA.
  • est_diameter_min: Минимальный оценочные диаметры астероида в километрах.
  • est_diameter_max: Максимальный оценочные диаметры астероида в километрах.
  • relative_velocity: Скорость астероида относительно Земли (в км/с).
  • miss_distance: Расстояние, на котором астероид пролетел мимо Земли, в километрах.
  • orbiting_body: Планета, вокруг которой вращается астероид.
  • sentry_object: Признак, указывающий на наличие астероида в системе автоматического мониторинга столкновений (система Sentry).
  • absolute_magnitude: Абсолютная величина, описывающая яркость объекта.
  • hazardous: Булев признак, указывающий, является ли астероид потенциально опасным.

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


Качество набора данных:

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

Степень покрытия: Набор данных включает данные о большом количестве астероидов (>90000 записей), что позволяет охватить значительную часть ближайших к Земле объектов. Однако не все астероиды могут быть обнаружены, так как данные зависят от возможности их наблюдения.

Соответствие реальным данным: Данные в наборе предоставлены NASA, что указывает на высокую достоверность и актуальность информации. Тем не менее, параметры, такие как диаметр и расстояние, могут быть оценочными и подвергаться уточнению с новыми наблюдениями.

Согласованность меток: Метрики в датасете четко обозначены, а булевы признаки, такие как "hazardous" (опасен или нет), соответствуют конкретным параметрам астероидов и легко интерпретируются.


Бизес-цели:

  1. Мониторинг космических угроз: Создание системы, которая анализирует астероиды и предсказывает риски столкновения с Землей, помогая государственным агентствам и частным компаниям разрабатывать превентивные меры.
  2. Поддержка космических миссий: Предоставление точных данных для планирования и безопасного проведения космических миссий, минимизация рисков столкновения с космическими объектами.
  3. Образовательные и научные исследования: Использование данных для поддержки образовательных программ и научных исследований в области астрономии и космической безопасности.

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


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

  1. Моделирование риска столкновения: Построение алгоритмов машинного обучения для прогнозирования вероятности столкновения астероидов с Землей.
  2. Анализ и кластеризация астероидов: Исследование взаимосвязей между астероидами, анализ орбитальных данных и выделение групп астероидов, имеющих схожие характеристики.
  3. Оптимизация системы предупреждения угроз: Создание системы раннего оповещения, которая будет автоматически анализировать данные и предупреждать о потенциальных угрозах в реальном времени.

Входные данные: Диаметр, скорость, расстояние, орбитальные параметры астероидов.

Целевой признак: Признак "hazardous" бинарная метка, указывающая на потенциальную опасность астероида.


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

In [1]:
from typing import Any
from math import ceil

import pandas as pd
from pandas import DataFrame, Series
from sklearn.model_selection import train_test_split
from imblearn.over_sampling import ADASYN
from imblearn.under_sampling import RandomUnderSampler
import matplotlib.pyplot as plt


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

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

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

# Статистическое описание числовых столбцов
df.describe()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 90836 entries, 0 to 90835
Data columns (total 10 columns):
 #   Column              Non-Null Count  Dtype  
---  ------              --------------  -----  
 0   id                  90836 non-null  int64  
 1   name                90836 non-null  object 
 2   est_diameter_min    90836 non-null  float64
 3   est_diameter_max    90836 non-null  float64
 4   relative_velocity   90836 non-null  float64
 5   miss_distance       90836 non-null  float64
 6   orbiting_body       90836 non-null  object 
 7   sentry_object       90836 non-null  bool   
 8   absolute_magnitude  90836 non-null  float64
 9   hazardous           90836 non-null  bool   
dtypes: bool(2), float64(5), int64(1), object(2)
memory usage: 5.7+ MB
Out[2]:
id est_diameter_min est_diameter_max relative_velocity miss_distance absolute_magnitude
count 9.083600e+04 90836.000000 90836.000000 90836.000000 9.083600e+04 90836.000000
mean 1.438288e+07 0.127432 0.284947 48066.918918 3.706655e+07 23.527103
std 2.087202e+07 0.298511 0.667491 25293.296961 2.235204e+07 2.894086
min 2.000433e+06 0.000609 0.001362 203.346433 6.745533e+03 9.230000
25% 3.448110e+06 0.019256 0.043057 28619.020645 1.721082e+07 21.340000
50% 3.748362e+06 0.048368 0.108153 44190.117890 3.784658e+07 23.700000
75% 3.884023e+06 0.143402 0.320656 62923.604633 5.654900e+07 25.700000
max 5.427591e+07 37.892650 84.730541 236990.128088 7.479865e+07 33.200000

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

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

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

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

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

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

# Проверка пропущенных данных
check_null_columns(df)
id                    False
name                  False
est_diameter_min      False
est_diameter_max      False
relative_velocity     False
miss_distance         False
orbiting_body         False
sentry_object         False
absolute_magnitude    False
hazardous             False
dtype: bool 

id                    0
name                  0
est_diameter_min      0
est_diameter_max      0
relative_velocity     0
miss_distance         0
orbiting_body         0
sentry_object         0
absolute_magnitude    0
hazardous             0
dtype: int64

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

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

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

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

In [4]:
# Числовые столбцы DataFrame
numeric_columns: list[str] = [
    'est_diameter_min',
    'est_diameter_max', 
    'relative_velocity', 
    'miss_distance', 
    'absolute_magnitude'
]

# Проверка выбросов в 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()


# Проверка выбросов
check_outliers(df, numeric_columns)
visualize_outliers(df, numeric_columns)
Колонка est_diameter_min:
	Есть выбросы: Да
	Количество выбросов: 8306
	Минимальное значение: 0.0006089126
	Максимальное значение: 37.8926498379
	1-й квартиль (Q1): 0.0192555078
	3-й квартиль (Q3): 0.1434019235

Колонка est_diameter_max:
	Есть выбросы: Да
	Количество выбросов: 8306
	Минимальное значение: 0.00136157
	Максимальное значение: 84.7305408852
	1-й квартиль (Q1): 0.0430566244
	3-й квартиль (Q3): 0.320656449

Колонка relative_velocity:
	Есть выбросы: Да
	Количество выбросов: 1574
	Минимальное значение: 203.34643253
	Максимальное значение: 236990.1280878666
	1-й квартиль (Q1): 28619.02064490995
	3-й квартиль (Q3): 62923.60463276395

Колонка miss_distance:
	Есть выбросы: Нет
	Количество выбросов: 0
	Минимальное значение: 6745.532515957
	Максимальное значение: 74798651.4521972
	1-й квартиль (Q1): 17210820.23576468
	3-й квартиль (Q3): 56548996.45139917

Колонка absolute_magnitude:
	Есть выбросы: Да
	Количество выбросов: 101
	Минимальное значение: 9.23
	Максимальное значение: 33.2
	1-й квартиль (Q1): 21.34
	3-й квартиль (Q3): 25.7

No description has been provided for this image
In [5]:
# Устранить выборсы в 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)

# Проверка выбросов
check_outliers(df, numeric_columns)
visualize_outliers(df, numeric_columns)
Колонка est_diameter_min:
	Есть выбросы: Нет
	Количество выбросов: 0
	Минимальное значение: 0.0006089126
	Максимальное значение: 0.32962154705
	1-й квартиль (Q1): 0.0192555078
	3-й квартиль (Q3): 0.1434019235

Колонка est_diameter_max:
	Есть выбросы: Нет
	Количество выбросов: 0
	Минимальное значение: 0.00136157
	Максимальное значение: 0.7370561859
	1-й квартиль (Q1): 0.0430566244
	3-й квартиль (Q3): 0.320656449

Колонка relative_velocity:
	Есть выбросы: Нет
	Количество выбросов: 0
	Минимальное значение: 203.34643253
	Максимальное значение: 114380.48061454494
	1-й квартиль (Q1): 28619.02064490995
	3-й квартиль (Q3): 62923.60463276395

Колонка miss_distance:
	Есть выбросы: Нет
	Количество выбросов: 0
	Минимальное значение: 6745.532515957
	Максимальное значение: 74798651.4521972
	1-й квартиль (Q1): 17210820.23576468
	3-й квартиль (Q3): 56548996.45139917

Колонка absolute_magnitude:
	Есть выбросы: Нет
	Количество выбросов: 0
	Минимальное значение: 14.8
	Максимальное значение: 32.239999999999995
	1-й квартиль (Q1): 21.34
	3-й квартиль (Q3): 25.7

No description has been provided for this image

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

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

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

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

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

Весь набор данных состоит из 90836 объектов, из которых 81996 (около 90.3%) неопасны (False), а 8840 (около 9.7%) опасны (True). Это говорит о том, что класс "неопасные" значительно преобладает.

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

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

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

In [6]:
# Функция для создания выборок
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 [7]:
# Вывод распределения количества наблюдений по меткам (классам)
print(df.hazardous.value_counts(), '\n')

data: DataFrame = df[[
   'est_diameter_min', 
   'est_diameter_max', 
   'relative_velocity', 
   'miss_distance', 
   'absolute_magnitude', 
   'hazardous'
]].copy()

df_train, df_val, df_test = split_stratified_into_train_val_test(
   data, 
   stratify_colname="hazardous", 
   frac_train=0.60, 
   frac_val=0.20, 
   frac_test=0.20
)

# Оценка сбалансированности
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()
 

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

# Проверка необходимости аугментации
print(f"Для обучающей выборки аугментация данных {'не ' if not need_augmentation(df_train, 'hazardous', True, False) else ''}требуется")
print(f"Для контрольной выборки аугментация данных {'не ' if not need_augmentation(df_val, 'hazardous', True, False) else ''}требуется")
print(f"Для тестовой выборки аугментация данных {'не ' if not need_augmentation(df_test, 'hazardous', True, False) else ''}требуется")
   
# Визуализация сбалансированности классов
visualize_balance(df_train, df_val, df_test, 'hazardous')
hazardous
False    81996
True      8840
Name: count, dtype: int64 

Обучающая выборка:  (54501, 6)
Распределение выборки данных по классам "hazardous":
 hazardous
False    49197
True      5304
Name: count, dtype: int64
Процент объектов класса "False": 90.27%
Процент объектов класса "True": 9.73%

Контрольная выборка:  (18167, 6)
Распределение выборки данных по классам "hazardous":
 hazardous
False    16399
True      1768
Name: count, dtype: int64
Процент объектов класса "False": 90.27%
Процент объектов класса "True": 9.73%

Тестовая выборка:  (18168, 6)
Распределение выборки данных по классам "hazardous":
 hazardous
False    16400
True      1768
Name: count, dtype: int64
Процент объектов класса "False": 90.27%
Процент объектов класса "True": 9.73%

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

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

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

Методы решения:

  1. Выборка с избытком (oversampling). Копирование наблюдений или генерация новых наблюдений на основе существующих с помощью алгоритмов SMOTE и ADASYN (нахождение k-ближайших соседей).
  2. Выборка с недостатком (undersampling). Исключение некоторых наблюдений для меток с большим количеством наблюдений. Наблюдения можно исключать случайным образом или на основе определения связей Томека для наблюдений разных меток.
In [8]:
# Метод приращения с избытком (oversampling)
def oversample(df: DataFrame, column: str) -> DataFrame:
    X: DataFrame = df.drop(column, axis=1)
    y: DataFrame = df[column] # type: ignore
    
    adasyn = ADASYN()
    X_resampled, y_resampled = adasyn.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, 'hazardous')
df_val_oversampled: DataFrame = oversample(df_val, 'hazardous')
df_test_oversampled: DataFrame = oversample(df_test, 'hazardous')

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

# Проверка необходимости аугментации
print(f"Для обучающей выборки аугментация данных {'не ' if not need_augmentation(df_train_oversampled, 'hazardous', True, False) else ''}требуется")
print(f"Для контрольной выборки аугментация данных {'не ' if not need_augmentation(df_val_oversampled, 'hazardous', True, False) else ''}требуется")
print(f"Для тестовой выборки аугментация данных {'не ' if not need_augmentation(df_test_oversampled, 'hazardous', True, False) else ''}требуется")
   
# Визуализация сбалансированности классов
visualize_balance(df_train_oversampled, df_val_oversampled, df_test_oversampled, 'hazardous')
После применения метода oversampling:
Обучающая выборка:  (100699, 6)
Распределение выборки данных по классам "hazardous":
 hazardous
True     51502
False    49197
Name: count, dtype: int64
Процент объектов класса "True": 51.14%
Процент объектов класса "False": 48.86%

Контрольная выборка:  (32759, 6)
Распределение выборки данных по классам "hazardous":
 hazardous
False    16399
True     16360
Name: count, dtype: int64
Процент объектов класса "False": 50.06%
Процент объектов класса "True": 49.94%

Тестовая выборка:  (33556, 6)
Распределение выборки данных по классам "hazardous":
 hazardous
True     17156
False    16400
Name: count, dtype: int64
Процент объектов класса "True": 51.13%
Процент объектов класса "False": 48.87%

Для обучающей выборки аугментация данных не требуется
Для контрольной выборки аугментация данных не требуется
Для тестовой выборки аугментация данных не требуется
No description has been provided for this image
In [9]:
# Метод приращения с недостатком (undersampling)
def undersample(df: DataFrame, column: str) -> DataFrame:
    X: DataFrame = df.drop(column, axis=1)
    y: DataFrame = df[column] # type: ignore
    
    undersampler = RandomUnderSampler()
    X_resampled, y_resampled = undersampler.fit_resample(X, y) # type: ignore
    
    df_resampled: DataFrame = pd.concat([X_resampled, y_resampled], axis=1)
    return df_resampled


# Приращение данных (undersampling)
df_train_undersampled: DataFrame = undersample(df_train, 'hazardous')
df_val_undersampled: DataFrame = undersample(df_val, 'hazardous')
df_test_undersampled: DataFrame = undersample(df_test, 'hazardous')

# Проверка сбалансированности
print('После применения метода undersampling:')
check_balance(df_train_undersampled, 'Обучающая выборка', 'hazardous')
check_balance(df_val_undersampled, 'Контрольная выборка', 'hazardous')
check_balance(df_test_undersampled, 'Тестовая выборка', 'hazardous')

# Проверка необходимости аугментации
print(f"Для обучающей выборки аугментация данных {'не ' if not need_augmentation(df_train_undersampled, 'hazardous', True, False) else ''}требуется")
print(f"Для контрольной выборки аугментация данных {'не ' if not need_augmentation(df_val_undersampled, 'hazardous', True, False) else ''}требуется")
print(f"Для тестовой выборки аугментация данных {'не ' if not need_augmentation(df_test_undersampled, 'hazardous', True, False) else ''}требуется")
   
# Визуализация сбалансированности классов
visualize_balance(df_train_undersampled, df_val_undersampled, df_test_undersampled, 'hazardous')
После применения метода undersampling:
Обучающая выборка:  (10608, 6)
Распределение выборки данных по классам "hazardous":
 hazardous
False    5304
True     5304
Name: count, dtype: int64
Процент объектов класса "False": 50.00%
Процент объектов класса "True": 50.00%

Контрольная выборка:  (3536, 6)
Распределение выборки данных по классам "hazardous":
 hazardous
False    1768
True     1768
Name: count, dtype: int64
Процент объектов класса "False": 50.00%
Процент объектов класса "True": 50.00%

Тестовая выборка:  (3536, 6)
Распределение выборки данных по классам "hazardous":
 hazardous
False    1768
True     1768
Name: count, dtype: int64
Процент объектов класса "False": 50.00%
Процент объектов класса "True": 50.00%

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