272 KiB
Лабораторная 3¶
Датасет: Набор данных для анализа и прогнозирования сердечного приступа
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from sklearn.model_selection import train_test_split
from imblearn.over_sampling import RandomOverSampler
from sklearn.preprocessing import StandardScaler
import featuretools as ft
import time
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
df = pd.read_csv(".//static//csv//heart_2020_cleaned.csv")
print(df.columns)
df.info()
df.head()
Бизнес цели и цели технического проекта¶
Улучшение профилактики сердечно-сосудистых заболеваний
Бизнес-цель: Повышение точности прогнозирования риска сердечно-сосудистых заболеваний среди пациентов для более раннего вмешательства и снижения частоты обострений. Определение основных факторов риска, чтобы медперсонал мог предоставлять более целенаправленные рекомендации по улучшению здоровья.
Цель технического проекта: Разработка классификационной модели для предсказания вероятности сердечно-сосудистых заболеваний на основе данных (возраст, индекс массы тела, физическая активность, курение и т. д.), что поможет выделить группы высокого риска. Интеграция этой модели в систему поддержки принятия решений для врачей, чтобы улучшить качество и своевременность рекомендаций.
Снижение расходов на лечение сердечно-сосудистых заболеваний
Бизнес-цель: Оптимизация затрат на лечение сердечно-сосудистых заболеваний путем эффективного распределения ресурсов и проведения профилактических мер среди целевых групп.
Цель технического проекта: Создание системы оценки индивидуального риска сердечно-сосудистых заболеваний для пациентов, которая позволит медицинским учреждениям и страховым компаниям выделять целевые группы для проведения превентивных мероприятий, тем самым сокращая затраты на лечение.
Проверка на пустые значения и дубликаты
null_values = df.isnull().sum()
print("Пустые значения по столбцам:")
print(null_values)
duplicates = df.duplicated().sum()
print(f"\nКоличество дубликатов: {duplicates}")
print("\nСтатистический обзор данных:")
df.describe()
Пустых значений нет, но есть дубликаты, удаляем их
df = df.drop_duplicates()
duplicates = df.duplicated().sum()
print(f"\nКоличество дубликатов: {duplicates}")
Преобразуем строковые значение в столбце 'Сердечный приступ' в числовые значения. Это понадобится для расчёта качества набора признаков.
map_stroke_to_int = {'No': 0, 'Yes': 1}
df['Stroke'] = df['Stroke'].map(map_stroke_to_int).astype('int32')
Создание выборок
# Разделение данных на признаки (X) и целевую переменную (y)
# В данном случае мы хотим предсказать 'stroke'
X = df.drop(columns=['Stroke'])
y = df['Stroke']
# Разбиение данных на обучающую и тестовую выборки
# Сначала разделим на обучающую и тестовую
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
# Затем разделим обучающую выборку на обучающую и контрольную
X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.3)
# Проверка размеров выборок
print("Размер обучающей выборки:", X_train.shape)
print("Размер контрольной выборки:", X_val.shape)
print("Размер тестовой выборки:", X_test.shape)
Оценим сбалансированность выборок
# Функция для анализа сбалансированности
def analyze_balance(y_train, y_val, y_test, y_name):
# Распределение классов
print("Распределение классов в обучающей выборке:")
print(y_train.value_counts(normalize=True))
print("\nРаспределение классов в контрольной выборке:")
print(y_val.value_counts(normalize=True))
print("\nРаспределение классов в тестовой выборке:")
print(y_test.value_counts(normalize=True))
# Создание фигуры и осей для трех столбчатых диаграмм
fig, axes = plt.subplots(1, 3, figsize=(18, 5), sharey=True)
fig.suptitle('Распределение в различных выборках')
# Обучающая выборка
sns.barplot(x=y_train.value_counts().index, y=y_train.value_counts(normalize=True), ax=axes[0])
axes[0].set_title('Обучающая выборка')
axes[0].set_xlabel(y_name)
axes[0].set_ylabel('Доля')
# Контрольная выборка
sns.barplot(x=y_val.value_counts().index, y=y_val.value_counts(normalize=True), ax=axes[1])
axes[1].set_title('Контрольная выборка')
axes[1].set_xlabel(y_name)
# Тестовая выборка
sns.barplot(x=y_test.value_counts().index, y=y_test.value_counts(normalize=True), ax=axes[2])
axes[2].set_title('Тестовая выборка')
axes[2].set_xlabel(y_name)
plt.show()
analyze_balance(y_train, y_val, y_test, 'Stroke')
Выборки несбалансированны. Необходимо сбалансировать обучающую и контрольную выборки, чтобы получить лучшие результаты при обучении модели. Для балансировки применим RandomOverSampler:
ros = RandomOverSampler(random_state=42)
# Применение RandomOverSampler для балансировки выборок
X_train_resampled, y_train_resampled = ros.fit_resample(X_train, y_train)
X_val_resampled, y_val_resampled = ros.fit_resample(X_val, y_val)
# Проверка сбалансированности после RandomOverSampler
analyze_balance(y_train_resampled, y_val_resampled, y_test, 'Stroke')
Перейдем к конструированию признаков. Применим унитарное кодирование категориальных признаков (one-hot encoding), переведя их в бинарные вектора:
# Определение категориальных признаков
categorical_features = ['HeartDisease', 'Smoking', 'AlcoholDrinking',
'DiffWalking', 'Sex', 'AgeCategory',
'Race', 'Diabetic', 'PhysicalActivity', 'GenHealth',
'Asthma', 'KidneyDisease', 'SkinCancer']
# Применение one-hot encoding к обучающей выборке
X_train_encoded = pd.get_dummies(X_train_resampled, columns=categorical_features, drop_first=True)
# Применение one-hot encoding к контрольной выборке
X_val_encoded = pd.get_dummies(X_val_resampled, columns=categorical_features, drop_first=True)
# Применение one-hot encoding к тестовой выборке
X_test_encoded = pd.get_dummies(X_test, columns=categorical_features, drop_first=True)
print(X_train_encoded.head())
Далее применим дискретизацию к числовым признакам
bmi_bins = [0, 18.5, 25, 30, 40, 60]
bmi_labels = ["Underweight", "Normal", "Overweight", "Obese", "Severely Obese"]
X_train_encoded['BMI_binned'] = pd.cut(X_train_encoded['BMI'], bins=bmi_bins, labels=bmi_labels)
X_val_encoded['BMI_binned'] = pd.cut(X_val_encoded['BMI'], bins=bmi_bins, labels=bmi_labels)
X_test_encoded['BMI_binned'] = pd.cut(X_test_encoded['BMI'], bins=bmi_bins, labels=bmi_labels)
print(X_train_encoded.head())
Применим ручной синтез признаков. К примеру, можно создать фактор риска для сердечных заболеваний: комбинированный признак на основе факторов риска, таких как курение, диабет, употребление алкоголя и наличие болезней.
X_train_encoded['RiskFactor'] = ((X_train_encoded['Smoking_Yes'] == True) |
(X_train_encoded['Diabetic_Yes'] == True) |
(X_train_encoded['AlcoholDrinking_Yes'] == True) |
(X_train_encoded['KidneyDisease_Yes'] == True) |
(X_train_encoded['SkinCancer_Yes'] == True)).astype(int)
print(X_train_encoded.head())
Используем масштабирование признаков
numerical_features = ['PhysicalHealth', 'MentalHealth', 'SleepTime']
scaler = StandardScaler()
X_train_encoded[numerical_features] = scaler.fit_transform(X_train_encoded[numerical_features])
X_val_encoded[numerical_features] = scaler.transform(X_val_encoded[numerical_features])
X_test_encoded[numerical_features] = scaler.transform(X_test_encoded[numerical_features])
print(X_train_encoded.head())
И также попробуем сконструировать признаки, используя фреймворк Featuretools:
data = X_train_encoded.copy()
es = ft.EntitySet(id="patients")
es = es.add_dataframe(dataframe_name="strokes_data", dataframe=data, index="index", make_index=True)
feature_matrix, feature_defs = ft.dfs(
entityset=es,
target_dataframe_name="strokes_data",
max_depth=1
)
print(feature_matrix.head())
Оценка качества набора признаков¶
Представим основные оценки качества наборов признаков:
Предсказательная способность (для задачи классификации) Метрики: Accuracy, Precision, Recall, F1-Score, ROC AUC
Методы: Обучение модели на обучающей выборке и оценка на контрольной и тестовой выборках.
Скорость вычисления
Методы: Измерение времени выполнения генерации признаков и обучения модели.
Надежность
Методы: Кросс-валидация, анализ чувствительности модели к изменениям в данных.
Корреляция
Методы: Анализ корреляционной матрицы признаков, удаление мультиколлинеарных признаков.
Цельность
Методы: Проверка логической связи между признаками и целевой переменной, интерпретация результатов модели.
X_train_encoded = pd.get_dummies(X_train_encoded, drop_first=True)
X_val_encoded = pd.get_dummies(X_val_encoded, drop_first=True)
X_test_encoded = pd.get_dummies(X_test_encoded, drop_first=True)
all_columns = X_train_encoded.columns
X_train_encoded = X_train_encoded.reindex(columns=all_columns, fill_value=0)
X_val_encoded = X_val_encoded.reindex(columns=all_columns, fill_value=0)
X_test_encoded = X_test_encoded.reindex(columns=all_columns, fill_value=0)
# Выбор модели
model = RandomForestClassifier(n_estimators=100, random_state=42)
# Начинаем отсчет времени
start_time = time.time()
model.fit(X_train_encoded, y_train_resampled)
# Время обучения модели
train_time = time.time() - start_time
print(f'Время обучения модели: {train_time:.2f} секунд')
# Получение важности признаков
importances = model.feature_importances_
feature_names = X_train_encoded.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)
# Предсказание и оценка
y_pred = model.predict(X_test_encoded)
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_encoded, y_train_resampled, cv=5, scoring='accuracy')
accuracy_cv = scores.mean()
print(f"Cross-validated Accuracy: {accuracy_cv}")
# Анализ важности признаков
feature_importances = model.feature_importances_
feature_names = X_train_encoded.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, 10))
sns.barplot(x='Importance', y='Feature', data=importance_df)
plt.title('Feature Importance')
plt.show()
# Проверка на переобучение
y_train_pred = model.predict(X_train_encoded)
accuracy_train = accuracy_score(y_train_resampled, y_train_pred)
precision_train = precision_score(y_train_resampled, y_train_pred)
recall_train = recall_score(y_train_resampled, y_train_pred)
f1_train = f1_score(y_train_resampled, y_train_pred)
roc_auc_train = roc_auc_score(y_train_resampled, 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}")