AIM-PIbd-32-Shabunov-O-A/lab_3/lab3.ipynb
olshab da44b6c41a Роддом. У мужа рожает жена, он сильно волнуется...
Роддом. У мужа рожает жена, он сильно волнуется.
Выходит медсестра с ребенком на руках, берет его за ногу и бьет головой об пол.
Мужик в афиге.
А медесестра смотрит на него и говорит:
- Шучу, шучу, он мертвым родился.
2024-12-07 13:09:06 +04:00

548 KiB
Raw Permalink Blame History

Лабораторная работа №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: Разработка персонализированных программ профилактики сердечно-сосудистых заболеваний

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

Цели технического проекта:

  1. Построить предиктивную модель машинного обучения для прогнозирования риска сердечного приступа на основе предоставленных данных.
  2. Разработать алгоритм классификации пациентов по группам риска с учетом их образа жизни, состояния здоровья и наследственных факторов.
  3. Выявить наиболее значимые факторы риска для рекомендации адресных изменений в образе жизни.

Бизнес-цель 2: Создание коммерческого продукта для оценки здоровья сотрудников компаний

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

Цели технического проекта:

  1. Разработать инструмент визуализации здоровья сотрудников с использованием анализа ключевых факторов из датасета (например, курение, индекс массы тела, физическая активность).
  2. Обучить и оптимизировать модель прогнозирования вероятности сердечного приступа в зависимости от корпоративного контекста (возрастные группы сотрудников, стрессовые факторы).
  3. Интегрировать предиктивную аналитику в продукт, предоставляющий персонализированные отчеты и рекомендации по здоровью.

Бизнес-цель: Улучшенное прогнозирование цен поможет продавцам устанавливать конкурентные цены, а покупателям — принимать более взвешенные решения о покупке. Это также даст риелторам возможность лучше ориентироваться на рынке и оптимизировать стратегию продажи.

Техническая цель: Прогнозирование цен на жилье

Входные данные: Исторические данные о продажах домов, включая все признаки (количество комнат, площадь, состояние, местоположение и др.).

Целевая переменная: Столбец HeartDisease, который указывает на наличие сердечного приступа у пациента (Yes или No).

In [1]:
import pandas as pd
df = pd.read_csv(".//static//csv//heart_2020_cleaned.csv")
df.head()
Out[1]:
HeartDisease BMI Smoking AlcoholDrinking Stroke PhysicalHealth MentalHealth DiffWalking Sex AgeCategory Race Diabetic PhysicalActivity GenHealth SleepTime Asthma KidneyDisease SkinCancer
0 No 16.60 Yes No No 3.0 30.0 No Female 55-59 White Yes Yes Very good 5.0 Yes No Yes
1 No 20.34 No No Yes 0.0 0.0 No Female 80 or older White No Yes Very good 7.0 No No No
2 No 26.58 Yes No No 20.0 30.0 No Male 65-69 White Yes Yes Fair 8.0 Yes No No
3 No 24.21 No No No 0.0 0.0 No Female 75-79 White No No Good 6.0 No No Yes
4 No 23.71 No No No 28.0 0.0 Yes Female 40-44 White No Yes Very good 8.0 No No No

Устранение проблемы пропущенных данных

Для начала определим, присутствуют ли в датасете пропущенные значения признаков:

In [2]:
# Количество пустых значений признаков
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}")
HeartDisease        0
BMI                 0
Smoking             0
AlcoholDrinking     0
Stroke              0
PhysicalHealth      0
MentalHealth        0
DiffWalking         0
Sex                 0
AgeCategory         0
Race                0
Diabetic            0
PhysicalActivity    0
GenHealth           0
SleepTime           0
Asthma              0
KidneyDisease       0
SkinCancer          0
dtype: int64

HeartDisease        False
BMI                 False
Smoking             False
AlcoholDrinking     False
Stroke              False
PhysicalHealth      False
MentalHealth        False
DiffWalking         False
Sex                 False
AgeCategory         False
Race                False
Diabetic            False
PhysicalActivity    False
GenHealth           False
SleepTime           False
Asthma              False
KidneyDisease       False
SkinCancer          False
dtype: bool

Пропущенных данных в датасете не обнаружено

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

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

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

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

