801 lines
210 KiB
Plaintext
801 lines
210 KiB
Plaintext
|
{
|
|||
|
"cells": [
|
|||
|
{
|
|||
|
"cell_type": "markdown",
|
|||
|
"metadata": {},
|
|||
|
"source": [
|
|||
|
"## Датасет №1: [Объекты вокруг Земли](https://www.kaggle.com/datasets/sameepvani/nasa-nearest-earth-objects).\n",
|
|||
|
"\n",
|
|||
|
"### Описание датасета:\n",
|
|||
|
"Данный набор данных представляет собой коллекцию сведений о ближайших к Земле объектах (астероидах), сертифицированных NASA. Он содержит данные, которые могут помочь идентифицировать потенциально опасные астероиды, которые могут оказать влияние на Землю или на космические миссии. Набор данных включает в себя такие ключевые характеристики астероидов, как их размер, скорость, расстояние до Земли и информация о возможной опасности столкновения.\n",
|
|||
|
"\n",
|
|||
|
"---\n",
|
|||
|
"\n",
|
|||
|
"### Анализ сведений:\n",
|
|||
|
"**Проблемная область:**\n",
|
|||
|
"Основной проблемной областью является отслеживание и оценка рисков, связанных с приближением астероидов к Земле. С помощью данных о движении и характеристиках астероидов можно предсказать возможные столкновения и минимизировать угрозу для Земли, планируя превентивные действия.\n",
|
|||
|
"\n",
|
|||
|
"**Актуальность:**\n",
|
|||
|
"Набор данных высокоактуален для задач оценки рисков от космических объектов, мониторинга космического пространства и разработки превентивных мер по защите Земли. Также он важен для научных исследований в области астрономии и планетарной безопасности.\n",
|
|||
|
"\n",
|
|||
|
"**Объекты наблюдения:**\n",
|
|||
|
"Объектами наблюдения в данном наборе данных являются астероиды, классифицированные NASA как \"ближайшие к Земле объекты\" (Near-Earth Objects, NEO). Эти объекты могут проходить в непосредственной близости от Земли, что потенциально представляет опасность.\n",
|
|||
|
"\n",
|
|||
|
"**Атрибуты объектов:**\n",
|
|||
|
"- id: Уникальный идентификатор астероида.\n",
|
|||
|
"- name: Название, присвоенное астероиду NASA.\n",
|
|||
|
"- est_diameter_min: Минимальный оценочные диаметры астероида в километрах.\n",
|
|||
|
"- est_diameter_max: Максимальный оценочные диаметры астероида в километрах.\n",
|
|||
|
"- relative_velocity: Скорость астероида относительно Земли (в км/с).\n",
|
|||
|
"- miss_distance: Расстояние, на котором астероид пролетел мимо Земли, в километрах.\n",
|
|||
|
"- orbiting_body: Планета, вокруг которой вращается астероид.\n",
|
|||
|
"- sentry_object: Признак, указывающий на наличие астероида в системе автоматического мониторинга столкновений (система Sentry).\n",
|
|||
|
"- absolute_magnitude: Абсолютная величина, описывающая яркость объекта.\n",
|
|||
|
"- hazardous: Булев признак, указывающий, является ли астероид потенциально опасным.\n",
|
|||
|
"\n",
|
|||
|
"**Связь между объектами:**\n",
|
|||
|
"В данном наборе данных отсутствует явная связь между астероидами, однако на основе орбитальных параметров можно исследовать группы объектов, имеющие схожие орбиты или величины риска столкновения с Землей.\n",
|
|||
|
"\n",
|
|||
|
"---\n",
|
|||
|
"\n",
|
|||
|
"### Качество набора данных:\n",
|
|||
|
"**Информативность:**\n",
|
|||
|
"Датасет предоставляет важные сведения о ключевых характеристиках астероидов, такие как размер, скорость и расстояние от Земли, что позволяет проводить качественный анализ их потенциальной опасности.\n",
|
|||
|
"\n",
|
|||
|
"**Степень покрытия:**\n",
|
|||
|
"Набор данных включает данные о большом количестве астероидов (>90000 записей), что позволяет охватить значительную часть ближайших к Земле объектов. Однако не все астероиды могут быть обнаружены, так как данные зависят от возможности их наблюдения.\n",
|
|||
|
"\n",
|
|||
|
"**Соответствие реальным данным:**\n",
|
|||
|
"Данные в наборе предоставлены NASA, что указывает на высокую достоверность и актуальность информации. Тем не менее, параметры, такие как диаметр и расстояние, могут быть оценочными и подвергаться уточнению с новыми наблюдениями.\n",
|
|||
|
"\n",
|
|||
|
"**Согласованность меток:**\n",
|
|||
|
"Метрики в датасете четко обозначены, а булевы признаки, такие как \"hazardous\" (опасен или нет), соответствуют конкретным параметрам астероидов и легко интерпретируются.\n",
|
|||
|
"\n",
|
|||
|
"---\n",
|
|||
|
"\n",
|
|||
|
"### Бизес-цели:\n",
|
|||
|
"1. **Мониторинг космических угроз:**\n",
|
|||
|
"Создание системы, которая анализирует астероиды и предсказывает риски столкновения с Землей, помогая государственным агентствам и частным компаниям разрабатывать превентивные меры.\n",
|
|||
|
"2. **Поддержка космических миссий:**\n",
|
|||
|
"Предоставление точных данных для планирования и безопасного проведения космических миссий, минимизация рисков столкновения с космическими объектами.\n",
|
|||
|
"3. **Образовательные и научные исследования:**\n",
|
|||
|
"Использование данных для поддержки образовательных программ и научных исследований в области астрономии и космической безопасности.\n",
|
|||
|
"\n",
|
|||
|
"**Эффект для бизнеса:**\n",
|
|||
|
"Набор данных способствует развитию технологий космической безопасности, минимизирует финансовые риски от потенциальных катастроф и поддерживает стратегическое планирование космических миссий.\n",
|
|||
|
"\n",
|
|||
|
"---\n",
|
|||
|
"\n",
|
|||
|
"### Технические цели:\n",
|
|||
|
"1. **Моделирование риска столкновения:**\n",
|
|||
|
"Построение алгоритмов машинного обучения для прогнозирования вероятности столкновения астероидов с Землей.\n",
|
|||
|
"2. **Анализ и кластеризация астероидов:**\n",
|
|||
|
"Исследование взаимосвязей между астероидами, анализ орбитальных данных и выделение групп астероидов, имеющих схожие характеристики.\n",
|
|||
|
"3. **Оптимизация системы предупреждения угроз:**\n",
|
|||
|
"Создание системы раннего оповещения, которая будет автоматически анализировать данные и предупреждать о потенциальных угрозах в реальном времени.\n",
|
|||
|
"\n",
|
|||
|
"**Входные данные:**\n",
|
|||
|
"Диаметр, скорость, расстояние, орбитальные параметры астероидов.\n",
|
|||
|
"\n",
|
|||
|
"**Целевой признак:**\n",
|
|||
|
"Признак \"hazardous\" – бинарная метка, указывающая на потенциальную опасность астероида."
|
|||
|
]
|
|||
|
},
|
|||
|
{
|
|||
|
"cell_type": "markdown",
|
|||
|
"metadata": {},
|
|||
|
"source": [
|
|||
|
"### Выгрузка данных из файла в DataFrame:"
|
|||
|
]
|
|||
|
},
|
|||
|
{
|
|||
|
"cell_type": "code",
|
|||
|
"execution_count": 1,
|
|||
|
"metadata": {},
|
|||
|
"outputs": [],
|
|||
|
"source": [
|
|||
|
"from typing import Any\n",
|
|||
|
"\n",
|
|||
|
"import pandas as pd\n",
|
|||
|
"from pandas import DataFrame, Series\n",
|
|||
|
"from sklearn.model_selection import train_test_split\n",
|
|||
|
"from imblearn.over_sampling import ADASYN\n",
|
|||
|
"from imblearn.under_sampling import RandomUnderSampler\n",
|
|||
|
"import matplotlib.pyplot as plt\n",
|
|||
|
"\n",
|
|||
|
"\n",
|
|||
|
"df: DataFrame = pd.read_csv('..//static//csv//neo.csv')"
|
|||
|
]
|
|||
|
},
|
|||
|
{
|
|||
|
"cell_type": "markdown",
|
|||
|
"metadata": {},
|
|||
|
"source": [
|
|||
|
"### Краткая информация о DataFrame:"
|
|||
|
]
|
|||
|
},
|
|||
|
{
|
|||
|
"cell_type": "code",
|
|||
|
"execution_count": 2,
|
|||
|
"metadata": {},
|
|||
|
"outputs": [
|
|||
|
{
|
|||
|
"name": "stdout",
|
|||
|
"output_type": "stream",
|
|||
|
"text": [
|
|||
|
"<class 'pandas.core.frame.DataFrame'>\n",
|
|||
|
"RangeIndex: 90836 entries, 0 to 90835\n",
|
|||
|
"Data columns (total 10 columns):\n",
|
|||
|
" # Column Non-Null Count Dtype \n",
|
|||
|
"--- ------ -------------- ----- \n",
|
|||
|
" 0 id 90836 non-null int64 \n",
|
|||
|
" 1 name 90836 non-null object \n",
|
|||
|
" 2 est_diameter_min 90836 non-null float64\n",
|
|||
|
" 3 est_diameter_max 90836 non-null float64\n",
|
|||
|
" 4 relative_velocity 90836 non-null float64\n",
|
|||
|
" 5 miss_distance 90836 non-null float64\n",
|
|||
|
" 6 orbiting_body 90836 non-null object \n",
|
|||
|
" 7 sentry_object 90836 non-null bool \n",
|
|||
|
" 8 absolute_magnitude 90836 non-null float64\n",
|
|||
|
" 9 hazardous 90836 non-null bool \n",
|
|||
|
"dtypes: bool(2), float64(5), int64(1), object(2)\n",
|
|||
|
"memory usage: 5.7+ MB\n",
|
|||
|
"\n",
|
|||
|
" count mean std min \\\n",
|
|||
|
"id 90836.0 1.438288e+07 2.087202e+07 2.000433e+06 \n",
|
|||
|
"est_diameter_min 90836.0 1.274321e-01 2.985112e-01 6.089126e-04 \n",
|
|||
|
"est_diameter_max 90836.0 2.849469e-01 6.674914e-01 1.361570e-03 \n",
|
|||
|
"relative_velocity 90836.0 4.806692e+04 2.529330e+04 2.033464e+02 \n",
|
|||
|
"miss_distance 90836.0 3.706655e+07 2.235204e+07 6.745533e+03 \n",
|
|||
|
"absolute_magnitude 90836.0 2.352710e+01 2.894086e+00 9.230000e+00 \n",
|
|||
|
"\n",
|
|||
|
" 25% 50% 75% max \n",
|
|||
|
"id 3.448110e+06 3.748362e+06 3.884023e+06 5.427591e+07 \n",
|
|||
|
"est_diameter_min 1.925551e-02 4.836765e-02 1.434019e-01 3.789265e+01 \n",
|
|||
|
"est_diameter_max 4.305662e-02 1.081534e-01 3.206564e-01 8.473054e+01 \n",
|
|||
|
"relative_velocity 2.861902e+04 4.419012e+04 6.292360e+04 2.369901e+05 \n",
|
|||
|
"miss_distance 1.721082e+07 3.784658e+07 5.654900e+07 7.479865e+07 \n",
|
|||
|
"absolute_magnitude 2.134000e+01 2.370000e+01 2.570000e+01 3.320000e+01 \n"
|
|||
|
]
|
|||
|
}
|
|||
|
],
|
|||
|
"source": [
|
|||
|
"# Краткая информация о DataFrame\n",
|
|||
|
"df.info()\n",
|
|||
|
"\n",
|
|||
|
"# Статистическое описание числовых столбцов\n",
|
|||
|
"print('\\n', df.describe().transpose())"
|
|||
|
]
|
|||
|
},
|
|||
|
{
|
|||
|
"cell_type": "markdown",
|
|||
|
"metadata": {},
|
|||
|
"source": [
|
|||
|
"### Проблема пропущенных данных:\n",
|
|||
|
"\n",
|
|||
|
"**Проблема пропущенных данных** — это отсутствие значений в наборе данных, что может искажать результаты анализа и статистические выводы.\n",
|
|||
|
"\n",
|
|||
|
"Проверка на отсутствие значений, представленная ниже, показала, что DataFrame не имеет пустых значений признаков. Нет необходимости использовать методы заполнения пропущенных данных."
|
|||
|
]
|
|||
|
},
|
|||
|
{
|
|||
|
"cell_type": "code",
|
|||
|
"execution_count": 3,
|
|||
|
"metadata": {},
|
|||
|
"outputs": [
|
|||
|
{
|
|||
|
"name": "stdout",
|
|||
|
"output_type": "stream",
|
|||
|
"text": [
|
|||
|
"id False\n",
|
|||
|
"name False\n",
|
|||
|
"est_diameter_min False\n",
|
|||
|
"est_diameter_max False\n",
|
|||
|
"relative_velocity False\n",
|
|||
|
"miss_distance False\n",
|
|||
|
"orbiting_body False\n",
|
|||
|
"sentry_object False\n",
|
|||
|
"absolute_magnitude False\n",
|
|||
|
"hazardous False\n",
|
|||
|
"dtype: bool \n",
|
|||
|
"\n",
|
|||
|
"id 0\n",
|
|||
|
"name 0\n",
|
|||
|
"est_diameter_min 0\n",
|
|||
|
"est_diameter_max 0\n",
|
|||
|
"relative_velocity 0\n",
|
|||
|
"miss_distance 0\n",
|
|||
|
"orbiting_body 0\n",
|
|||
|
"sentry_object 0\n",
|
|||
|
"absolute_magnitude 0\n",
|
|||
|
"hazardous 0\n",
|
|||
|
"dtype: int64\n"
|
|||
|
]
|
|||
|
}
|
|||
|
],
|
|||
|
"source": [
|
|||
|
"# Проверка пропущенных данных\n",
|
|||
|
"def check_null_columns(dataframe: DataFrame) -> None:\n",
|
|||
|
" # Присутствуют ли пустые значения признаков\n",
|
|||
|
" print(dataframe.isnull().any(), '\\n')\n",
|
|||
|
"\n",
|
|||
|
" # Количество пустых значений признаков\n",
|
|||
|
" print(dataframe.isnull().sum())\n",
|
|||
|
"\n",
|
|||
|
" # Процент пустых значений признаков\n",
|
|||
|
" for i in dataframe.columns:\n",
|
|||
|
" null_rate: float = dataframe[i].isnull().sum() / len(dataframe) * 100\n",
|
|||
|
" if null_rate > 0:\n",
|
|||
|
" print(f\"{i} процент пустых значений: %{null_rate:.2f}\")\n",
|
|||
|
" \n",
|
|||
|
"\n",
|
|||
|
"# Проверка пропущенных данных\n",
|
|||
|
"check_null_columns(df)"
|
|||
|
]
|
|||
|
},
|
|||
|
{
|
|||
|
"cell_type": "markdown",
|
|||
|
"metadata": {},
|
|||
|
"source": [
|
|||
|
"### Проблема зашумленности данных:\n",
|
|||
|
"\n",
|
|||
|
"**Зашумленность** – это наличие случайных ошибок или вариаций в данных, которые могут затруднить выявление истинных закономерностей. Шум может возникать из-за ошибок измерений, неправильных записей или других факторов.\n",
|
|||
|
"\n",
|
|||
|
"**Выбросы** – это значения, которые значительно отличаются от остальных наблюдений в наборе данных. Выбросы могут указывать на ошибки в данных или на редкие, но важные события. Их наличие может повлиять на статистические методы анализа.\n",
|
|||
|
"\n",
|
|||
|
"Представленный ниже код помогает определить наличие выбросов в наборе данных и устранить их (при наличии), заменив значения ниже нижней границы (рассматриваемого минимума) на значения нижней границы, а значения выше верхней границы (рассматриваемого максимума) – на значения верхней границы."
|
|||
|
]
|
|||
|
},
|
|||
|
{
|
|||
|
"cell_type": "code",
|
|||
|
"execution_count": 4,
|
|||
|
"metadata": {},
|
|||
|
"outputs": [
|
|||
|
{
|
|||
|
"name": "stdout",
|
|||
|
"output_type": "stream",
|
|||
|
"text": [
|
|||
|
"Колонка est_diameter_min:\n",
|
|||
|
"\tЕсть выбросы: Да\n",
|
|||
|
"\tКоличество выбросов: 8306\n",
|
|||
|
"\tМинимальное значение: 0.0006089126\n",
|
|||
|
"\tМаксимальное значение: 0.32962154705\n",
|
|||
|
"\t1-й квартиль (Q1): 0.0192555078\n",
|
|||
|
"\t3-й квартиль (Q3): 0.1434019235\n",
|
|||
|
"\n",
|
|||
|
"Колонка est_diameter_max:\n",
|
|||
|
"\tЕсть выбросы: Да\n",
|
|||
|
"\tКоличество выбросов: 8306\n",
|
|||
|
"\tМинимальное значение: 0.00136157\n",
|
|||
|
"\tМаксимальное значение: 0.7370561859\n",
|
|||
|
"\t1-й квартиль (Q1): 0.0430566244\n",
|
|||
|
"\t3-й квартиль (Q3): 0.320656449\n",
|
|||
|
"\n",
|
|||
|
"Колонка relative_velocity:\n",
|
|||
|
"\tЕсть выбросы: Да\n",
|
|||
|
"\tКоличество выбросов: 1574\n",
|
|||
|
"\tМинимальное значение: 203.34643253\n",
|
|||
|
"\tМаксимальное значение: 114380.48061454494\n",
|
|||
|
"\t1-й квартиль (Q1): 28619.02064490995\n",
|
|||
|
"\t3-й квартиль (Q3): 62923.60463276395\n",
|
|||
|
"\n",
|
|||
|
"Колонка miss_distance:\n",
|
|||
|
"\tЕсть выбросы: Нет\n",
|
|||
|
"\tКоличество выбросов: 0\n",
|
|||
|
"\tМинимальное значение: 6745.532515957\n",
|
|||
|
"\tМаксимальное значение: 74798651.4521972\n",
|
|||
|
"\t1-й квартиль (Q1): 17210820.23576468\n",
|
|||
|
"\t3-й квартиль (Q3): 56548996.45139917\n",
|
|||
|
"\n",
|
|||
|
"Колонка absolute_magnitude:\n",
|
|||
|
"\tЕсть выбросы: Да\n",
|
|||
|
"\tКоличество выбросов: 101\n",
|
|||
|
"\tМинимальное значение: 14.8\n",
|
|||
|
"\tМаксимальное значение: 32.239999999999995\n",
|
|||
|
"\t1-й квартиль (Q1): 21.34\n",
|
|||
|
"\t3-й квартиль (Q3): 25.7\n",
|
|||
|
"\n"
|
|||
|
]
|
|||
|
}
|
|||
|
],
|
|||
|
"source": [
|
|||
|
"# Числовые столбцы DataFrame\n",
|
|||
|
"numeric_columns: list[str] = [\n",
|
|||
|
" 'est_diameter_min',\n",
|
|||
|
" 'est_diameter_max', \n",
|
|||
|
" 'relative_velocity', \n",
|
|||
|
" 'miss_distance', \n",
|
|||
|
" 'absolute_magnitude'\n",
|
|||
|
"]\n",
|
|||
|
"\n",
|
|||
|
"# Проверка выбросов в DataFrame\n",
|
|||
|
"def check_outliers(dataframe: DataFrame, columns: list[str]) -> DataFrame:\n",
|
|||
|
" for column in columns:\n",
|
|||
|
" if not pd.api.types.is_numeric_dtype(dataframe[column]): # Проверяем, является ли колонка числовой\n",
|
|||
|
" continue\n",
|
|||
|
" \n",
|
|||
|
" Q1: float = dataframe[column].quantile(0.25) # 1-й квартиль (25%)\n",
|
|||
|
" Q3: float = dataframe[column].quantile(0.75) # 3-й квартиль (75%)\n",
|
|||
|
" IQR: float = Q3 - Q1 # Вычисляем межквартильный размах\n",
|
|||
|
"\n",
|
|||
|
" # Определяем границы для выбросов\n",
|
|||
|
" lower_bound: float = Q1 - 1.5 * IQR # Нижняя граница\n",
|
|||
|
" upper_bound: float = Q3 + 1.5 * IQR # Верхняя граница\n",
|
|||
|
"\n",
|
|||
|
" # Подсчитываем количество выбросов\n",
|
|||
|
" outliers: DataFrame = dataframe[(dataframe[column] < lower_bound) | (dataframe[column] > upper_bound)]\n",
|
|||
|
" outlier_count: int = outliers.shape[0]\n",
|
|||
|
"\n",
|
|||
|
" # Устраняем выбросы:\n",
|
|||
|
" # Заменяем значения ниже нижней границы на нижнюю границу\n",
|
|||
|
" # А значения выше верхней границы – на верхнюю\n",
|
|||
|
" dataframe[column] = dataframe[column].apply(lambda x: lower_bound if x < lower_bound else upper_bound if x > upper_bound else x)\n",
|
|||
|
"\n",
|
|||
|
" print(f\"Колонка {column}:\")\n",
|
|||
|
" print(f\"\\tЕсть выбросы: {'Да' if outlier_count > 0 else 'Нет'}\")\n",
|
|||
|
" print(f\"\\tКоличество выбросов: {outlier_count}\")\n",
|
|||
|
" print(f\"\\tМинимальное значение: {dataframe[column].min()}\")\n",
|
|||
|
" print(f\"\\tМаксимальное значение: {dataframe[column].max()}\")\n",
|
|||
|
" print(f\"\\t1-й квартиль (Q1): {Q1}\")\n",
|
|||
|
" print(f\"\\t3-й квартиль (Q3): {Q3}\\n\")\n",
|
|||
|
" \n",
|
|||
|
" return dataframe\n",
|
|||
|
"\n",
|
|||
|
"\n",
|
|||
|
"# Проверка выбросов\n",
|
|||
|
"df: DataFrame = check_outliers(df, numeric_columns)"
|
|||
|
]
|
|||
|
},
|
|||
|
{
|
|||
|
"cell_type": "markdown",
|
|||
|
"metadata": {},
|
|||
|
"source": [
|
|||
|
"### Разбиение набора данных на выборки:\n",
|
|||
|
"\n",
|
|||
|
"**Групповое разбиение данных** – это метод разделения данных на несколько групп или подмножеств на основе определенного признака или характеристики. При этом наблюдения для одного объекта должны попасть только в одну выборку.\n",
|
|||
|
"\n",
|
|||
|
"**Основные виды выборки данных**:\n",
|
|||
|
"1. Обучающая выборка (60-80%). Обучение модели (подбор коэффициентов некоторой математической функции для аппроксимации).\n",
|
|||
|
"2. Контрольная выборка (10-20%). Выбор метода обучения, настройка гиперпараметров.\n",
|
|||
|
"3. Тестовая выборка (10-20% или 20-30%). Оценка качества модели перед передачей заказчику.\n",
|
|||
|
"\n",
|
|||
|
"Разделим выборку данных на 3 группы и проанализируем качество распределения данных.\n",
|
|||
|
"\n",
|
|||
|
"Весь набор данных состоит из 90836 объектов, из которых 81996 (около 90.3%) неопасны (False), а 8840 (около 9.7%) опасны (True). Это говорит о том, что класс \"неопасные\" значительно преобладает.\n",
|
|||
|
"\n",
|
|||
|
"Все выборки показывают одинаковое распределение классов, что свидетельствует о том, что данные были отобраны случайным образом и не содержат явного смещения.\n",
|
|||
|
"\n",
|
|||
|
"Однако, несмотря на сбалансированность при разбиении данных, в целом данные обладают значительным дисбалансом между классами. Это может быть проблемой при обучении модели, так как она может иметь тенденцию игнорировать опасные объекты (True), что следует учитывать при дальнейшем анализе и выборе методов обработки данных.\n",
|
|||
|
"\n",
|
|||
|
"Для получения более сбалансированных выборок данных необходимо воспользоваться методами приращения (аугментации) данных, а именно методами oversampling и undersampling."
|
|||
|
]
|
|||
|
},
|
|||
|
{
|
|||
|
"cell_type": "code",
|
|||
|
"execution_count": 5,
|
|||
|
"metadata": {},
|
|||
|
"outputs": [],
|
|||
|
"source": [
|
|||
|
"# Функция для создания выборок\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[Any, Any, Any]:\n",
|
|||
|
" \"\"\"\n",
|
|||
|
" Splits a Pandas dataframe into three subsets (train, val, and test)\n",
|
|||
|
" following fractional ratios provided by the user, where each subset is\n",
|
|||
|
" stratified by the values in a specific column (that is, each subset has\n",
|
|||
|
" the same relative frequency of the values in the column). It performs this\n",
|
|||
|
" splitting by running train_test_split() twice.\n",
|
|||
|
"\n",
|
|||
|
" Parameters\n",
|
|||
|
" ----------\n",
|
|||
|
" df_input : Pandas dataframe\n",
|
|||
|
" Input dataframe to be split.\n",
|
|||
|
" stratify_colname : str\n",
|
|||
|
" The name of the column that will be used for stratification. Usually\n",
|
|||
|
" this column would be for the label.\n",
|
|||
|
" frac_train : float\n",
|
|||
|
" frac_val : float\n",
|
|||
|
" frac_test : float\n",
|
|||
|
" The ratios with which the dataframe will be split into train, val, and\n",
|
|||
|
" test data. The values should be expressed as float fractions and should\n",
|
|||
|
" sum to 1.0.\n",
|
|||
|
" random_state : int, None, or RandomStateInstance\n",
|
|||
|
" Value to be passed to train_test_split().\n",
|
|||
|
"\n",
|
|||
|
" Returns\n",
|
|||
|
" -------\n",
|
|||
|
" df_train, df_val, df_test :\n",
|
|||
|
" Dataframes containing the three splits.\n",
|
|||
|
" \"\"\"\n",
|
|||
|
"\n",
|
|||
|
" if frac_train + frac_val + frac_test != 1.0:\n",
|
|||
|
" raise ValueError(\n",
|
|||
|
" \"fractions %f, %f, %f do not add up to 1.0\"\n",
|
|||
|
" % (frac_train, frac_val, frac_test)\n",
|
|||
|
" )\n",
|
|||
|
"\n",
|
|||
|
" if stratify_colname not in df_input.columns:\n",
|
|||
|
" raise ValueError(\"%s is not a column in the dataframe\" % (stratify_colname))\n",
|
|||
|
"\n",
|
|||
|
" X: DataFrame = df_input # Contains all columns.\n",
|
|||
|
" y: DataFrame = df_input[\n",
|
|||
|
" [stratify_colname]\n",
|
|||
|
" ] # Dataframe of just the column on which to stratify.\n",
|
|||
|
"\n",
|
|||
|
" # Split original dataframe into train and temp dataframes.\n",
|
|||
|
" df_train, df_temp, y_train, y_temp = train_test_split(\n",
|
|||
|
" X, y, \n",
|
|||
|
" stratify=y, \n",
|
|||
|
" test_size=(1.0 - frac_train), \n",
|
|||
|
" random_state=random_state\n",
|
|||
|
" )\n",
|
|||
|
"\n",
|
|||
|
" # Split the temp dataframe into val and test dataframes.\n",
|
|||
|
" relative_frac_test: float = frac_test / (frac_val + frac_test)\n",
|
|||
|
" df_val, df_test, y_val, y_test = train_test_split(\n",
|
|||
|
" df_temp,\n",
|
|||
|
" y_temp,\n",
|
|||
|
" stratify=y_temp,\n",
|
|||
|
" test_size=relative_frac_test,\n",
|
|||
|
" random_state=random_state,\n",
|
|||
|
" )\n",
|
|||
|
"\n",
|
|||
|
" assert len(df_input) == len(df_train) + len(df_val) + len(df_test)\n",
|
|||
|
"\n",
|
|||
|
" return df_train, df_val, df_test"
|
|||
|
]
|
|||
|
},
|
|||
|
{
|
|||
|
"cell_type": "code",
|
|||
|
"execution_count": 6,
|
|||
|
"metadata": {},
|
|||
|
"outputs": [
|
|||
|
{
|
|||
|
"name": "stdout",
|
|||
|
"output_type": "stream",
|
|||
|
"text": [
|
|||
|
"hazardous\n",
|
|||
|
"False 81996\n",
|
|||
|
"True 8840\n",
|
|||
|
"Name: count, dtype: int64 \n",
|
|||
|
"\n",
|
|||
|
"Обучающая выборка: (54501, 6)\n",
|
|||
|
"Распределение выборки данных по классам \"hazardous\":\n",
|
|||
|
" hazardous\n",
|
|||
|
"False 49197\n",
|
|||
|
"True 5304\n",
|
|||
|
"Name: count, dtype: int64\n",
|
|||
|
"Процент объектов класса \"False\": 90.27%\n",
|
|||
|
"Процент объектов класса \"True\": 9.73%\n",
|
|||
|
"\n",
|
|||
|
"Контрольная выборка: (18167, 6)\n",
|
|||
|
"Распределение выборки данных по классам \"hazardous\":\n",
|
|||
|
" hazardous\n",
|
|||
|
"False 16399\n",
|
|||
|
"True 1768\n",
|
|||
|
"Name: count, dtype: int64\n",
|
|||
|
"Процент объектов класса \"False\": 90.27%\n",
|
|||
|
"Процент объектов класса \"True\": 9.73%\n",
|
|||
|
"\n",
|
|||
|
"Тестовая выборка: (18168, 6)\n",
|
|||
|
"Распределение выборки данных по классам \"hazardous\":\n",
|
|||
|
" hazardous\n",
|
|||
|
"False 16400\n",
|
|||
|
"True 1768\n",
|
|||
|
"Name: count, dtype: int64\n",
|
|||
|
"Процент объектов класса \"False\": 90.27%\n",
|
|||
|
"Процент объектов класса \"True\": 9.73%\n",
|
|||
|
"\n",
|
|||
|
"Для обучающей выборки аугментация данных требуется\n",
|
|||
|
"Для контрольной выборки аугментация данных требуется\n",
|
|||
|
"Для тестовой выборки аугментация данных требуется\n"
|
|||
|
]
|
|||
|
},
|
|||
|
{
|
|||
|
"data": {
|
|||
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAABgcAAAH/CAYAAABzUQ1QAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAACenklEQVR4nOzdd3xT9f7H8Xe6S8sqZRZk7yGKgIAIKg7An1vEcRkuFATFdR1XARduRdxeBRSuA72CEwUVAQUBBWTILnu27O425/dHb2NDd5v0e5Lzej4ePJSQJp9mnFfa78mJy7IsSwAAAAAAAAAAwDFCTA8AAAAAAAAAAAAqF4sDAAAAAAAAAAA4DIsDAAAAAAAAAAA4DIsDAAAAAAAAAAA4DIsDAAAAAAAAAAA4DIsDAAAAAAAAAAA4DIsDAAAAAAAAAAA4DIsDAAAAAAAAAAA4DIsDABAAjhw5os2bNys7O9v0KPAhy7J06NAhbdq0yfQoAACggjIzM7Vv3z7t2bPH9CgAAMCHUlJStHPnTh0+fNj0KD7H4gAQAJo0aaKLL77Y9Bg+07dvX/Xt29f0GLaWlZWlZ599VqeeeqoiIyNVs2ZNtWzZUj/88IPp0QLCmjVrNGvWLM/fV65cqa+//trcQPkcP35c//rXv9S6dWtFRESoVq1aatWqlTZs2GB6NADlQKMB/5s+fbq2bdvm+fvUqVO1e/ducwPls3z5cl133XWKj49XZGSk6tevryuvvNL0WIDj0GMgMD311FNyu92SJLfbrYkTJxqe6G8zZ87Ueeedp6pVqyo2NlannHKKnn32WdNj+VyZFgemTp0ql8vl+RMVFaVWrVrpjjvu0P79+/01IxD0xo8fryZNmkj6+3kG/+vbt6+GDRsmSRo2bJhtXnxlZGSoX79+euSRR9S3b1/NnDlTc+fO1Y8//qgePXqYHi8gHD9+XCNGjNCSJUu0adMm3XnnnVq9erXpsZScnKwePXrolVde0VVXXaXZs2dr7ty5mj9/vmcbUF40GvAPGm2GXRsNMxYuXKj7779f27Zt03fffadRo0YpJMT8fm6zZ8/WWWedpXXr1unJJ5/U3Llzde+992rJkiX0GPAxemwGPYa/TZs2Tc8//7x27dqlF154QdOmTTM9kiTpgQce0KBBg1S1alW98847mjt3rubNm6eRI0eaHs3nwsrzRY899piaNm2q9PR0LVq0SG+88Ya++eYbrVmzRlWqVPH1jADgKM8884x+++03fffdd7z4KqcePXp4/khSq1atdMsttxieSrrvvvu0d+9eLV68WO3bt/fLddBoAECwGTt2rPr27aumTZtKku6++27Vr1/f6EyHDh3SzTffrAsvvFAzZ85URESEJGnXrl2S6DEAAKXx2GOPaciQIfrnP/+pyMhITZ8+3fRI+vnnn/XMM89o4sSJeuCBB0yP43flWhzo37+/zjjjDEnSzTffrFq1aunFF1/U7Nmzde211/p0QACBJyUlRTExMabHCEjZ2dl6+eWXdc8997AwUEGzZs3SunXrlJaWpo4dO3p+aDflwIEDmjZtmt58802/LQxINBpA8Wg0AlGbNm20ZcsWrVmzRvHx8WrevLnpkTRlyhSlp6dr6tSphb7GoMcAikOPgVzXXHONzjnnHG3evFktW7ZU7dq1TY+k559/Xj179nTEwoDko88cOPfccyVJiYmJknL3orj33nvVsWNHxcbGqlq1aurfv79WrVpV4GvT09M1fvx4tWrVSlFRUapfv76uuOIKbdmyRZK0bds2r8MknPwn/y/P5s+fL5fLpY8//lgPPfSQ6tWrp5iYGF1yySXauXNngev+7bffdNFFF6l69eqqUqWK+vTpo19++aXQ77Fv376FXv/48eMLnHf69Onq0qWLoqOjFRcXp8GDBxd6/cV9b/m53W69/PLLat++vaKiolS3bl2NGDGiwIdgFHWMvTvuuKPAZRY2+3PPPVfgNpVyD3Eybtw4tWjRQpGRkWrUqJHuv/9+ZWRkFHpb5VfYcfKefPJJhYSE6D//+U+5bo+8J2mtWrUUHR2tLl266NNPPy30+qdPn65u3bqpSpUqqlmzps4++2x9//33Xuf59ttv1adPH1WtWlXVqlVT165dC8w2c+ZMz30aHx+vG264ocBxTocNG+Y1c82aNdW3b18tXLiwxNuptBYtWqRu3bopKipKzZo10/vvv+/176V97jVp0qTI23r+/PmSpO3bt2vkyJFq3bq1oqOjVatWLV199dVex3uV/n5L588//6yRI0eqTp06atiwoeff3377bTVv3lzR0dHq1q1bkbfHgQMHdNNNN6lu3bqKiorSqaeeWuDtZHnP8bwZ8+Q9dqZOneo5bd++fRo+fLgaNmzoOf7rpZdeWmD+ssp/24WEhKhevXq65pprtGPHjlJ9/euvv6727dsrMjJSDRo00KhRo3TkyBHPv2/YsEGHDx9W1apV1adPH1WpUkXVq1fXxRdfrDVr1njO99NPP8nlcunzzz8vcB3/+c9/5HK5tHjxYs/MeW8FzVPYbblw4UJdffXVOuWUUzzP9bFjxyotLc3ra8ePH1/geTljxgx17txZUVFRqlWrlq699toCt8mwYcMUGxvrddqnn35a6H0aGxtbYGapdNvX/Nuddu3aqUuXLlq1alWh25PCnLy9j4+P18CBA71ufyl3O3rHHXcUeTl5z428x9yyZcvkdruVmZmpM844o9jbSpJ+/PFH9e7dWzExMapRo4YuvfRS/fXXX17nybsv1q9fr9dff12SdN555+nOO+9Uenq6pL8bfd1112n8+PGe7USHDh0UFhYml8ul3r17e20n8m7nqKgoRUdHq2rVqjSaRtPoEtDowG50Ya289dZbFRUVVeB7KqnlUu7zq0OHDgWu5/nnn/dqQ3H3t8vl8hxCI+92fP755/XSSy+pcePGio6OVp8+fQr0SSpdQwq73Qp7vOWdp6Rjeeef8WQdOnQosL0pzeMq/+MnJiZG3bt3V/PmzTVq1Ci5XK5CXysU9vV5f8LDw9WkSRPdd999yszM9Jwv77myfPnyIi/r5G3mkiVL1LlzZz311FNq1KiRIiMj1bJly0I/4yg7O9tzSKF//OMfatKkicaOHauxY8d6bROio6M928K811Xt2rXTRx995PUzc40aNTzP7cK+z5P/NGzY0DNjQkKCXC6XPvzwQ3qcDz2mx/TYfI/91cT169frqquuUlxcnKKionTGGWfoiy++8DpPUR1ISkoqsC0o7OfhEydOqF69egVu+7zXA7///rt69uyp6OhoNW3aVG+++WaBOcvSxZK6Jklbt27V1Vdfrbi4OFWpUkVnnnlmgUYV9r1IBV8XleX2kaQVK1aof//+qlatmmJjY3XeeedpyZIlXufJ//NynTp1PNusTp06FXisFubkw+pWqVJFHTt21L///W+v8xX2e4iTnfw9LFmyRB06dNDgwYMVFxen6Ohode3a1etzDfOU5X4rzWN22LBhBQ45PH36dIWEhOjpp5/2Or00j+3SKNc7B06W90uCWrVqScp9AM6aNUtXX321mjZtqv379+utt95Snz59tG7dOjVo0ECSlJOTo4svvlg//PCDBg8erDvvvFPHjx/X3LlztWbNGq89Qq699loNGDDA63offPDBQud58skn5XK59M9//lMHDhzQyy+/rH79+mnlypWKjo6WlPuCuX///urSpYvGjRunkJAQTZkyReeee64WLlyobt26Fbjchg0bej4Y48SJE7r99tsLve5HHnlEgwYN0s0336yDBw9q8uTJOvvss7VixQrVqFGjwNfceuut6t27tyTpv//9b4Ff9o0YMUJTp07V8OHDNWbMGCUmJurVV1/VihUr9Msvvyg8PLzQ26Esjhw5UuiHfrjdbl1yySVatGiRbr31VrVt21arV6/WSy+9pI0bNxb6xCjOlClT9K9//UsvvPCCrrvuukLPU9LtMWnSJF1yySW6/vrrlZmZqY8++khXX321vvrqKw0cONBzvgkTJmj8+PHq2bOnHnvsMUVEROi3337Tjz/+qAsuuEBS7sbkxhtvVPv27fX
|
|||
|
"text/plain": [
|
|||
|
"<Figure size 1500x500 with 3 Axes>"
|
|||
|
]
|
|||
|
},
|
|||
|
"metadata": {},
|
|||
|
"output_type": "display_data"
|
|||
|
}
|
|||
|
],
|
|||
|
"source": [
|
|||
|
"# Вывод распределения количества наблюдений по меткам (классам)\n",
|
|||
|
"print(df.hazardous.value_counts(), '\\n')\n",
|
|||
|
"\n",
|
|||
|
"data: DataFrame = df[[\n",
|
|||
|
" 'est_diameter_min', \n",
|
|||
|
" 'est_diameter_max', \n",
|
|||
|
" 'relative_velocity', \n",
|
|||
|
" 'miss_distance', \n",
|
|||
|
" 'absolute_magnitude', \n",
|
|||
|
" 'hazardous'\n",
|
|||
|
"]].copy()\n",
|
|||
|
"\n",
|
|||
|
"df_train, df_val, df_test = split_stratified_into_train_val_test(\n",
|
|||
|
" data, \n",
|
|||
|
" stratify_colname=\"hazardous\", \n",
|
|||
|
" frac_train=0.60, \n",
|
|||
|
" frac_val=0.20, \n",
|
|||
|
" frac_test=0.20\n",
|
|||
|
")\n",
|
|||
|
"\n",
|
|||
|
"# Оценка сбалансированности\n",
|
|||
|
"def check_balance(dataframe: DataFrame, dataframe_name: str, column: str) -> None:\n",
|
|||
|
" counts: Series[int] = dataframe[column].value_counts()\n",
|
|||
|
" print(dataframe_name + \": \", dataframe.shape)\n",
|
|||
|
" print(f\"Распределение выборки данных по классам \\\"{column}\\\":\\n\", counts)\n",
|
|||
|
" total_count: int = len(dataframe)\n",
|
|||
|
" for value in counts.index:\n",
|
|||
|
" percentage: float = counts[value] / total_count * 100\n",
|
|||
|
" print(f\"Процент объектов класса \\\"{value}\\\": {percentage:.2f}%\")\n",
|
|||
|
" print()\n",
|
|||
|
" \n",
|
|||
|
"# Определение необходимости аугментации данных\n",
|
|||
|
"def need_augmentation(dataframe: DataFrame,\n",
|
|||
|
" column: str, \n",
|
|||
|
" first_value: Any, second_value: Any) -> bool:\n",
|
|||
|
" counts: Series[int] = dataframe[column].value_counts()\n",
|
|||
|
" ratio: float = counts[first_value] / counts[second_value]\n",
|
|||
|
" return ratio > 1.5 or ratio < 0.67\n",
|
|||
|
" \n",
|
|||
|
" # Визуализация сбалансированности классов\n",
|
|||
|
"def visualize_balance(dataframe_train: DataFrame,\n",
|
|||
|
" dataframe_val: DataFrame,\n",
|
|||
|
" dataframe_test: DataFrame, \n",
|
|||
|
" column: str) -> None:\n",
|
|||
|
" fig, axes = plt.subplots(1, 3, figsize=(15, 5))\n",
|
|||
|
"\n",
|
|||
|
" # Обучающая выборка\n",
|
|||
|
" counts_train: Series[int] = dataframe_train[column].value_counts()\n",
|
|||
|
" axes[0].pie(counts_train, labels=counts_train.index, autopct='%1.1f%%', startangle=90)\n",
|
|||
|
" axes[0].set_title(f\"Распределение классов \\\"{column}\\\" в обучающей выборке\")\n",
|
|||
|
"\n",
|
|||
|
" # Контрольная выборка\n",
|
|||
|
" counts_val: Series[int] = dataframe_val[column].value_counts()\n",
|
|||
|
" axes[1].pie(counts_val, labels=counts_val.index, autopct='%1.1f%%', startangle=90)\n",
|
|||
|
" axes[1].set_title(f\"Распределение классов \\\"{column}\\\" в контрольной выборке\")\n",
|
|||
|
"\n",
|
|||
|
" # Тестовая выборка\n",
|
|||
|
" counts_test: Series[int] = dataframe_test[column].value_counts()\n",
|
|||
|
" axes[2].pie(counts_test, labels=counts_test.index, autopct='%1.1f%%', startangle=90)\n",
|
|||
|
" axes[2].set_title(f\"Распределение классов \\\"{column}\\\" в тренировочной выборке\")\n",
|
|||
|
"\n",
|
|||
|
" # Отображение графиков\n",
|
|||
|
" plt.tight_layout()\n",
|
|||
|
" plt.show()\n",
|
|||
|
" \n",
|
|||
|
"\n",
|
|||
|
"# Проверка сбалансированности\n",
|
|||
|
"check_balance(df_train, 'Обучающая выборка', 'hazardous')\n",
|
|||
|
"check_balance(df_val, 'Контрольная выборка', 'hazardous')\n",
|
|||
|
"check_balance(df_test, 'Тестовая выборка', 'hazardous')\n",
|
|||
|
"\n",
|
|||
|
"# Проверка необходимости аугментации\n",
|
|||
|
"print(f\"Для обучающей выборки аугментация данных {'не ' if not need_augmentation(df_train, 'hazardous', True, False) else ''}требуется\")\n",
|
|||
|
"print(f\"Для контрольной выборки аугментация данных {'не ' if not need_augmentation(df_val, 'hazardous', True, False) else ''}требуется\")\n",
|
|||
|
"print(f\"Для тестовой выборки аугментация данных {'не ' if not need_augmentation(df_test, 'hazardous', True, False) else ''}требуется\")\n",
|
|||
|
" \n",
|
|||
|
"# Визуализация сбалансированности классов\n",
|
|||
|
"visualize_balance(df_train, df_val, df_test, 'hazardous')"
|
|||
|
]
|
|||
|
},
|
|||
|
{
|
|||
|
"cell_type": "markdown",
|
|||
|
"metadata": {},
|
|||
|
"source": [
|
|||
|
"### Приращение данных:\n",
|
|||
|
"\n",
|
|||
|
"**Аугментация данных** может быть полезна в том случае, когда имеется недостаточное количество данных и мы хотим сгенерировать новые данные на основе имеющихся, слегка модифицировав их.\n",
|
|||
|
"\n",
|
|||
|
"**Методы решения:**\n",
|
|||
|
"1. **Выборка с избытком (oversampling).** Копирование наблюдений или генерация новых наблюдений на основе существующих с помощью алгоритмов SMOTE и ADASYN (нахождение k-ближайших соседей).\n",
|
|||
|
"2. **Выборка с недостатком (undersampling).** Исключение некоторых наблюдений для меток с большим количеством наблюдений. Наблюдения можно исключать случайным образом или на основе определения связей Томека для наблюдений разных меток."
|
|||
|
]
|
|||
|
},
|
|||
|
{
|
|||
|
"cell_type": "code",
|
|||
|
"execution_count": 7,
|
|||
|
"metadata": {},
|
|||
|
"outputs": [
|
|||
|
{
|
|||
|
"name": "stdout",
|
|||
|
"output_type": "stream",
|
|||
|
"text": [
|
|||
|
"После применения метода oversampling:\n",
|
|||
|
"Обучающая выборка: (100573, 6)\n",
|
|||
|
"Распределение выборки данных по классам \"hazardous\":\n",
|
|||
|
" hazardous\n",
|
|||
|
"True 51376\n",
|
|||
|
"False 49197\n",
|
|||
|
"Name: count, dtype: int64\n",
|
|||
|
"Процент объектов класса \"True\": 51.08%\n",
|
|||
|
"Процент объектов класса \"False\": 48.92%\n",
|
|||
|
"\n",
|
|||
|
"Контрольная выборка: (32787, 6)\n",
|
|||
|
"Распределение выборки данных по классам \"hazardous\":\n",
|
|||
|
" hazardous\n",
|
|||
|
"False 16399\n",
|
|||
|
"True 16388\n",
|
|||
|
"Name: count, dtype: int64\n",
|
|||
|
"Процент объектов класса \"False\": 50.02%\n",
|
|||
|
"Процент объектов класса \"True\": 49.98%\n",
|
|||
|
"\n",
|
|||
|
"Тестовая выборка: (32750, 6)\n",
|
|||
|
"Распределение выборки данных по классам \"hazardous\":\n",
|
|||
|
" hazardous\n",
|
|||
|
"False 16400\n",
|
|||
|
"True 16350\n",
|
|||
|
"Name: count, dtype: int64\n",
|
|||
|
"Процент объектов класса \"False\": 50.08%\n",
|
|||
|
"Процент объектов класса \"True\": 49.92%\n",
|
|||
|
"\n",
|
|||
|
"Для обучающей выборки аугментация данных не требуется\n",
|
|||
|
"Для контрольной выборки аугментация данных не требуется\n",
|
|||
|
"Для тестовой выборки аугментация данных не требуется\n"
|
|||
|
]
|
|||
|
},
|
|||
|
{
|
|||
|
"data": {
|
|||
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAABg4AAAHxCAYAAAC1TkfqAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAADI70lEQVR4nOzdd3hTZRsG8Lu7pYNVNsgesgQRlCVbpoiCCA6GMhTQD1QUVGSIDBFlIygbZAsoMssulE0LbSm0pYUO6J5ps9/vj5jQkHRB25Ok9++6cilpcnLnJDlvcp532AkhBIiIiIiIiIiIiIiIiADYSx2AiIiIiIiIiIiIiIgsBwsHRERERERERERERERkwMIBEREREREREREREREZsHBAREREREREREREREQGLBwQEREREREREREREZEBCwdERERERERERERERGTAwgERERERERERERERERmwcEBERERERERERERERAYsHBARWYHU1FSEhYVBrVZLHYWKkBACycnJCA0NlToKERERFSGlUolHjx4hNjZW6ihERERUjGQyGaKiopCSkiJ1lCLHwgGRFahTpw4GDBggdYwi07VrV3Tt2lXqGBZNpVLhp59+wgsvvAAXFxeUL18eDRs2xIkTJ6SOZhUCAwOxf/9+w7/9/f3x77//Shcoh4yMDHz33Xdo3LgxnJ2dUbFiRTRq1Ah37tyROhoRPSO210TFb+vWrYiMjDT8e+PGjYiJiZEuUA5Xr17Fu+++C29vb7i4uKBatWoYPHiw1LGISj22z0TWad68edBqtQAArVaL+fPnS5zosd27d6NHjx7w9PSEh4cHnnvuOfz0009SxypyhSocbNy4EXZ2doaLq6srGjVqhEmTJiEuLq64MhLZvFmzZqFOnToAHn/OqPh17doVo0aNAgCMGjXKYr58KRQK9OzZEzNmzEDXrl2xe/duHD9+HCdPnkT79u2ljmcVMjIyMH78eFy8eBGhoaH43//+h1u3bkkdC0lJSWjfvj2WLVuGIUOG4MCBAzh+/DhOnz5tOAYUBbbXRMWD7bU0LLW9JmmcO3cOX331FSIjI3H06FFMnDgR9vbS94c7cOAAOnXqhODgYPz44484fvw4jh8/jjVr1hhuw/aZqHiwfZYG22cqbps2bcLPP/+M6OhoLF68GJs2bZI6EgBg2rRpGDp0KDw9PfH777/j+PHj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
|
|||
|
"text/plain": [
|
|||
|
"<Figure size 1500x500 with 3 Axes>"
|
|||
|
]
|
|||
|
},
|
|||
|
"metadata": {},
|
|||
|
"output_type": "display_data"
|
|||
|
}
|
|||
|
],
|
|||
|
"source": [
|
|||
|
"# Метод приращения с избытком (oversampling)\n",
|
|||
|
"def oversample(df: DataFrame, column: str) -> DataFrame:\n",
|
|||
|
" X: DataFrame = df.drop(column, axis=1)\n",
|
|||
|
" y: DataFrame = df[column] # type: ignore\n",
|
|||
|
" \n",
|
|||
|
" adasyn = ADASYN()\n",
|
|||
|
" X_resampled, y_resampled = adasyn.fit_resample(X, y) # type: ignore\n",
|
|||
|
" \n",
|
|||
|
" df_resampled: DataFrame = pd.concat([X_resampled, y_resampled], axis=1)\n",
|
|||
|
" return df_resampled\n",
|
|||
|
"\n",
|
|||
|
"\n",
|
|||
|
"# Приращение данных (oversampling)\n",
|
|||
|
"df_train_oversampled: DataFrame = oversample(df_train, 'hazardous')\n",
|
|||
|
"df_val_oversampled: DataFrame = oversample(df_val, 'hazardous')\n",
|
|||
|
"df_test_oversampled: DataFrame = oversample(df_test, 'hazardous')\n",
|
|||
|
"\n",
|
|||
|
"# Проверка сбалансированности\n",
|
|||
|
"print('После применения метода oversampling:')\n",
|
|||
|
"check_balance(df_train_oversampled, 'Обучающая выборка', 'hazardous')\n",
|
|||
|
"check_balance(df_val_oversampled, 'Контрольная выборка', 'hazardous')\n",
|
|||
|
"check_balance(df_test_oversampled, 'Тестовая выборка', 'hazardous')\n",
|
|||
|
"\n",
|
|||
|
"# Проверка необходимости аугментации\n",
|
|||
|
"print(f\"Для обучающей выборки аугментация данных {'не ' if not need_augmentation(df_train_oversampled, 'hazardous', True, False) else ''}требуется\")\n",
|
|||
|
"print(f\"Для контрольной выборки аугментация данных {'не ' if not need_augmentation(df_val_oversampled, 'hazardous', True, False) else ''}требуется\")\n",
|
|||
|
"print(f\"Для тестовой выборки аугментация данных {'не ' if not need_augmentation(df_test_oversampled, 'hazardous', True, False) else ''}требуется\")\n",
|
|||
|
" \n",
|
|||
|
"# Визуализация сбалансированности классов\n",
|
|||
|
"visualize_balance(df_train_oversampled, df_val_oversampled, df_test_oversampled, 'hazardous')"
|
|||
|
]
|
|||
|
},
|
|||
|
{
|
|||
|
"cell_type": "code",
|
|||
|
"execution_count": 8,
|
|||
|
"metadata": {},
|
|||
|
"outputs": [
|
|||
|
{
|
|||
|
"name": "stdout",
|
|||
|
"output_type": "stream",
|
|||
|
"text": [
|
|||
|
"После применения метода undersampling:\n",
|
|||
|
"Обучающая выборка: (10608, 6)\n",
|
|||
|
"Распределение выборки данных по классам \"hazardous\":\n",
|
|||
|
" hazardous\n",
|
|||
|
"False 5304\n",
|
|||
|
"True 5304\n",
|
|||
|
"Name: count, dtype: int64\n",
|
|||
|
"Процент объектов класса \"False\": 50.00%\n",
|
|||
|
"Процент объектов класса \"True\": 50.00%\n",
|
|||
|
"\n",
|
|||
|
"Контрольная выборка: (3536, 6)\n",
|
|||
|
"Распределение выборки данных по классам \"hazardous\":\n",
|
|||
|
" hazardous\n",
|
|||
|
"False 1768\n",
|
|||
|
"True 1768\n",
|
|||
|
"Name: count, dtype: int64\n",
|
|||
|
"Процент объектов класса \"False\": 50.00%\n",
|
|||
|
"Процент объектов класса \"True\": 50.00%\n",
|
|||
|
"\n",
|
|||
|
"Тестовая выборка: (3536, 6)\n",
|
|||
|
"Распределение выборки данных по классам \"hazardous\":\n",
|
|||
|
" hazardous\n",
|
|||
|
"False 1768\n",
|
|||
|
"True 1768\n",
|
|||
|
"Name: count, dtype: int64\n",
|
|||
|
"Процент объектов класса \"False\": 50.00%\n",
|
|||
|
"Процент объектов класса \"True\": 50.00%\n",
|
|||
|
"\n",
|
|||
|
"Для обучающей выборки аугментация данных не требуется\n",
|
|||
|
"Для контрольной выборки аугментация данных не требуется\n",
|
|||
|
"Для тестовой выборки аугментация данных не требуется\n"
|
|||
|
]
|
|||
|
},
|
|||
|
{
|
|||
|
"data": {
|
|||
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAABgcAAAHzCAYAAAAEk80rAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAACIjUlEQVR4nOzdd3gU9d6G8WfTQxJqIPTepInSpAkqqJRXsYD1UGwoYsHu8ShgQ8QCYkOPIgo25Ah2AQEVREEUpEsJICAldEL6zvtHyJrNbvruzszu/bmuXErYbL7Zdk/4zcw6DMMwBAAAAAAAAAAAQkaY2QMAAAAAAAAAAIDAYnEAAAAAAAAAAIAQw+IAAAAAAAAAAAAhhsUBAAAAAAAAAABCDIsDAAAAAAAAAACEGBYHAAAAAAAAAAAIMSwOAAAAAAAAAAAQYlgcAAAAAAAAAAAgxLA4AAA2cPToUW3dulXZ2dlmjwIfMgxDhw8f1pYtW8weBQAAlFNmZqb27dunvXv3mj0KAADwodTUVP311186cuSI2aP4HIsDgA00bNhQAwcONHsMn+ndu7d69+5t9hiWlpWVpWeffVZnnnmmoqOjVaVKFTVr1kzfffed2aPZwrp16zR37lzXn1evXq0vv/zSvIHyOXHihP7zn/+oRYsWioqKUrVq1dS8eXNt3rzZ7NEAlAGNBvxv5syZ2rFjh+vP77zzjvbs2WPeQPn8+uuvuvbaa5WYmKjo6GjVqlVLV1xxhdljASGHHgP29PTTT8vpdEqSnE6nJkyYYPJE/5g9e7YuuOACJSQkKD4+XvXr19ezzz5r9lg+V6rFgXfeeUcOh8P1ERMTo+bNm2v06NHav3+/v2YEgt64cePUsGFDSf88z+B/vXv31vDhwyVJw4cPt8zGV0ZGhvr06aNHH31UvXv31uzZs7VgwQItWrRIXbt2NXs8Wzhx4oRGjhypn3/+WVu2bNFdd92ltWvXmj2WDh06pK5du+qll17SlVdeqXnz5mnBggVasmSJ6zWgrGg04B802hxWbTTM8eOPP+qBBx7Qjh079O233+r2229XWJj5+7nNmzdPPXr00IYNG/TUU09pwYIFuu+++/Tzzz/TY8DH6LE56DH8bcaMGXruuee0e/duPf/885oxY4bZI0mSHnroIQ0ZMkQJCQl68803tWDBAi1cuFCjRo0yezSfiyjLFz3++ONq1KiR0tPTtXTpUr322mv66quvtG7dOlWoUMHXMwJASJk4caJ++eUXffvtt2x8lVHXrl1dH5LUvHlz3XzzzSZPJd1///36+++/tXz5crVu3dov34NGAwCCzZgxY9S7d281atRIknTPPfeoVq1aps50+PBh3XTTTbrooos0e/ZsRUVFSZJ2794tiR4DAFASjz/+uIYOHaoHH3xQ0dHRmjlzptkj6fvvv9fEiRM1YcIEPfTQQ2aP43dlWhzo16+fOnbsKEm66aabVK1aNb3wwguaN2+errnmGp8OCMB+UlNTFRcXZ/YYtpSdna3Jkyfr3nvvZWGgnObOnasNGzYoLS1Nbdu2df3SbpYDBw5oxowZev311/22MCDRaABFo9Gwo5YtW2rbtm1at26dEhMT1aRJE7NH0vTp05Wenq533nnH6zYGPQZQFHoM5Lrqqqt03nnnaevWrWrWrJmqV69u9kh67rnn1K1bt5BYGJB89J4D559/viQpOTlZUu5eFPfdd5/atm2r+Ph4VaxYUf369dOaNWs8vjY9PV3jxo1T8+bNFRMTo1q1aunyyy/Xtm3bJEk7duxwO01CwY/8/3i2ZMkSORwOffTRR/r3v/+tmjVrKi4uTpdccon++usvj+/9yy+/6OKLL1alSpVUoUIF9erVS8uWLfP6M/bu3dvr9x83bpzHZWfOnKkOHTooNjZWVatW1dVXX+31+xf1s+XndDo1efJktW7dWjExMUpKStLIkSM93gSjsHPsjR492uM6vc0+adIkj9tUyj3FydixY9W0aVNFR0erXr16euCBB5SRkeH1tsrP23nynnrqKYWFhen9998v0+2R9yStVq2aYmNj1aFDB33yySdev//MmTPVuXNnVahQQVWqVNG5556r+fPnu13m66+/Vq9evZSQkKCKFSuqU6dOHrPNnj3bdZ8mJibq+uuv9zjP6fDhw91mrlKlinr37q0ff/yx2NuppJYuXarOnTsrJiZGjRs31rvvvuv29yV97jVs2LDQ23rJkiWSpJ07d2rUqFFq0aKFYmNjVa1aNQ0ePNjtfK/SP4d0fv/99xo1apRq1KihunXruv7+jTfeUJMmTRQbG6vOnTsXenscOHBAN954o5KSkhQTE6MzzzzT43CyvOd43ox58h4777zzjutz+/bt04gRI1S3bl3X+V8vvfRSj/lLK/9tFxYWppo1a+qqq67Srl27SvT1r776qlq3bq3o6GjVrl1bt99+u44ePer6+82bN+vIkSNKSEhQr169VKFCBVWqVEkDBw7UunXrXJdbvHixHA6HPv30U4/v8f7778vhcGj58uWumfMOBc3j7bb88ccfNXjwYNWvX9/1XB8zZozS0tLcvnbcuHEez8tZs2apffv2iomJUbVq1XTNNdd43CbDhw9XfHy82+c++eQTr/dpfHy8x8xSyV5f87/utGrVSh06dNCaNWu8vp54U/D1PjExUQMGDHC7/aXc19HRo0cXej15z428x9zKlSvldDqVmZmpjh07FnlbSdKiRYvUs2dPxcXFqXLlyrr00ku1ceNGt8vk3RebNm3Sq6++Kkm64IILdNdddyk9PV3SP42+9tprNW7cONfrRJs2bRQRESGHw6GePXu6vU7k3c4xMTGKjY1VQkICjabRNLoYNNrejfbWyltuuUUxMTEeP1NxLZdyn19t2rTx+D7PPfecWxuKur8dDofrFBp5t+Nzzz2nF198UQ0aNFBsbKx69erl0SepZA3xdrt5e7zlXaa4c3nnn7GgNm3aeLzelORxlf/xExcXpy5duqhJkya6/fbb5XA4vG4rePv6vI/IyEg1bNhQ999/vzIzM12Xy3uu/Prrr4VeV8HXzJ9//lnt27fX008/rXr16ik6OlrNmjXz+h5H2dnZrlMK/etf/1LDhg01ZswYjRkzxu01ITY21vVamLdd1apVK3344YduvzNXrlzZ9dz29nMW/Khbt65rxjp16sjhcOiDDz6gx/nQY3pMj83vsb+auGnTJl155ZWqWrWqYmJi1LFjR3322WdulymsAykpKR6vBd5+Hz558qRq1qzpcdvnbQ+sWrVK3bp1U2xsrBo1aqTXX3/dY87SdLG4rknS9u3bNXjwYFWtWlUVKlTQOeec49Eobz+L5LldVJrbR5J+//139evXTxUrVlR8fLwuuOAC/fzzz26Xyf/7co0aNVyvWe3atfN4rHpT8LS6FSpUUNu2bfXf//7X7XLe/h2ioII/w88//6w2bdro6quvVtWqVRUbG6tOnTq5va9hntLcbyV5zA4fPtzjlMMzZ85UWFiYnnnmGbfPl+SxXRJlOnKgoLx/JKhWrZqk3Afg3LlzNXjwYDVq1Ej79+/XtGnT1KtXL23YsEG1a9eWJOXk5GjgwIH67rvvdPXVV+uuu+7SiRMntGDBAq1bt85tj5BrrrlG/fv3d/u+Dz/8sNd5nnrqKTkcDj344IM6cOCAJk+erD59+mj16tWKjY2VlLvB3K9fP3Xo0EFjx45VWFiYpk+frvPPP18//vijOnfu7HG9devWdb0xxsmTJ3Xbbbd5/d6PPvqohgwZoptuukkHDx7U1KlTde655+r3339X5cqVPb7mlltuUc+ePSVJ//vf/zz+sW/kyJF65513NGLECN15551KTk7Wyy+/rN9//13Lli1TZGSk19uhNI4ePer1TT+cTqcuueQSLV26VLfccovOOOMMrV27Vi+++KL+/PNPr0+MokyfPl3/+c9/9Pzzz+vaa6/1epnibo8pU6bokksu0XXXXafMzEx9+OGHGjx4sL744gsNGDDAdbnx48dr3Lhx6tatmx5//HFFRUXpl19+0aJFi3ThhRdKyn0xueGGG9S6dWs9/PDDqly
|
|||
|
"text/plain": [
|
|||
|
"<Figure size 1500x500 with 3 Axes>"
|
|||
|
]
|
|||
|
},
|
|||
|
"metadata": {},
|
|||
|
"output_type": "display_data"
|
|||
|
}
|
|||
|
],
|
|||
|
"source": [
|
|||
|
"# Метод приращения с недостатком (undersampling)\n",
|
|||
|
"def undersample(df: DataFrame, column: str) -> DataFrame:\n",
|
|||
|
" X: DataFrame = df.drop(column, axis=1)\n",
|
|||
|
" y: DataFrame = df[column] # type: ignore\n",
|
|||
|
" \n",
|
|||
|
" undersampler = RandomUnderSampler()\n",
|
|||
|
" X_resampled, y_resampled = undersampler.fit_resample(X, y) # type: ignore\n",
|
|||
|
" \n",
|
|||
|
" df_resampled: DataFrame = pd.concat([X_resampled, y_resampled], axis=1)\n",
|
|||
|
" return df_resampled\n",
|
|||
|
"\n",
|
|||
|
"\n",
|
|||
|
"# Приращение данных (undersampling)\n",
|
|||
|
"df_train_undersampled: DataFrame = undersample(df_train, 'hazardous')\n",
|
|||
|
"df_val_undersampled: DataFrame = undersample(df_val, 'hazardous')\n",
|
|||
|
"df_test_undersampled: DataFrame = undersample(df_test, 'hazardous')\n",
|
|||
|
"\n",
|
|||
|
"\n",
|
|||
|
"# Проверка сбалансированности\n",
|
|||
|
"print('После применения метода undersampling:')\n",
|
|||
|
"check_balance(df_train_undersampled, 'Обучающая выборка', 'hazardous')\n",
|
|||
|
"check_balance(df_val_undersampled, 'Контрольная выборка', 'hazardous')\n",
|
|||
|
"check_balance(df_test_undersampled, 'Тестовая выборка', 'hazardous')\n",
|
|||
|
"\n",
|
|||
|
"# Проверка необходимости аугментации\n",
|
|||
|
"print(f\"Для обучающей выборки аугментация данных {'не ' if not need_augmentation(df_train_undersampled, 'hazardous', True, False) else ''}требуется\")\n",
|
|||
|
"print(f\"Для контрольной выборки аугментация данных {'не ' if not need_augmentation(df_val_undersampled, 'hazardous', True, False) else ''}требуется\")\n",
|
|||
|
"print(f\"Для тестовой выборки аугментация данных {'не ' if not need_augmentation(df_test_undersampled, 'hazardous', True, False) else ''}требуется\")\n",
|
|||
|
" \n",
|
|||
|
"# Визуализация сбалансированности классов\n",
|
|||
|
"visualize_balance(df_train_undersampled, df_val_undersampled, df_test_undersampled, 'hazardous')"
|
|||
|
]
|
|||
|
}
|
|||
|
],
|
|||
|
"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.5"
|
|||
|
}
|
|||
|
},
|
|||
|
"nbformat": 4,
|
|||
|
"nbformat_minor": 2
|
|||
|
}
|