1.6 MiB
1. Датасет: Диабет у индейцев Пима¶
https://www.kaggle.com/datasets/uciml/pima-indians-diabetes-database
О наборе данных:¶
Этот набор данных был получен из Национального института диабета, болезней органов пищеварения и почек. Цель набора данных - диагностически предсказать, есть ли у пациента сахарный диабет, на основе определенных диагностических измерений, включенных в набор данных. На выбор этих образцов из более обширной базы данных было наложено несколько ограничений. В частности, все пациенты были женщинами в возрасте не менее 21 года, родом из племени пима.
Таким образом:¶
- Объект наблюдения - женщины племени пима, возрастом от 21 года.
- Атрибуты: Pregnancies, Glucose, BloodPressure, SkinThickness, Insulin, BMI, DiabetesPedigreeFunction, Age, Outcome.
- Проблемная область: Предсказание диабета у пациента на основе измерений.
import pandas as pd
df = pd.read_csv(".//static//csv//diabetes.csv", sep=",")
print('Количество колонок: ' + str(df.columns.size))
print('Колонки: ' + ', '.join(df.columns)+'\n')
df.info()
df.head()
Получение сведений о пропущенных данных¶
Типы пропущенных данных:
- None - представление пустых данных в Python
- NaN - представление пустых данных в Pandas
- '' - пустая строка
# Количество пустых значений признаков
print(df.isnull().sum())
print()
# Есть ли пустые значения признаков
print(df.isnull().any())
print()
# Процент пустых значений признаков
for i in df.columns:
null_rate = df[i].isnull().sum() / len(df) * 100
print(f"{i} процент пустых значений: %{null_rate:.2f}")
Проверим выбросы и устраним их:¶
numeric_columns = ['Pregnancies', 'Glucose', 'BloodPressure', 'SkinThickness', 'Insulin', 'BMI', 'DiabetesPedigreeFunction', 'Age']
for column in numeric_columns:
if pd.api.types.is_numeric_dtype(df[column]): # Проверяем, является ли колонка числовой
q1 = df[column].quantile(0.25) # Находим 1-й квартиль (Q1)
q3 = df[column].quantile(0.75) # Находим 3-й квартиль (Q3)
iqr = q3 - q1 # Вычисляем межквартильный размах (IQR)
# Определяем границы для выбросов
lower_bound = q1 - 1.5 * iqr # Нижняя граница
upper_bound = q3 + 1.5 * iqr # Верхняя граница
# Подсчитываем количество выбросов
outliers = df[(df[column] < lower_bound) | (df[column] > upper_bound)]
outlier_count = outliers.shape[0]
# Устраняем выбросы: заменяем значения ниже нижней границы на саму нижнюю границу, а выше верхней — на верхнюю
df[column] = df[column].apply(lambda x: lower_bound if x < lower_bound else upper_bound if x > upper_bound else x)
print(f"Колонка {column}:")
print(f" Есть выбросы: {'Да' if outlier_count > 0 else 'Нет'}")
print(f" Количество выбросов: {outlier_count}")
print(f" Минимальное значение: {df[column].min()}")
print(f" Максимальное значение: {df[column].max()}")
print(f" 1-й квартиль (Q1): {q1}")
print(f" 3-й квартиль (Q3): {q3}\n")
Постараемся выявить зависимости Outcome от остальных колонок:¶
import matplotlib.pyplot as plt
# Создание диаграмм зависимости
for column in numeric_columns:
plt.figure(figsize=(8, 6)) # Установка размера графика
if pd.api.types.is_numeric_dtype(df[column]): # Проверяем, является ли колонка числовой
# Проверяем, содержит ли колонка только два уникальных значения (0 и 1)
if df[column].nunique() == 2 and set(df[column].unique()).issubset({0, 1}):
# Если да, то строим столбчатую диаграмму
counts = df[column].value_counts() # Считаем количество повторений каждого значения
counts.plot(kind='bar') # Создаем столбчатую диаграмму
plt.title(f'Количество значений для {column}')
plt.xlabel(column)
plt.ylabel('Количество повторений')
else:
# Если колонка числовая, создаем диаграмму рассеяния
plt.scatter(df['Outcome'], df[column], alpha=0.5) # Создаем диаграмму рассеяния
plt.title(f'Зависимость {column} от Outcome')
plt.xlabel('Outcome (0 = нет, 1 = да)')
plt.ylabel(column)
plt.xticks([0, 1]) # Установка меток по оси X
plt.grid() # Добавление сетки для удобства восприятия
else:
# Если колонка не числовая, строим столбчатую диаграмму
counts = df[column].value_counts() # Считаем количество повторений каждого значения
counts.plot(kind='bar') # Создаем столбчатую диаграмму
plt.title(f'Количество значений для {column}')
plt.xlabel(column)
plt.ylabel('Количество повторений')
plt.show() # Отображение графика
Разобьем наш набор на выборки относительно параметра Outcome:¶
# Функция для создания выборок
from sklearn.model_selection import train_test_split
def split_stratified_into_train_val_test(
df_input,
stratify_colname="y",
frac_train=0.6,
frac_val=0.15,
frac_test=0.25,
random_state=None,
):
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
# Вывод распределения количества наблюдений по меткам (классам)
print(df.Outcome.value_counts())
print()
data = df.copy()
df_train, df_val, df_test = split_stratified_into_train_val_test(
data, stratify_colname="Outcome", frac_train=0.60, frac_val=0.20, frac_test=0.20
)
print("Обучающая выборка: ", df_train.shape)
print(df_train.Outcome.value_counts())
counts = df_train['Outcome'].value_counts()
plt.figure(figsize=(2, 2))# Установка размера графика
plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)# Построение круговой диаграммы
plt.title('Распределение классов Outcome в обучающей выборке')# Добавление заголовка
plt.show()# Отображение графика
print("Контрольная выборка: ", df_val.shape)
print(df_val.Outcome.value_counts())
counts = df_val['Outcome'].value_counts()
plt.figure(figsize=(2, 2))
plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)
plt.title('Распределение классов Outcome в контрольной выборке')
plt.show()
print("Тестовая выборка: ", df_test.shape)
print(df_test.Outcome.value_counts())
counts = df_test['Outcome'].value_counts()
plt.figure(figsize=(2, 2))
plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)
plt.title('Распределение классов Outcome в тестовой выборке')
plt.show()
Сбалансируем распределение:¶
- Балансировка данных оверсемплингом. Это метод, увеличивающий число наблюдений в меньшинственном классе для достижения более равномерного распределения классов.
from imblearn.over_sampling import ADASYN
ada = ADASYN()
print("Обучающая выборка: ", df_train.shape)
print(df_train.Outcome.value_counts())
X_resampled, y_resampled = ada.fit_resample(df_train, df_train["Outcome"])
df_train_adasyn = pd.DataFrame(X_resampled)
print("Обучающая выборка после oversampling: ", df_train_adasyn.shape)
print(df_train_adasyn.Outcome.value_counts())
counts = df_train_adasyn['Outcome'].value_counts()
plt.figure(figsize=(2, 2))
plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)
plt.title('Распределение классов Outcome в тренировочной выборке после ADASYN')
plt.show()
df_train_adasyn
- Балансировка данных андерсемплингом. Этот метод помогает сбалансировать выборку, уменьшая количество экземпляров класса большинства, чтобы привести его в соответствие с классом меньшинства.
from imblearn.under_sampling import RandomUnderSampler
rus = RandomUnderSampler()# Создание экземпляра RandomUnderSampler
# Применение RandomUnderSampler
X_resampled, y_resampled = rus.fit_resample(df_train.drop(columns=['Outcome']), df_train['Outcome'])
# Создание нового DataFrame
df_train_undersampled = pd.DataFrame(X_resampled)
df_train_undersampled['Outcome'] = y_resampled # Добавление целевой переменной
# Вывод информации о новой выборке
print("Обучающая выборка после undersampling: ", df_train_undersampled.shape)
print(df_train_undersampled['Outcome'].value_counts())
# Визуализация распределения классов
counts = df_train_undersampled['Outcome'].value_counts()
plt.figure(figsize=(2, 2))
plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)
plt.title('Распределение классов Outcome в тренировочной выборке после Undersampling')
plt.show()
2. Датасет: Данные по инсультам¶
https://www.kaggle.com/datasets/fedesoriano/stroke-prediction-dataset
О наборе данных:¶
По данным Всемирной организации здравоохранения (ВОЗ), инсульт является второй по значимости причиной смертности во всем мире, на его долю приходится примерно 11% от общего числа смертей. Этот набор данных используется для прогнозирования вероятности инсульта у пациента на основе входных параметров, таких как пол, возраст, различные заболевания и статус курильщика. Каждая строка в данных содержит соответствующую информацию о пациенте.
Атрибуты:
- id: уникальный идентификатор
- gender: "Male", "Female" или "Other"
- age: возраст пациента
- hypertension: 0, если у пациента нет артериальной гипертензии, 1, если у пациента есть артериальная гипертензия
- heart_disease: 0, если у пациента нет сердечных заболеваний, 1, если у пациента есть сердечные заболевания
- ever_married: "No" или "Yes"
- work_type: "children", "Govt_jov", "Never_worked", "Private" or "Self-employed"
- Residence_type: "Rural" or "Urban"
- avg_glucose_level: средний уровень глюкозы в крови
- bmi: индекс массы тела
- smoking_status: "formerly smoked", "never smoked", "smokes" или "Unknown"*
- stroke: 1, если у пациента был инсульт, или 0, если нет.
Таким образом:¶
- Объект наблюдения - Реальные пациенты.
- Атрибуты: id, gender, age, hypertension, heart_disease, ever_married, work_type, Residence_type, avg_glucose_level, bmi, smoking_status, stroke.
- Проблемная область: Прогнозирование вероятности инсульта у пациента.
import pandas as pd
df = pd.read_csv(".//static//csv//stroke.csv", sep=",")
print('Количество колонок: ' + str(df.columns.size))
print('Колонки: ' + ', '.join(df.columns)+'\n')
df.info()
df.head()
Получение сведений о пропущенных данных¶
Типы пропущенных данных:
- None - представление пустых данных в Python
- NaN - представление пустых данных в Pandas
- '' - пустая строка
# Количество пустых значений признаков
print(df.isnull().sum())
print()
# Есть ли пустые значения признаков
print(df.isnull().any())
print()
# Процент пустых значений признаков
for i in df.columns:
null_rate = df[i].isnull().sum() / len(df) * 100
print(f"{i} процент пустых значений: %{null_rate:.2f}")
Пропущенные данные существуют. Необходимо заполнить пропуски медианными значениями.¶
Заполнение пропущенных данных:
fillna_df = df.fillna(0)
print(fillna_df.shape)
print(fillna_df.isnull().any())
# Замена пустых данных на 0
df["bmi"] = df["bmi"].fillna(0)
# Вычисляем медиану для колонки "bmi"
median_bmi = df["bmi"].median()
# Заменяем значения 0 на медиану
df.loc[df["bmi"] == 0, "bmi"] = median_bmi
df.tail()
Удалим наблюдения с пропусками:
dropna_df = df.dropna()
print(dropna_df.shape)
print(fillna_df.isnull().any())
df.tail()
Проверим выбросы и усредним их:¶
numeric_columns = ['age', 'avg_glucose_level', 'bmi']
for column in numeric_columns:
if pd.api.types.is_numeric_dtype(df[column]): # Проверяем, является ли колонка числовой
q1 = df[column].quantile(0.25) # Находим 1-й квартиль (Q1)
q3 = df[column].quantile(0.75) # Находим 3-й квартиль (Q3)
iqr = q3 - q1 # Вычисляем межквартильный размах (IQR)
# Определяем границы для выбросов
lower_bound = q1 - 1.5 * iqr # Нижняя граница
upper_bound = q3 + 1.5 * iqr # Верхняя граница
# Подсчитываем количество выбросов
outliers = df[(df[column] < lower_bound) | (df[column] > upper_bound)]
outlier_count = outliers.shape[0]
print(f"Колонка {column}:")
print(f" Есть выбросы: {'Да' if outlier_count > 0 else 'Нет'}")
print(f" Количество выбросов: {outlier_count}")
print(f" Минимальное значение: {df[column].min()}")
print(f" Максимальное значение: {df[column].max()}")
print(f" 1-й квартиль (Q1): {q1}")
print(f" 3-й квартиль (Q3): {q3}\n")
# Устраняем выбросы: заменяем значения ниже нижней границы на саму нижнюю границу, а выше верхней — на верхнюю
df[column] = df[column].apply(lambda x: lower_bound if x < lower_bound else upper_bound if x > upper_bound else x)
Постараемся выявить зависимости Stroke от остальных колонок:¶
Разобьем наш набор на выборки относительно параметра Stroke:¶
import matplotlib.pyplot as plt
# Список колонок для построения графиков
columns = ['gender', 'age', 'hypertension', 'heart_disease', 'ever_married',
'work_type', 'Residence_type', 'avg_glucose_level', 'bmi',
'smoking_status']
# Создание диаграмм зависимости
for column in columns:
plt.figure(figsize=(8, 6)) # Установка размера графика
if pd.api.types.is_numeric_dtype(df[column]): # Проверяем, является ли колонка числовой
# Проверяем, содержит ли колонка только два уникальных значения (0 и 1)
if df[column].nunique() == 2 and set(df[column].unique()).issubset({0, 1}):
# Если да, то строим столбчатую диаграмму
counts = df[column].value_counts() # Считаем количество повторений каждого значения
counts.plot(kind='bar') # Создаем столбчатую диаграмму
plt.title(f'Количество значений для {column}')
plt.xlabel(column)
plt.ylabel('Количество повторений')
else:
# Если колонка числовая, создаем диаграмму рассеяния
plt.scatter(df['stroke'], df[column], alpha=0.5) # Создаем диаграмму рассеяния
plt.title(f'Зависимость {column} от stroke')
plt.xlabel('stroke (0 = нет, 1 = да)')
plt.ylabel(column)
plt.xticks([0, 1]) # Установка меток по оси X
plt.grid() # Добавление сетки для удобства восприятия
else:
# Если колонка не числовая, строим столбчатую диаграмму
counts = df[column].value_counts() # Считаем количество повторений каждого значения
counts.plot(kind='bar') # Создаем столбчатую диаграмму
plt.title(f'Количество значений для {column}')
plt.xlabel(column)
plt.ylabel('Количество повторений')
plt.show() # Отображение графика
# Функция для создания выборок
from sklearn.model_selection import train_test_split
def split_stratified_into_train_val_test(
df_input,
stratify_colname="y",
frac_train=0.6,
frac_val=0.15,
frac_test=0.25,
random_state=None,
):
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
# Вывод распределения количества наблюдений по меткам (классам)
print(df.stroke.value_counts())
print()
data = df.copy()
df_train, df_val, df_test = split_stratified_into_train_val_test(
data, stratify_colname="stroke", frac_train=0.60, frac_val=0.20, frac_test=0.20
)
print("Обучающая выборка: ", df_train.shape)
print(df_train.stroke.value_counts())
counts = df_train['stroke'].value_counts()
plt.figure(figsize=(2, 2))# Установка размера графика
plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)# Построение круговой диаграммы
plt.title('Распределение классов stroke в обучающей выборке')# Добавление заголовка
plt.show()# Отображение графика
print("Контрольная выборка: ", df_val.shape)
print(df_val.stroke.value_counts())
counts = df_val['stroke'].value_counts()
plt.figure(figsize=(2, 2))
plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)
plt.title('Распределение классов stroke в контрольной выборке')
plt.show()
print("Тестовая выборка: ", df_test.shape)
print(df_test.stroke.value_counts())
counts = df_test['stroke'].value_counts()
plt.figure(figsize=(2, 2))
plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)
plt.title('Распределение классов stroke в тестовой выборке')
plt.show()
Сбалансируем распределение:¶
- Балансировка данных оверсемплингом. Это метод, увеличивающий число наблюдений в меньшинственном классе для достижения более равномерного распределения классов.
from imblearn.over_sampling import ADASYN
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder
categorical_features = ['gender', 'ever_married', 'work_type', 'Residence_type'] # Ваши категориальные признаки
numeric_features = ['age', 'hypertension', 'heart_disease', 'avg_glucose_level', 'bmi'] # Ваши числовые признаки
# Создание пайплайна для обработки категориальных данных
preprocessor = ColumnTransformer(
transformers=[
('cat', OneHotEncoder(), categorical_features), # OneHotEncoder для категориальных данных
('num', 'passthrough', numeric_features) # Оставляем числовые колонки без изменений
]
)
# Создание экземпляра ADASYN
ada = ADASYN()
# Преобразование данных с помощью пайплайна
X = preprocessor.fit_transform(df_train.drop(columns=['stroke']))
y = df_train['stroke']
# Применение ADASYN
X_resampled, y_resampled = ada.fit_resample(X, y)
# Создание нового DataFrame
df_train_adasyn = pd.DataFrame(X_resampled)
# Восстанавливаем названия столбцов для DataFrame
ohe_columns = preprocessor.named_transformers_['cat'].get_feature_names_out(categorical_features)
new_column_names = list(ohe_columns) + numeric_features
df_train_adasyn.columns = new_column_names
# Добавление целевой переменной
df_train_adasyn['stroke'] = y_resampled
# Вывод информации о новой выборке
print("Обучающая выборка после oversampling: ", df_train_adasyn.shape)
print(df_train_adasyn['stroke'].value_counts())
# Визуализация
counts = df_train_adasyn['stroke'].value_counts()
plt.figure(figsize=(6, 6))
plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)
plt.title('Распределение классов Stroke в тренировочной выборке после ADASYN')
plt.show()
- Балансировка данных андерсемплингом. Этот метод помогает сбалансировать выборку, уменьшая количество экземпляров класса большинства, чтобы привести его в соответствие с классом меньшинства.
from imblearn.under_sampling import RandomUnderSampler
rus = RandomUnderSampler()# Создание экземпляра RandomUnderSampler
# Применение RandomUnderSampler
X_resampled, y_resampled = rus.fit_resample(df_train.drop(columns=['stroke']), df_train['stroke'])
# Создание нового DataFrame
df_train_undersampled = pd.DataFrame(X_resampled)
df_train_undersampled['stroke'] = y_resampled # Добавление целевой переменной
# Вывод информации о новой выборке
print("Обучающая выборка после undersampling: ", df_train_undersampled.shape)
print(df_train_undersampled['stroke'].value_counts())
# Визуализация распределения классов
counts = df_train_undersampled['stroke'].value_counts()
plt.figure(figsize=(2, 2))
plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)
plt.title('Распределение классов stroke в тренировочной выборке после Undersampling')
plt.show()
3. Датасет: Набор данных для анализа и прогнозирования сердечного приступа¶
https://www.kaggle.com/datasets/kamilpytlak/personal-key-indicators-of-heart-disease
О наборе данных:¶
По данным CDC, болезни сердца являются основной причиной смерти представителей большинства рас в США (афроамериканцев, американских индейцев и коренных жителей Аляски, а также белых). Около половины всех американцев (47%) имеют по крайней мере 1 из 3 основных факторов риска сердечно-сосудистых заболеваний: высокое кровяное давление, высокий уровень холестерина и курение. Другие ключевые показатели включают сахарный диабет, ожирение (высокий ИМТ), недостаточную физическую активность или чрезмерное употребление алкоголя. Выявление и профилактика факторов, оказывающих наибольшее влияние на сердечно-сосудистые заболевания, очень важны в здравоохранении. В свою очередь, достижения в области вычислительной техники позволяют применять методы машинного обучения для выявления "закономерностей" в данных, которые позволяют предсказать состояние пациента.
Таким образом:¶
- Объект наблюдения - представители большинства рас в США
- Атрибуты: HeartDisease, BMI, Smoking, AlcoholDrinking, Stroke, PhysicalHealth(как много дней за месяц вы чувствовали себя плохо), MentalHealth(как много дней за месяц вы чувствовали себя ментально плохо), DiffWalking, Sex, AgeCategory, Race, Diabetic, PhysicalActivity, GenHealth, SleepTime, Asthma, KidneyDisease, SkinCancer.
- Проблемная область: прогнозирование сердечного приступа у человека.
import pandas as pd
df = pd.read_csv(".//static//csv//heart.csv", sep=",")
print('Количество колонок: ' + str(df.columns.size))
print('Колонки: ' + ', '.join(df.columns)+'\n')
df.info()
df.head()
Получение сведений о пропущенных данных¶
Типы пропущенных данных:
- None - представление пустых данных в Python
- NaN - представление пустых данных в Pandas
- '' - пустая строка
# Количество пустых значений признаков
print(df.isnull().sum())
print()
# Есть ли пустые значения признаков
print(df.isnull().any())
print()
# Процент пустых значений признаков
for i in df.columns:
null_rate = df[i].isnull().sum() / len(df) * 100
print(f"{i} процент пустых значений: %{null_rate:.2f}")
Пропущенные данные отсутствуют.¶
Проверим выбросы и усредним их:¶
numeric_columns = ['BMI', 'PhysicalHealth', 'MentalHealth', 'AgeCategory', 'SleepTime']
for column in numeric_columns:
if pd.api.types.is_numeric_dtype(df[column]): # Проверяем, является ли колонка числовой
q1 = df[column].quantile(0.25) # Находим 1-й квартиль (Q1)
q3 = df[column].quantile(0.75) # Находим 3-й квартиль (Q3)
iqr = q3 - q1 # Вычисляем межквартильный размах (IQR)
# Определяем границы для выбросов
lower_bound = q1 - 1.5 * iqr # Нижняя граница
upper_bound = q3 + 1.5 * iqr # Верхняя граница
# Подсчитываем количество выбросов
outliers = df[(df[column] < lower_bound) | (df[column] > upper_bound)]
outlier_count = outliers.shape[0]
print(f"Колонка {column}:")
print(f" Есть выбросы: {'Да' if outlier_count > 0 else 'Нет'}")
print(f" Количество выбросов: {outlier_count}")
print(f" Минимальное значение: {df[column].min()}")
print(f" Максимальное значение: {df[column].max()}")
print(f" 1-й квартиль (Q1): {q1}")
print(f" 3-й квартиль (Q3): {q3}\n")
# Устраняем выбросы: заменяем значения ниже нижней границы на саму нижнюю границу, а выше верхней — на верхнюю
df[column] = df[column].apply(lambda x: lower_bound if x < lower_bound else upper_bound if x > upper_bound else x)
Постараемся выявить зависимости HeartDisease от остальных колонок:¶
Разобьем наш набор на выборки относительно параметра HeartDisease:¶
import matplotlib.pyplot as plt
# Список колонок для построения графиков
columns = ['BMI', 'Smoking', 'AlcoholDrinking', 'Stroke',
'PhysicalHealth', 'MentalHealth', 'DiffWalking', 'Sex',
'AgeCategory', 'Race', 'Diabetic', 'PhysicalActivity', 'GenHealth', 'SleepTime', 'Asthma', 'KidneyDisease', 'SkinCancer']
# Создание диаграмм зависимости
for column in columns:
plt.figure(figsize=(8, 6)) # Установка размера графика
if pd.api.types.is_numeric_dtype(df[column]): # Проверяем, является ли колонка числовой
# Проверяем, содержит ли колонка только два уникальных значения (0 и 1)
if df[column].nunique() == 2 and set(df[column].unique()).issubset({0, 1}):
# Если да, то строим столбчатую диаграмму
counts = df[column].value_counts() # Считаем количество повторений каждого значения
counts.plot(kind='bar') # Создаем столбчатую диаграмму
plt.title(f'Количество значений для {column}')
plt.xlabel(column)
plt.ylabel('Количество повторений')
else:
# Если колонка числовая, создаем диаграмму рассеяния
plt.scatter(df['HeartDisease'], df[column], alpha=0.5) # Создаем диаграмму рассеяния
plt.title(f'Зависимость HeartDisease от {column}')
plt.xlabel('HeartDisease (0 = нет, 1 = да)')
plt.ylabel(column)
plt.xticks([0, 1]) # Установка меток по оси X
plt.grid() # Добавление сетки для удобства восприятия
else:
# Если колонка не числовая, строим столбчатую диаграмму
counts = df[column].value_counts() # Считаем количество повторений каждого значения
counts.plot(kind='bar') # Создаем столбчатую диаграмму
plt.title(f'Количество значений для {column}')
plt.xlabel(column)
plt.ylabel('Количество повторений')
plt.show() # Отображение графика
# Функция для создания выборок
from sklearn.model_selection import train_test_split
def split_stratified_into_train_val_test(
df_input,
stratify_colname="y",
frac_train=0.6,
frac_val=0.15,
frac_test=0.25,
random_state=None,
):
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
# Вывод распределения количества наблюдений по меткам (классам)
print(df.HeartDisease.value_counts())
print()
data = df.copy()
df_train, df_val, df_test = split_stratified_into_train_val_test(
data, stratify_colname="HeartDisease", frac_train=0.60, frac_val=0.20, frac_test=0.20
)
print("Обучающая выборка: ", df_train.shape)
print(df_train.HeartDisease.value_counts())
counts = df_train['HeartDisease'].value_counts()
plt.figure(figsize=(2, 2))# Установка размера графика
plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)# Построение круговой диаграммы
plt.title('Распределение классов HeartDisease в обучающей выборке')# Добавление заголовка
plt.show()# Отображение графика
print("Контрольная выборка: ", df_val.shape)
print(df_val.HeartDisease.value_counts())
counts = df_val['HeartDisease'].value_counts()
plt.figure(figsize=(2, 2))
plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)
plt.title('Распределение классов HeartDisease в контрольной выборке')
plt.show()
print("Тестовая выборка: ", df_test.shape)
print(df_test.HeartDisease.value_counts())
counts = df_test['HeartDisease'].value_counts()
plt.figure(figsize=(2, 2))
plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)
plt.title('Распределение классов HeartDisease в тестовой выборке')
plt.show()
Сбалансируем распределение:¶
- Балансировка данных оверсемплингом. Это метод, увеличивающий число наблюдений в меньшинственном классе для достижения более равномерного распределения классов.
from imblearn.over_sampling import ADASYN
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder
categorical_features = ['Smoking', 'AlcoholDrinking', 'Stroke', 'DiffWalking', 'Sex', 'AgeCategory', 'Race', 'Diabetic', 'PhysicalActivity', 'GenHealth', 'Asthma', 'KidneyDisease', 'SkinCancer'] # Ваши категориальные признаки
numeric_features = ['BMI', 'PhysicalHealth', 'MentalHealth', 'SleepTime'] # Ваши числовые признаки
# Создание пайплайна для обработки категориальных данных
preprocessor = ColumnTransformer(
transformers=[
('cat', OneHotEncoder(), categorical_features), # OneHotEncoder для категориальных данных
('num', 'passthrough', numeric_features) # Оставляем числовые колонки без изменений
]
)
# Создание экземпляра ADASYN
ada = ADASYN()
# Преобразование данных с помощью пайплайна
X = preprocessor.fit_transform(df_train.drop(columns=['HeartDisease']))
y = df_train['HeartDisease']
# Применение ADASYN
X_resampled, y_resampled = ada.fit_resample(X, y)
# Создание нового DataFrame
df_train_adasyn = pd.DataFrame(X_resampled)
# Восстанавливаем названия столбцов для DataFrame
ohe_columns = preprocessor.named_transformers_['cat'].get_feature_names_out(categorical_features)
new_column_names = list(ohe_columns) + numeric_features
df_train_adasyn.columns = new_column_names
# Добавление целевой переменной
df_train_adasyn['HeartDisease'] = y_resampled
# Вывод информации о новой выборке
print("Обучающая выборка после oversampling: ", df_train_adasyn.shape)
print(df_train_adasyn['HeartDisease'].value_counts())
# Визуализация
counts = df_train_adasyn['HeartDisease'].value_counts()
plt.figure(figsize=(6, 6))
plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)
plt.title('Распределение классов HeartDisease в тренировочной выборке после ADASYN')
plt.show()
- Балансировка данных андерсемплингом. Этот метод помогает сбалансировать выборку, уменьшая количество экземпляров класса большинства, чтобы привести его в соответствие с классом меньшинства.
from imblearn.under_sampling import RandomUnderSampler
rus = RandomUnderSampler()# Создание экземпляра RandomUnderSampler
# Применение RandomUnderSampler
X_resampled, y_resampled = rus.fit_resample(df_train.drop(columns=['HeartDisease']), df_train['HeartDisease'])
# Создание нового DataFrame
df_train_undersampled = pd.DataFrame(X_resampled)
df_train_undersampled['HeartDisease'] = y_resampled # Добавление целевой переменной
# Вывод информации о новой выборке
print("Обучающая выборка после undersampling: ", df_train_undersampled.shape)
print(df_train_undersampled['HeartDisease'].value_counts())
# Визуализация распределения классов
counts = df_train_undersampled['HeartDisease'].value_counts()
plt.figure(figsize=(2, 2))
plt.pie(counts, labels=counts.index, autopct='%1.1f%%', startangle=90)
plt.title('Распределение классов HeartDisease в тренировочной выборке после Undersampling')
plt.show()