In [3]:
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:
	Есть выбросы: Да
	Количество выбросов: 10396
	Минимальное значение: 12.02
	Максимальное значение: 94.85
	1-й квартиль (Q1): 24.03
	3-й квартиль (Q3): 31.42

Колонка PhysicalHealth:
	Есть выбросы: Да
	Количество выбросов: 47146
	Минимальное значение: 0.0
	Максимальное значение: 30.0
	1-й квартиль (Q1): 0.0
	3-й квартиль (Q3): 2.0

Колонка MentalHealth:
	Есть выбросы: Да
	Количество выбросов: 51576
	Минимальное значение: 0.0
	Максимальное значение: 30.0
	1-й квартиль (Q1): 0.0
	3-й квартиль (Q3): 3.0

Колонка SleepTime:
	Есть выбросы: Да
	Количество выбросов: 4543
	Минимальное значение: 1.0
	Максимальное значение: 24.0
	1-й квартиль (Q1): 6.0
	3-й квартиль (Q3): 8.0

No description has been provided for this image

Признаки BMI и SleepTime имеют достаточное количество выбросов, которое стоит устранить. Также числовые признаки PhysicalHealth и MentalHealth имеют большое количество выбросов, но так как количество таких наблюдений по сравнению с общим количеством объектов велико, а диапазон значений, которые эти признаки принимают, сравнительно небольшой, то удаление такого объема важной информации, как состояние здоровья, может негативно сказаться на способности прогнозировать сердечный приступ.

Для решения проблемы выбросов у признаков BMI и SleepTime воспользуемся методом отсечения слишком отклоняющихся значений путем замены на экстремальное значение соответствующей границы:

In [4]:
# Устранить выборсы в 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)
Проверка наличия выбросов в колонках после их устранения:
Колонка BMI:
	Есть выбросы: Нет
	Количество выбросов: 0
	Минимальное значение: 12.945
	Максимальное значение: 42.505
	1-й квартиль (Q1): 24.03
	3-й квартиль (Q3): 31.42

Колонка SleepTime:
	Есть выбросы: Нет
	Количество выбросов: 0
	Минимальное значение: 3.0
	Максимальное значение: 11.0
	1-й квартиль (Q1): 6.0
	3-й квартиль (Q3): 8.0

No description has been provided for this image

Разбиение датасета на выборки

Разделим выборку данных на 3 группы:

  1. Обучающая выборка (70%).
  2. Контрольная выборка (15%).
  3. Тестовая выборка (15%).
In [5]:
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')
Проверка сбалансированности выборок:
Обучающая выборка:  (191877, 18)
Распределение выборки данных по классам в колонке "HeartDisease":
 HeartDisease
No     175453
Yes     16424
Name: count, dtype: int64
Процент объектов класса "No": 91.44%
Процент объектов класса "Yes": 8.56%

Контрольная выборка:  (63959, 18)
Распределение выборки данных по классам в колонке "HeartDisease":
 HeartDisease
No     58484
Yes     5475
Name: count, dtype: int64
Процент объектов класса "No": 91.44%
Процент объектов класса "Yes": 8.56%

Тестовая выборка:  (63959, 18)
Распределение выборки данных по классам в колонке "HeartDisease":
 HeartDisease
No     58485
Yes     5474
Name: count, dtype: int64
Процент объектов класса "No": 91.44%
Процент объектов класса "Yes": 8.56%

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

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

In [6]:
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')
Проверка сбалансированности выборок:
Обучающая выборка:  (350906, 51)
Распределение выборки данных по классам в колонке "HeartDisease":
 HeartDisease
No     175453
Yes    175453
Name: count, dtype: int64
Процент объектов класса "No": 50.00%
Процент объектов класса "Yes": 50.00%

Контрольная выборка:  (116968, 51)
Распределение выборки данных по классам в колонке "HeartDisease":
 HeartDisease
No     58484
Yes    58484
Name: count, dtype: int64
Процент объектов класса "No": 50.00%
Процент объектов класса "Yes": 50.00%

Тестовая выборка:  (116970, 51)
Распределение выборки данных по классам в колонке "HeartDisease":
 HeartDisease
