Роддом. У мужа рожает жена, он сильно волнуется. Выходит медсестра с ребенком на руках, берет его за ногу и бьет головой об пол. Мужик в афиге. А медесестра смотрит на него и говорит: - Шучу, шучу, он мертвым родился.
548 KiB
Лабораторная работа №3. Конструирование признаков.¶
Датасет "Набор данных для анализа и прогнозирования сердечного приступа".¶
Описание датасета¶
Проблемная область: Датасет связан с медицинской статистикой и направлен на анализ факторов, связанных с риском сердечного приступа. Это важно для прогнозирования и разработки стратегий профилактики сердечно-сосудистых заболеваний.
Актуальность: Сердечно-сосудистые заболевания являются одной из ведущих причин смертности во всем мире. Анализ данных об образе жизни, состоянии здоровья и наследственных факторах позволяет выделить ключевые предикторы, влияющие на развитие сердечно-сосудистых заболеваний. Этот датасет предоставляет инструменты для анализа таких факторов и может быть полезен в создании прогнозных моделей, направленных на снижение рисков и своевременную диагностику.
Объекты наблюдения: Каждая запись представляет собой данные о человеке, включая информацию об их состоянии здоровья, образе жизни, демографических характеристиках и наличию определенных заболеваний. Объекты наблюдений — это индивидуальные пациенты.
Атрибуты объектов:
HeartDisease
— наличие сердечного приступа (Yes/No) (целевая переменная).BMI
— индекс массы тела (Body Mass Index), числовой показатель.Smoking
— курение (Yes/No).AlcoholDrinking
— употребление алкоголя (Yes/No).Stroke
— наличие инсульта (Yes/No).PhysicalHealth
— количество дней в месяц, когда физическое здоровье было неудовлетворительным.MentalHealth
— количество дней в месяц, когда психическое здоровье было неудовлетворительным.DiffWalking
— трудности при ходьбе (Yes/No).Sex
— пол (Male/Female).AgeCategory
— возрастная категория (например, 55-59, 80 or older).Race
— расовая принадлежность (например, White, Black).Diabetic
— наличие диабета (Yes/No/No, borderline diabetes).PhysicalActivity
— физическая активность (Yes/No).GenHealth
— общее состояние здоровья (от Excellent до Poor).SleepTime
— среднее количество часов сна за сутки.Asthma
— наличие астмы (Yes/No).KidneyDisease
— наличие заболеваний почек (Yes/No).SkinCancer
— наличие кожного рака (Yes/No).
Бизнес-цели и соответствующие цели технического проекта¶
Бизнес-цель 1: Разработка персонализированных программ профилактики сердечно-сосудистых заболеваний
Снижение числа сердечно-сосудистых заболеваний в группе риска благодаря внедрению программ профилактики уменьшает затраты на медицинское обслуживание (страховые выплаты, лечение). Компании, предоставляющие страховые или медицинские услуги, могут минимизировать убытки и увеличить доходы за счет раннего выявления риска у клиентов.
Цели технического проекта:
- Построить предиктивную модель машинного обучения для прогнозирования риска сердечного приступа на основе предоставленных данных.
- Разработать алгоритм классификации пациентов по группам риска с учетом их образа жизни, состояния здоровья и наследственных факторов.
- Выявить наиболее значимые факторы риска для рекомендации адресных изменений в образе жизни.
Бизнес-цель 2: Создание коммерческого продукта для оценки здоровья сотрудников компаний
Продукт может быть предложен корпоративным клиентам для оценки состояния здоровья их сотрудников и снижения риска долгосрочных больничных листов, что положительно скажется на производительности и снизит страховые выплаты работодателей. Компании смогут предлагать услуги в формате подписки или единовременной оценки.
Цели технического проекта:
- Разработать инструмент визуализации здоровья сотрудников с использованием анализа ключевых факторов из датасета (например, курение, индекс массы тела, физическая активность).
- Обучить и оптимизировать модель прогнозирования вероятности сердечного приступа в зависимости от корпоративного контекста (возрастные группы сотрудников, стрессовые факторы).
- Интегрировать предиктивную аналитику в продукт, предоставляющий персонализированные отчеты и рекомендации по здоровью.
Бизнес-цель: Улучшенное прогнозирование цен поможет продавцам устанавливать конкурентные цены, а покупателям — принимать более взвешенные решения о покупке. Это также даст риелторам возможность лучше ориентироваться на рынке и оптимизировать стратегию продажи.
Техническая цель: Прогнозирование цен на жилье
Входные данные: Исторические данные о продажах домов, включая все признаки (количество комнат, площадь, состояние, местоположение и др.).
Целевая переменная: Столбец HeartDisease
, который указывает на наличие сердечного приступа у пациента (Yes
или No
).
import pandas as pd
df = pd.read_csv(".//static//csv//heart_2020_cleaned.csv")
df.head()
Устранение проблемы пропущенных данных¶
Для начала определим, присутствуют ли в датасете пропущенные значения признаков:
# Количество пустых значений признаков
print(df.isnull().sum())
print()
# Есть ли пустые значения признаков
print(df.isnull().any())
print()
# Процент пустых значений признаков
for i in df.columns:
null_rate = df[i].isnull().sum() / len(df) * 100
if null_rate > 0:
print(f"{i} процент пустых значений: %{null_rate:.2f}")
Пропущенных данных в датасете не обнаружено
Устранение проблемы зашумленности данных¶
Зашумленность – это наличие случайных ошибок или вариаций в данных, которые могут затруднить выявление истинных закономерностей. Шум может возникать из-за ошибок измерений, неправильных записей или других факторов.
Выбросы – это значения, которые значительно отличаются от остальных наблюдений в наборе данных. Выбросы могут указывать на ошибки в данных или на редкие, но важные события. Их наличие может повлиять на статистические методы анализа.
Представленный ниже код помогает определить наличие выбросов в наборе данных и устранить их (при наличии), заменив значения ниже нижней границы (рассматриваемого минимума) на значения нижней границы, а значения выше верхней границы (рассматриваемого максимума) – на значения верхней границы:
import matplotlib.pyplot as plt
import seaborn as sns
from math import ceil
# Проверка выбросов в DataFrame
def check_outliers(dataframe, columns):
for column in columns:
if not pd.api.types.is_numeric_dtype(dataframe[column]): # Проверяем, является ли колонка числовой
continue
Q1 = dataframe[column].quantile(0.25) # 1-й квартиль (25%)
Q3 = dataframe[column].quantile(0.75) # 3-й квартиль (75%)
IQR = Q3 - Q1 # Вычисляем межквартильный размах
# Определяем границы для выбросов
lower_bound = Q1 - 1.5 * IQR # Нижняя граница
upper_bound = Q3 + 1.5 * IQR # Верхняя граница
# Подсчитываем количество выбросов
outliers = dataframe[(dataframe[column] < lower_bound) | (dataframe[column] > upper_bound)]
outlier_count = 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, columns):
# Диаграммы размахов
plt.figure(figsize=(15, 10))
rows = 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 = [
'BMI',
'PhysicalHealth',
'MentalHealth',
'SleepTime'
]
# Проверка наличия выбросов в колонках
print('Проверка наличия выбросов в колонках:')
check_outliers(df, numeric_columns)
visualize_outliers(df, numeric_columns)
Признаки BMI
и SleepTime
имеют достаточное количество выбросов, которое стоит устранить. Также числовые признаки PhysicalHealth
и MentalHealth
имеют большое количество выбросов, но так как количество таких наблюдений по сравнению с общим количеством объектов велико, а диапазон значений, которые эти признаки принимают, сравнительно небольшой, то удаление такого объема важной информации, как состояние здоровья, может негативно сказаться на способности прогнозировать сердечный приступ.
Для решения проблемы выбросов у признаков BMI
и SleepTime
воспользуемся методом отсечения слишком отклоняющихся значений путем замены на экстремальное значение соответствующей границы:
# Устранить выборсы в DataFrame
def remove_outliers(dataframe, columns):
for column in columns:
if not pd.api.types.is_numeric_dtype(dataframe[column]): # Проверяем, является ли колонка числовой
continue
Q1 = dataframe[column].quantile(0.25) # 1-й квартиль (25%)
Q3 = dataframe[column].quantile(0.75) # 3-й квартиль (75%)
IQR = Q3 - Q1 # Вычисляем межквартильный размах
# Определяем границы для выбросов
lower_bound = Q1 - 1.5 * IQR # Нижняя граница
upper_bound = 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
# Cтолбцы, которые нужно исправить
columns_to_fix = [
'BMI',
'SleepTime'
]
# Устраняем выборсы
df = remove_outliers(df, columns_to_fix)
# Проверка наличия выбросов в колонках
print('Проверка наличия выбросов в колонках после их устранения:')
check_outliers(df, columns_to_fix)
visualize_outliers(df, columns_to_fix)
Разбиение датасета на выборки¶
Разделим выборку данных на 3 группы:
- Обучающая выборка (70%).
- Контрольная выборка (15%).
- Тестовая выборка (15%).
from sklearn.model_selection import train_test_split
def split_stratified_into_train_val_test(
df_input,
stratify_colname,
frac_train,
frac_val,
frac_test,
random_state=None,
):
"""
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 = df_input # Contains all columns.
y = 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 = 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
# Оценка сбалансированности
def check_balance(dataframe, dataframe_name, column):
counts = dataframe[column].value_counts()
print(dataframe_name + ": ", dataframe.shape)
print(f"Распределение выборки данных по классам в колонке \"{column}\":\n", counts)
total_count = len(dataframe)
for value in counts.index:
percentage: float = counts[value] / total_count * 100
print(f"Процент объектов класса \"{value}\": {percentage:.2f}%")
print()
# Определение необходимости аугментации данных
def need_augmentation(dataframe,
column,
first_value, second_value):
counts = 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_val,
dataframe_test,
column: str):
fig, axes = plt.subplots(3, 1, figsize=(6, 8))
# Обучающая выборка
counts_train = 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 = 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 = 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()
df_train, df_val, df_test = split_stratified_into_train_val_test(
df,
stratify_colname="HeartDisease",
frac_train=0.60,
frac_val=0.20,
frac_test=0.20
)
# Проверка сбалансированности выборок
print('Проверка сбалансированности выборок:')
check_balance(df_train, 'Обучающая выборка', 'HeartDisease')
check_balance(df_val, 'Контрольная выборка', 'HeartDisease')
check_balance(df_test, 'Тестовая выборка', 'HeartDisease')
# Проверка необходимости аугментации выборок
print('Проверка необходимости аугментации выборок:')
print(f"Для обучающей выборки аугментация данных {'не ' if not need_augmentation(df_train, 'HeartDisease', 'No', 'Yes') else ''}требуется")
print(f"Для контрольной выборки аугментация данных {'не ' if not need_augmentation(df_val, 'HeartDisease', 'No', 'Yes') else ''}требуется")
print(f"Для тестовой выборки аугментация данных {'не ' if not need_augmentation(df_test, 'HeartDisease', 'No', 'Yes') else ''}требуется")
# Визуализация сбалансированности классов
visualize_balance(df_train, df_val, df_test, 'HeartDisease')
Выборки оказались недостаточно сбалансированными. Используем методы приращения данных с избытком (oversampling) – копирование наблюдений или генерация новых наблюдений на основе существующих с помощью алгоритмов SMOTE и ADASYN (нахождение k-ближайших соседей):
from imblearn.over_sampling import SMOTE
# Метод приращения с избытком (oversampling)
def oversample(df, column):
X = pd.get_dummies(df.drop(column, axis=1))
y = df[column]
smote = SMOTE()
X_resampled, y_resampled = smote.fit_resample(X, y)
df_resampled = pd.concat([X_resampled, y_resampled], axis=1)
return df_resampled
# Приращение данных (oversampling)
df_train_oversampled = oversample(df_train, 'HeartDisease')
df_val_oversampled = oversample(df_val, 'HeartDisease')
df_test_oversampled = oversample(df_test, 'HeartDisease')
# Проверка сбалансированности выборок
print('Проверка сбалансированности выборок:')
check_balance(df_train_oversampled, 'Обучающая выборка', 'HeartDisease')
check_balance(df_val_oversampled, 'Контрольная выборка', 'HeartDisease')
check_balance(df_test_oversampled, 'Тестовая выборка', 'HeartDisease')
# Проверка необходимости аугментации выборок
print('Проверка необходимости аугментации выборок:')
print(f"Для обучающей выборки аугментация данных {'не ' if not need_augmentation(df_train_oversampled, 'HeartDisease', 'No', 'Yes') else ''}требуется")
print(f"Для контрольной выборки аугментация данных {'не ' if not need_augmentation(df_val_oversampled, 'HeartDisease', 'No', 'Yes') else ''}требуется")
print(f"Для тестовой выборки аугментация данных {'не ' if not need_augmentation(df_test_oversampled, 'HeartDisease', 'No', 'Yes') else ''}требуется")
# Визуализация сбалансированности классов
visualize_balance(df_train_oversampled, df_val_oversampled, df_test_oversampled, 'HeartDisease')
Конструирование признаков¶
Конструирование признаков (feature engineering) – процесс использования знаний об особенностях решаемой задачи и предметной области для определения признаков, которые будут использованы для обучения статистической модели.
Методы конструирования признаков:
- Для категориальных данных:
- Унитарное кодирование категориальных признаков (one-hot encoding) – метод, который применяется для преобразования категориальных переменных в числовой формат. Каждая характеристика представляется в виде бинарного вектора, где для каждой категории выделяется отдельный признак (столбец) со значением 1 (True), если объект принадлежит этой категории, и 0 (False) в противном случае.
- Для числовых данных:
- Дискретизация – процесс преобразования непрерывных числовых значений в категориальные группы или интервалы (дискретные значения).
- Ручной синтез – процесс создания новых признаков на основе существующих данных. Это может включать в себя комбинирование нескольких признаков, использование математических операций (например, сложение, вычитание), а также создание полиномиальных или логарифмических признаков.
- Масштабирование признаков на основе нормировки и стандартизации – метод, который позволяет привести все числовые признаки к одинаковым или очень похожим диапазонам значений либо распределениям.
- С применением фреймворка FeatureTools – библиотека для автоматизированного создания признаков (features) из структурированных данных. Подходит для задач машинного обучения, когда нужно быстро извлекать полезные признаки из больших объемов данных.
Унитарное кодирование¶
Преобразование уже было выполнено на этапе приращения с избытком (метод pd.get_dummies(...)
), так как метод fit_resample
требовал для работы признаки типа число с плавающей точкой. Были преобразованы категориальные признаки Smoking
, AlcoholDrinking
, Stroke
, DiffWalking
и т.д. в бинарные признаки:
categorical_features = [
'Smoking', 'AlcoholDrinking', 'Stroke', 'DiffWalking', 'Sex', 'AgeCategory', 'Race',
'Diabetic', 'PhysicalActivity', 'GenHealth', 'Asthma', 'KidneyDisease', 'SkinCancer'
]
df_train_oversampled.head(10)
Дискретизация числовых признаков¶
Распределим значения признака BMI
по интервалам, преобразуя его из числового представления в категориальное. Будем использовать метод Равномерная группировка:
# Функция для дискретизации числовых признаков
def discretize_features(df, features, bins=5, labels=False):
for feature in features:
df[f'{feature}_Category'] = pd.cut(df[feature], bins=bins, labels=labels)
return df
# Определение числовых признаков для дискретизации
numerical_features = ['BMI']
# Применение дискретизации к обучающей, контрольной и тестовой выборкам
df_train_oversampled = discretize_features(df_train_oversampled, numerical_features)
df_val_oversampled = discretize_features(df_val_oversampled, numerical_features)
df_test_oversampled = discretize_features(df_test_oversampled, numerical_features)
df_train_oversampled[['BMI', 'BMI_Category']].head(20)
Ручной синтез признаков¶
Будем синтезировать новый признак HealthScore
, являющийся числовым показателем здоровья на основе таких признаков, как PhysicalHealth
, MentalHealth
, SleepTime
:
# Рассчитаем новый признак HealthScore
# Используем взвешенную сумму физического, ментального здоровья и количества сна
df_train_oversampled["HealthScore"] = (
(30.0 - df_train_oversampled["PhysicalHealth"]) * 0.4 + # Чем меньше проблем с физическим здоровьем, тем лучше
(30.0 - df_train_oversampled["MentalHealth"]) * 0.3 + # Чем меньше проблем с ментальным здоровьем, тем лучше
df_train_oversampled["SleepTime"] * 0.3 # Оптимальное время сна
)
df_train_oversampled.head(10)
Масштабирование признаков на основе нормировки и стандартизации¶
Методы масштабирования признаков:
- Нормировка – обычно применяется для равномерного распределения;
- Стандартизация – обычно применяется для нормального распределения.
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
# Применяем масштабирование к выбранным признакам
df_train_oversampled_normalized = df_train_oversampled
df_train_oversampled_normalized[numeric_columns] = scaler.fit_transform(df_train_oversampled_normalized[numeric_columns])
df_train_oversampled_normalized[numeric_columns].head(10)
df_train_oversampled_normalized.columns
Конструирование с применением FeatureTools¶
import featuretools as ft
# Создание EntitySet
df_testing = df_train_oversampled_normalized
# Создание уникального идентификатора для каждой строки
df_testing['Id'] = range(1, len(df_testing) + 1)
es = ft.EntitySet(id='my-test-data')
es = es.add_dataframe(dataframe=df_testing, dataframe_name='my-name', index='Id')
# Указываем, какие трансформации нужно применить
trans_primitives = ['multiply_numeric']
# Генерация признаков с помощью глубокого синтеза признаков
feature_matrix, feature_defs = ft.dfs(
entityset=es,
target_dataframe_name='my-name',
max_depth=1,
trans_primitives=trans_primitives
)
# Выводим первые 10 строк сгенерированного набора признаков
feature_matrix.head(10)
Оценка качества каждого набора признаков¶
Предсказательная способность: Способность набора признаков успешно прогнозировать целевую переменную. Это определяется через метрики, такие как RMSE, MAE, R², которые показывают, насколько хорошо модель использует признаки для достижения точных результатов. Для определения качества необходимо провести обучение модели на обучающей выборке и сравнить с оценкой прогнозирования на контрольной и тестовой выборках.
Скорость вычисления: Время, необходимое для обработки данных и выполнения алгоритмов машинного обучения. Признаки должны быть вычисляемыми за разумный срок, чтобы обеспечить эффективность модели, особенно при работе с большими наборами данных. Для оценки качества необходимо провести измерение времени выполнения генерации признаков и обучения модели.
Надежность: Устойчивость и воспроизводимость результатов при изменении входных данных. Надежные признаки должны давать схожие результаты независимо от случайных факторов или незначительных изменений в данных. Методы оценки: Кросс-валидация, анализ чувствительности модели к изменениям в данных.
Корреляция: Степень взаимосвязи между признаками и целевой переменной, а также между самими признаками. Высокая корреляция с целевой переменной указывает на потенциальную предсказательную силу, тогда как высокая взаимосвязь между самими признаками может приводить к многоколлинеарности и снижению эффективности модели. Методы оценки: Анализ корреляционной матрицы признаков, удаление мультиколлинеарных признаков.
Цельность: Не является производным от других признаков. Методы оценки: Проверка логической связи между признаками и целевой переменной, интерпретация результатов модели.
import pandas as pd
from sklearn.model_selection import train_test_split
from imblearn.over_sampling import RandomOverSampler
import featuretools as ft
from sklearn.ensemble import RandomForestClassifier
# Загрузка данных
df = pd.read_csv(".//static//csv//heart_2020_cleaned.csv")
# Разделение на обучающую и тестовую выборки (например, 70% обучающая, 30% тестовая)
train_df, test_df = train_test_split(df, test_size=0.3, random_state=42)
# Разделение обучающей выборки на обучающую и контрольную (например, 70% обучающая, 30% контрольная)
train_df, val_df = train_test_split(train_df, test_size=0.3, random_state=42)
# Вывод размеров выборок
print("Размер обучающей выборки:", len(train_df))
print("Размер контрольной выборки:", len(val_df))
print("Размер тестовой выборки:", len(test_df))
# Определение категориальных признаков
categorical_features = [
'Smoking', 'AlcoholDrinking', 'Stroke', 'DiffWalking', 'Sex', 'AgeCategory', 'Race',
'Diabetic', 'PhysicalActivity', 'GenHealth', 'Asthma', 'KidneyDisease', 'SkinCancer'
]
# Применение one-hot encoding к обучающей выборке
train_df_encoded = pd.get_dummies(train_df, columns=categorical_features)
# Применение one-hot encoding к контрольной выборке
val_df_encoded = pd.get_dummies(val_df, columns=categorical_features)
# Применение one-hot encoding к тестовой выборке
test_df_encoded = pd.get_dummies(test_df, columns=categorical_features)
# Определение сущностей
es = ft.EntitySet(id='heart_data')
es = es.add_dataframe(dataframe_name='heart', dataframe=train_df_encoded, index='id')
# Генерация признаков
feature_matrix, feature_defs = ft.dfs(entityset=es, target_dataframe_name='heart', max_depth=2)
# Преобразование признаков для контрольной и тестовой выборок
val_feature_matrix = ft.calculate_feature_matrix(features=feature_defs, entityset=es, instance_ids=val_df_encoded.index)
test_feature_matrix = ft.calculate_feature_matrix(features=feature_defs, entityset=es, instance_ids=test_df_encoded.index)
# Оценка важности признаков
X = feature_matrix
y = train_df_encoded['HeartDisease']
# Разделение данных на обучающую и тестовую выборки
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Обучение модели
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
# Получение важности признаков
importances = model.feature_importances_
feature_names = feature_matrix.columns
# Сортировка признаков по важности
feature_importance = pd.DataFrame({'feature': feature_names, 'importance': importances})
feature_importance = feature_importance.sort_values(by='importance', ascending=False)
print("Feature Importance:")
print(feature_importance)
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score
from sklearn.model_selection import cross_val_score
import matplotlib.pyplot as plt
import seaborn as sns
import featuretools as ft
# Загрузка данных
df = pd.read_csv(".//static//csv//heart_2020_cleaned.csv")
# Уменьшение размера выборки для ускорения работы (опционально)
df = df.sample(frac=0.1, random_state=42)
# Разделение на обучающую и тестовую выборки (например, 70% обучающая, 30% тестовая)
train_df, test_df = train_test_split(df, test_size=0.3, random_state=42)
# Разделение обучающей выборки на обучающую и контрольную (например, 70% обучающая, 30% контрольная)
train_df, val_df = train_test_split(train_df, test_size=0.3, random_state=42)
# Вывод размеров выборок
print("Размер обучающей выборки:", len(train_df))
print("Размер контрольной выборки:", len(val_df))
print("Размер тестовой выборки:", len(test_df))
# Определение категориальных признаков
categorical_features = [
'Smoking', 'AlcoholDrinking', 'Stroke', 'DiffWalking', 'Sex', 'AgeCategory', 'Race',
'Diabetic', 'PhysicalActivity', 'GenHealth', 'Asthma', 'KidneyDisease', 'SkinCancer'
]
# Применение one-hot encoding к обучающей выборке
train_df_encoded = pd.get_dummies(train_df, columns=categorical_features)
# Применение one-hot encoding к контрольной выборке
val_df_encoded = pd.get_dummies(val_df, columns=categorical_features)
# Применение one-hot encoding к тестовой выборке
test_df_encoded = pd.get_dummies(test_df, columns=categorical_features)
# Определение сущностей
es = ft.EntitySet(id='heart_data')
es = es.add_dataframe(dataframe_name='heart', dataframe=train_df_encoded, index='id')
# Генерация признаков с уменьшенной глубиной
feature_matrix, feature_defs = ft.dfs(entityset=es, target_dataframe_name='heart', max_depth=1)
# Преобразование признаков для контрольной и тестовой выборок
val_feature_matrix = ft.calculate_feature_matrix(features=feature_defs, entityset=es, instance_ids=val_df_encoded.index)
test_feature_matrix = ft.calculate_feature_matrix(features=feature_defs, entityset=es, instance_ids=test_df_encoded.index)
# Удаление строк с NaN
feature_matrix = feature_matrix.dropna()
val_feature_matrix = val_feature_matrix.dropna()
test_feature_matrix = test_feature_matrix.dropna()
# Разделение данных на обучающую и тестовую выборки
X_train = feature_matrix.drop('HeartDisease', axis=1)
y_train = feature_matrix['HeartDisease']
X_val = val_feature_matrix.drop('HeartDisease', axis=1)
y_val = val_feature_matrix['HeartDisease']
X_test = test_feature_matrix.drop('HeartDisease', axis=1)
y_test = test_feature_matrix['HeartDisease']
# Выбор модели
model = RandomForestClassifier(random_state=42)
# Обучение модели
model.fit(X_train, y_train)
# Предсказание и оценка
y_pred = model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
roc_auc = roc_auc_score(y_test, y_pred)
print(f"Accuracy: {accuracy}")
print(f"Precision: {precision}")
print(f"Recall: {recall}")
print(f"F1 Score: {f1}")
print(f"ROC AUC: {roc_auc}")
# Кросс-валидация
scores = cross_val_score(model, X_train, y_train, cv=5, scoring='accuracy')
accuracy_cv = scores.mean()
print(f"Cross-validated Accuracy: {accuracy_cv}")
# Анализ важности признаков
feature_importances = model.feature_importances_
feature_names = X_train.columns
importance_df = pd.DataFrame({'Feature': feature_names, 'Importance': feature_importances})
importance_df = importance_df.sort_values(by='Importance', ascending=False)
plt.figure(figsize=(10, 6))
sns.barplot(x='Importance', y='Feature', data=importance_df)
plt.title('Feature Importance')
plt.show()
# Проверка на переобучение
y_train_pred = model.predict(X_train)
accuracy_train = accuracy_score(y_train, y_train_pred)
precision_train = precision_score(y_train, y_train_pred)
recall_train = recall_score(y_train, y_train_pred)
f1_train = f1_score(y_train, y_train_pred)
roc_auc_train = roc_auc_score(y_train, y_train_pred)
print(f"Train Accuracy: {accuracy_train}")
print(f"Train Precision: {precision_train}")
print(f"Train Recall: {recall_train}")
print(f"Train F1 Score: {f1_train}")
print(f"Train ROC AUC: {roc_auc_train}")
# Визуализация результатов
plt.figure(figsize=(10, 6))
plt.scatter(y_test, y_pred, alpha=0.5)
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'k--', lw=2)
plt.xlabel('Actual HeartDisease')
plt.ylabel('Predicted HeartDisease')
plt.title('Actual vs Predicted HeartDisease')
plt.show()