No     58485
Yes    58485
Name: count, dtype: int64
Процент объектов класса "No": 50.00%
Процент объектов класса "Yes": 50.00%

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

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

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

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

  1. Для категориальных данных:
    • Унитарное кодирование категориальных признаков (one-hot encoding) метод, который применяется для преобразования категориальных переменных в числовой формат. Каждая характеристика представляется в виде бинарного вектора, где для каждой категории выделяется отдельный признак (столбец) со значением 1 (True), если объект принадлежит этой категории, и 0 (False) в противном случае.
  2. Для числовых данных:
    • Дискретизация процесс преобразования непрерывных числовых значений в категориальные группы или интервалы (дискретные значения).
    • Ручной синтез процесс создания новых признаков на основе существующих данных. Это может включать в себя комбинирование нескольких признаков, использование математических операций (например, сложение, вычитание), а также создание полиномиальных или логарифмических признаков.
    • Масштабирование признаков на основе нормировки и стандартизации метод, который позволяет привести все числовые признаки к одинаковым или очень похожим диапазонам значений либо распределениям.
    • С применением фреймворка FeatureTools библиотека для автоматизированного создания признаков (features) из структурированных данных. Подходит для задач машинного обучения, когда нужно быстро извлекать полезные признаки из больших объемов данных.

Унитарное кодирование

Преобразование уже было выполнено на этапе приращения с избытком (метод pd.get_dummies(...)), так как метод fit_resample требовал для работы признаки типа число с плавающей точкой. Были преобразованы категориальные признаки Smoking, AlcoholDrinking, Stroke, DiffWalking и т.д. в бинарные признаки:

In [7]:
categorical_features = [
    'Smoking', 'AlcoholDrinking', 'Stroke', 'DiffWalking', 'Sex', 'AgeCategory', 'Race',
    'Diabetic', 'PhysicalActivity', 'GenHealth', 'Asthma', 'KidneyDisease', 'SkinCancer'
]

df_train_oversampled.head(10)
Out[7]:
BMI PhysicalHealth MentalHealth SleepTime Smoking_No Smoking_Yes AlcoholDrinking_No AlcoholDrinking_Yes Stroke_No Stroke_Yes ... GenHealth_Good GenHealth_Poor GenHealth_Very good Asthma_No Asthma_Yes KidneyDisease_No KidneyDisease_Yes SkinCancer_No SkinCancer_Yes HeartDisease
0 24.28 2.0 3.0 8.0 True False True False True False ... False False True False True True False True False No
1 34.44 0.0 0.0 8.0 False True True False True False ... True False False True False True False True False No
2 25.86 0.0 5.0 8.0 True False True False True False ... False False True True False True False True False No
3 19.47 0.0 2.0 8.0 False True True False True False ... False False True True False True False True False No
4 34.70 0.0 0.0 8.0 False True True False True False ... False False True True False True False True False No
5 29.05 0.0 0.0 6.0 True False False True True False ... True False False True False True False True False No
6 32.45 0.0 5.0 7.0 True False True False True False ... True False False True False True False True False No
7 26.25 0.0 30.0 6.0 False True True False True False ... False False True True False True False True False No
8 30.67 2.0 3.0 7.0 True False True False True False ... True False False True False True False True False No
9 34.96 14.0 0.0 6.0 True False True False True False ... True False False True False True False True False No

10 rows × 51 columns

Дискретизация числовых признаков

Распределим значения признака BMI по интервалам, преобразуя его из числового представления в категориальное. Будем использовать метод Равномерная группировка:

In [8]:
# Функция для дискретизации числовых признаков
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)
Out[8]:
BMI BMI_Category
0 24.280 1
1 34.440 3
2 25.860 2
3 19.470 1
4 34.700 3
5 29.050 2
6 32.450 3
7 26.250 2
8 30.670 2
9 34.960 3
10 27.810 2
11 20.360 1
12 27.400 2
13 42.505 4
14 21.520 1
15 36.260 3
16 23.490 1
17 28.190 2
18 28.290 2
19 20.800 1

Ручной синтез признаков

Будем синтезировать новый признак HealthScore, являющийся числовым показателем здоровья на основе таких признаков, как PhysicalHealth, MentalHealth, SleepTime:

In [9]:
# Рассчитаем новый признак 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)
Out[9]:
BMI PhysicalHealth MentalHealth SleepTime Smoking_No Smoking_Yes AlcoholDrinking_No AlcoholDrinking_Yes Stroke_No Stroke_Yes ... GenHealth_Very good Asthma_No Asthma_Yes KidneyDisease_No KidneyDisease_Yes SkinCancer_No SkinCancer_Yes HeartDisease BMI_Category HealthScore
0 24.28 2.0 3.0 8.0 True False True False True False ... True False True True False True False No 1 21.7
1 34.44 0.0 0.0 8.0 False True True False True False ... False True False True False True False No 3 23.4
2 25.86 0.0 5.0 8.0 True False True False True False ... True True False True False True False No 2 21.9
3 19.47 0.0 2.0 8.0 False True True False True False ... True True False True False True False No 1 22.8
4 34.70 0.0 0.0 8.0 False True True False True False ... True True False True False True False No 3 23.4
5 29.05 0.0 0.0 6.0 True False False True True False ... False True False True False True False No 2 22.8
6 32.45 0.0 5.0 7.0 True False True False True False ... False True False True False True False No 3 21.6
7 26.25 0.0 30.0 6.0 False True True False True False ... True True False True False True False No 2 13.8
8 30.67 2.0 3.0 7.0 True False True False True False ... False True False True False True False No 2 21.4
9 34.96 14.0 0.0 6.0 True False True False True False ... False True False True False True False No 3 17.2

10 rows × 53 columns

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

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

  • Нормировка обычно применяется для равномерного распределения;
  • Стандартизация обычно применяется для нормального распределения.
In [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)
Out[10]:
BMI PhysicalHealth MentalHealth SleepTime
0 0.383457 0.066667 0.100000 0.625
1 0.727165 0.000000 0.000000 0.625
2 0.436908 0.000000 0.166667 0.625
3 0.220737 0.000000 0.066667 0.625
4 0.735961 0.000000 0.000000 0.625
5 0.544824 0.000000 0.000000 0.375
6 0.659844 0.000000 0.166667 0.500
7 0.450101 0.000000 1.000000 0.375
8 0.599628 0.066667 0.100000 0.500
9 0.744756 0.466667 0.000000 0.375
In [32]:
df_train_oversampled_normalized.columns
Out[32]:
Index(['BMI', 'PhysicalHealth', 'MentalHealth', 'SleepTime', 'Smoking_No',
       'Smoking_Yes', 'AlcoholDrinking_No', 'AlcoholDrinking_Yes', 'Stroke_No',
       'Stroke_Yes', 'DiffWalking_No', 'DiffWalking_Yes', 'Sex_Female',
       'Sex_Male', 'AgeCategory_18-24', 'AgeCategory_25-29',
       'AgeCategory_30-34', 'AgeCategory_35-39', 'AgeCategory_40-44',
       'AgeCategory_45-49', 'AgeCategory_50-54', 'AgeCategory_55-59',
       'AgeCategory_60-64', 'AgeCategory_65-69', 'AgeCategory_70-74',
       'AgeCategory_75-79', 'AgeCategory_80 or older',
       'Race_American Indian/Alaskan Native', 'Race_Asian', 'Race_Black',
       'Race_Hispanic', 'Race_Other', 'Race_White', 'Diabetic_No',
       'Diabetic_No, borderline diabetes', 'Diabetic_Yes',
       'Diabetic_Yes (during pregnancy)', 'PhysicalActivity_No',
       'PhysicalActivity_Yes', 'GenHealth_Excellent', 'GenHealth_Fair',
       'GenHealth_Good', 'GenHealth_Poor', 'GenHealth_Very good', 'Asthma_No',
       'Asthma_Yes', 'KidneyDisease_No', 'KidneyDisease_Yes', 'SkinCancer_No',
       'SkinCancer_Yes', 'HeartDisease', 'BMI_Category', 'HealthScore'],
      dtype='object')

Конструирование с применением FeatureTools

In [11]:
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)
/home/oleg/aim_labs/lab_3/aimenv/lib/python3.12/site-packages/woodwork/type_sys/utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  pd.to_datetime(
/home/oleg/aim_labs/lab_3/aimenv/lib/python3.12/site-packages/woodwork/type_sys/utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  pd.to_datetime(
Out[11]:
BMI PhysicalHealth MentalHealth SleepTime Smoking_No Smoking_Yes AlcoholDrinking_No AlcoholDrinking_Yes Stroke_No Stroke_Yes ... BMI_Category * HealthScore BMI_Category * MentalHealth BMI_Category * PhysicalHealth BMI_Category * SleepTime HealthScore * MentalHealth HealthScore * PhysicalHealth HealthScore * SleepTime MentalHealth * PhysicalHealth MentalHealth * SleepTime PhysicalHealth * SleepTime
Id
1 0.383457 0.066667 0.100000 0.625 True False True False True False ... 21.7 0.100000 0.066667 0.625 2.17 1.446667 13.5625 0.006667 0.062500 0.041667
2 0.727165 0.000000 0.000000 0.625 False True True False True False ... 70.2 0.000000 0.000000 1.875 0.00 0.000000 14.6250 0.000000 0.000000 0.000000
3 0.436908 0.000000 0.166667 0.625 True False True False True False ... 43.8 0.333333 0.000000 1.250 3.65 0.000000 13.6875 0.000000 0.104167 0.000000
4 0.220737 0.000000 0.066667 0.625 False True True False True False ... 22.8 0.066667 0.000000 0.625 1.52 0.000000 14.2500 0.000000 0.041667 0.000000
5 0.735961 0.000000 0.000000 0.625 False True True False True False ... 70.2 0.000000 0.000000 1.875 0.00 0.000000 14.6250 0.000000 0.000000 0.000000
6 0.544824 0.000000 0.000000 0.375 True False False True True False ... 45.6 0.000000 0.000000 0.750 0.00 0.000000 8.5500 0.000000 0.000000 0.000000
7 0.659844 0.000000 0.166667 0.500 True False True False True False ... 64.8 0.500000 0.000000 1.500 3.60 0.000000 10.8000 0.000000 0.083333 0.000000
8 0.450101 0.000000 1.000000 0.375 False True True False True False ... 27.6 2.000000 0.000000 0.750 13.80 0.000000 5.1750 0.000000 0.375000 0.000000
9 0.599628 0.066667 0.100000 0.500 True False True False True False ... 42.8 0.200000 0.133333 1.000 2.14 1.426667 10.7000 0.006667 0.050000 0.033333
10 0.744756 0.466667 0.000000 0.375 True False True False True False ... 51.6 0.000000 1.400000 1.125 0.00 8.026667 6.4500 0.000000 0.000000 0.175000

10 rows × 68 columns

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

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

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

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

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

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

In [12]:
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)
Размер обучающей выборки: 156699
Размер контрольной выборки: 67157
Размер тестовой выборки: 95939
/home/oleg/aim_labs/lab_3/aimenv/lib/python3.12/site-packages/featuretools/entityset/entityset.py:1733: UserWarning: index id not found in dataframe, creating new integer column
  warnings.warn(
/home/oleg/aim_labs/lab_3/aimenv/lib/python3.12/site-packages/woodwork/type_sys/utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  pd.to_datetime(
/home/oleg/aim_labs/lab_3/aimenv/lib/python3.12/site-packages/woodwork/type_sys/utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  pd.to_datetime(
/home/oleg/aim_labs/lab_3/aimenv/lib/python3.12/site-packages/featuretools/synthesis/deep_feature_synthesis.py:169: UserWarning: Only one dataframe in entityset, changing max_depth to 1 since deeper features cannot be created
  warnings.warn(
/home/oleg/aim_labs/lab_3/aimenv/lib/python3.12/site-packages/featuretools/computational_backends/feature_set_calculator.py:143: FutureWarning: The behavior of DataFrame concatenation with empty or all-NA entries is deprecated. In a future version, this will no longer exclude empty or all-NA columns when determining the result dtypes. To retain the old behavior, exclude the relevant entries before the concat operation.
  df = pd.concat([df, default_df], sort=True)
/home/oleg/aim_labs/lab_3/aimenv/lib/python3.12/site-packages/featuretools/computational_backends/feature_set_calculator.py:143: FutureWarning: The behavior of DataFrame concatenation with empty or all-NA entries is deprecated. In a future version, this will no longer exclude empty or all-NA columns when determining the result dtypes. To retain the old behavior, exclude the relevant entries before the concat operation.
  df = pd.concat([df, default_df], sort=True)
Feature Importance:
                                feature  importance
0                          HeartDisease    0.851120
1                                   BMI    0.014203
9                             Stroke_No    0.012600
2                        PhysicalHealth    0.008628
12                      DiffWalking_Yes    0.008111
11                       DiffWalking_No    0.007721
36                         Diabetic_Yes    0.007583
10                           Stroke_Yes    0.007551
4                             SleepTime    0.007525
43                       GenHealth_Poor    0.006605
27              AgeCategory_80 or older    0.006269
34                          Diabetic_No    0.005300
3                          MentalHealth    0.005102
41                       GenHealth_Fair    0.004277
48                    KidneyDisease_Yes    0.003435
47                     KidneyDisease_No    0.003086
13                           Sex_Female    0.002607
26                    AgeCategory_75-79    0.002567
25                    AgeCategory_70-74    0.002462
14                             Sex_Male    0.002457
6                           Smoking_Yes    0.002127
5                            Smoking_No    0.001934
42                       GenHealth_Good    0.001787
44                  GenHealth_Very good    0.001734
33                           Race_White    0.001731
50                       SkinCancer_Yes    0.001687
38                  PhysicalActivity_No    0.001658
39                 PhysicalActivity_Yes    0.001585
49                        SkinCancer_No    0.001513
40                  GenHealth_Excellent    0.001451
24                    AgeCategory_65-69    0.001318
46                           Asthma_Yes    0.001315
45                            Asthma_No    0.001256
23                    AgeCategory_60-64    0.001091
30                           Race_Black    0.000885
22                    AgeCategory_55-59    0.000853
31                        Race_Hispanic    0.000825
21                    AgeCategory_50-54    0.000715
32                           Race_Other    0.000699
7                    AlcoholDrinking_No    0.000560
8                   AlcoholDrinking_Yes    0.000550
20                    AgeCategory_45-49    0.000520
28  Race_American Indian/Alaskan Native    0.000503
35     Diabetic_No, borderline diabetes    0.000479
19                    AgeCategory_40-44    0.000444
18                    AgeCategory_35-39    0.000412
17                    AgeCategory_30-34    0.000267
29                           Race_Asian    0.000260
15                    AgeCategory_18-24    0.000231
16                    AgeCategory_25-29    0.000217
37      Diabetic_Yes (during pregnancy)    0.000184
In [13]:
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()
Размер обучающей выборки: 15670
Размер контрольной выборки: 6716
Размер тестовой выборки: 9594
/home/oleg/aim_labs/lab_3/aimenv/lib/python3.12/site-packages/featuretools/entityset/entityset.py:1733: UserWarning: index id not found in dataframe, creating new integer column
  warnings.warn(
/home/oleg/aim_labs/lab_3/aimenv/lib/python3.12/site-packages/woodwork/type_sys/utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  pd.to_datetime(
/home/oleg/aim_labs/lab_3/aimenv/lib/python3.12/site-packages/woodwork/type_sys/utils.py:33: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  pd.to_datetime(
/home/oleg/aim_labs/lab_3/aimenv/lib/python3.12/site-packages/featuretools/computational_backends/feature_set_calculator.py:143: FutureWarning: The behavior of DataFrame concatenation with empty or all-NA entries is deprecated. In a future version, this will no longer exclude empty or all-NA columns when determining the result dtypes. To retain the old behavior, exclude the relevant entries before the concat operation.
  df = pd.concat([df, default_df], sort=True)
/home/oleg/aim_labs/lab_3/aimenv/lib/python3.12/site-packages/featuretools/computational_backends/feature_set_calculator.py:143: FutureWarning: The behavior of DataFrame concatenation with empty or all-NA entries is deprecated. In a future version, this will no longer exclude empty or all-NA columns when determining the result dtypes. To retain the old behavior, exclude the relevant entries before the concat operation.
  df = pd.concat([df, default_df], sort=True)
Accuracy: 1.0
Precision: 1.0
Recall: 1.0
F1 Score: 1.0
ROC AUC: 1.0
Cross-validated Accuracy: 0.906126356094448
No description has been provided for this image
Train Accuracy: 0.9994894703254626
Train Precision: 0.9992816091954023
Train Recall: 0.9949928469241774
Train F1 Score: 0.9971326164874552
Train ROC AUC: 0.9974613898298016
No description has been provided for this image