396 KiB
Лабораторная работа №2¶
Анализ нескольких датасетов¶
Инсульты¶
Данный датасет используется для предсказания вероятности возникновения инсульта у пациента на основе различных параметров, таких как пол, возраст, наличие заболеваний и статус курения. Инсульт является второй по значимости причиной смерти в мире, по данным Всемирной организации здравоохранения (ВОЗ), и ответственен за около 11% всех случаев смерти.
Информация о колонках
- id: уникальный идентификатор пациента (int)
- gender: пол пациента, возможные значения — "Male" (мужчина), "Female" (женщина) или "Other" (другое) (object, строковый)
- age: возраст пациента (float)
- hypertension: наличие гипертензии; 0 — если гипертензии нет, 1 — если гипертензия есть (int)
- heart_disease: наличие сердечных заболеваний; 0 — если заболеваний нет, 1 — если есть (int)
- ever_married: статус брака; "No" (нет) или "Yes" (да) (object, строковый)
- work_type: тип работы; возможные значения — "children" (дети), "Govt_job" (государственная работа), "Never_worked" (никогда не работал), "Private" (частный сектор) или "Self-employed" (самозанятый) (object, строковый)
- Residence_type: тип проживания; "Rural" (сельская местность) или "Urban" (городская местность) (object, строковый)
- avg_glucose_level: средний уровень глюкозы в крови (float)
- bmi: индекс массы тела (ИМТ) (float)
- smoking_status: статус курения; возможные значения — "formerly smoked" (курил раньше), "never smoked" (никогда не курил), "smokes" (курит) или "Unknown" (неизвестно). Значение "Unknown" указывает на недоступность информации о статусе курения пациента (object, строковый)
- stroke: наличие инсульта; 1 — если инсульт был, 0 — если не был (int)
Каждая строка в датасете содержит соответствующую информацию о пациенте, что позволяет проводить анализ и строить модели для предсказания риска инсульта.
import pandas as pd
strokes = pd.read_csv("healthcare-dataset-stroke-data.csv")
strokes
strokes.dtypes
Автомобили¶
Данный датасет используется для предсказания цены автомобиля на основе различных параметров, таких как производитель, модель, год выпуска и другие характеристики.
Информация о колонках
- ID: уникальный идентификатор автомобиля (int)
- Price: цена автомобиля (целевой столбец) (int)
- Levy: налог или сбор, связанный с автомобилем (obect, строковый)
- Manufacturer: производитель автомобиля (obect, строковый)
- Model: модель автомобиля (obect, строковый)
- Prod. year: год производства (int)
- Category: категория автомобиля (obect, строковый)
- Leather interior: наличие кожаного салона (да/нет) (obect, строковый)
- Fuel type: тип топлива (бензин, дизель и т.д.) (obect, строковый)
- Engine volume: рабочий объем двигателя (obect, строковый)
- Mileage: пробег автомобиля (obect, строковый)
- Cylinders: количество цилиндров в двигателе (float)
- Gear box type: тип коробки передач (механическая, автоматическая и т.д.) (obect, строковый)
- Drive wheels: тип привода (передний, задний, полный) (obect, строковый)
- Doors: количество дверей (obect, строковый)
- Wheel: расположение руля (левосторонний, правосторонний) (obect, строковый)
- Color: цвет автомобиля (obect, строковый)
- Airbags: наличие подушек безопасности (int)
Каждая строка в датасете содержит соответствующую информацию о автомобиле, что позволяет проводить анализ и строить модели для предсказания его цены.
auto = pd.read_csv("car_price_prediction.csv")
auto
auto.dtypes
Магазины¶
Информация о колонках
- Store ID: уникальный идентификатор конкретного магазина (индекс) (int)
- Store_Area: физическая площадь магазина в квадратных ярдах (int)
- Items_Available: количество различных товаров, доступных в соответствующем магазине (int)
- Daily_Customer_Count: среднее количество клиентов, посещающих магазины за месяц (int)
- Store_Sales: объем продаж (в долларах США), полученный магазинами (int)
Каждая строка в датасете содержит соответствующую информацию о магазине, что позволяет проводить анализ и строить модели для оценки его работы.
shop = pd.read_csv("Stores.csv")
shop
shop.dtypes
3. Провести анализ содержимого каждого набора данных. Что является объектом/объектами наблюдения? Каковы атрибуты объектов? Есть ли связи между объектами?¶
- Датасет о риске инсульта
Объект наблюдения: Пациенты.
Атрибуты перечисленны выше.
- Датасет с ценами автомобилей
Объект наблюдения: Автомобили.
Атрибуты перечисленны выше.
- Датасет супермакета
Объект наблюдения: Магазины супермаркета.
Атрибуты перечисленны выше.
4. Привести примеры бизнес-целей, для достижения которых могут подойти выбранные наборы данных. Каков эффект для бизнеса?¶
- Датасет о риске инсульта
Бизнес-цель: Разработка системы раннего предупреждения инсульта на основе анализа данных пациентов.
Эффект для бизнеса:
Улучшение здоровья пациентов: Снижение числа инсультов за счет раннего выявления рисков.
Снижение затрат: Уменьшение расходов на лечение инсульта и реабилитацию.
- Датасет для прогнозирования цен на автомобили
Бизнес-цель: Оптимизация ценообразования и улучшение стратегии продаж автомобилей.
Эффект для бизнеса:
Увеличение прибыли: Установка конкурентоспособных цен на автомобили на основе анализа данных.
Лучшее планирование запасов: Снижение излишков и оптимизация поставок.
- Датасет супермаркета
Бизнес-цель: Оптимизация ассортимента и улучшение обслуживания клиентов на основе анализа посещаемости и продаж.
Эффект для бизнеса:
Увеличение объема продаж: Подбор товаров, наиболее популярных среди клиентов. Снижение затрат: Оптимизация площади магазина и распределения товаров. Повышение клиентской удовлетворенности: Улучшение опыта покупок за счет более эффективной организации товаров и обслуживания.
5. Привести примеры целей технического проекта для каждой выделенной ранее бизнес-цели. Что поступает на вход, что является целевым признаком?¶
- Датасет о риске инсульта
Бизнес-цель: Разработка системы раннего предупреждения инсульта.
Цель технического проекта: Создание модели машинного обучения для прогнозирования вероятности инсульта.
Входные данные:
Пол Возраст Наличие гипертензии Наличие сердечных заболеваний Статус брака Тип работы Тип проживания Средний уровень глюкозы Индекс массы тела Статус курения и так далее
Целевой признак: Наличие инсульта (stroke).
- Датасет для прогнозирования цен на автомобили
Бизнес-цель: Оптимизация ценообразования и улучшение стратегии продаж автомобилей.
Цель технического проекта: Построение модели для предсказания цены автомобиля на основе характеристик.
Входные данные:
Производитель Модель Год производства Категория Налог Наличие кожаного салона Тип топлива Рабочий объем двигателя Пробег Количество цилиндров Тип коробки передач и так далее
Целевой признак: Цена автомобиля (Price).
- Датасет супермаркета
Бизнес-цель: Оптимизация ассортимента и улучшение обслуживания клиентов.
Цель технического проекта: Разработка аналитической платформы для анализа посещаемости и продаж.
Входные данные:
Физическая площадь магазина Количество доступных товаров Среднее количество клиентов Объем продаж и так далее
Целевой признак: Объем продаж (Store_Sales)
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
# 1. Проверка на зашумленность ---- количество пропусков в процентах от общего кол-ва
def check_noise(dataframe):
total_values = dataframe.size
missing_values = dataframe.isnull().sum().sum()
noise_percentage = (missing_values / total_values) * 100
return f"Зашумленность: {noise_percentage:.2f}%"
# 2. Проверка на смещение ----- объем уникальных значений внутри определнной колонки
def check_bias(dataframe, target_column):
if target_column in dataframe.columns:
unique_values = dataframe[target_column].nunique()
total_values = len(dataframe)
bias_percentage = (unique_values / total_values) * 100
return f"Смещение по {target_column}: {bias_percentage:.2f}% уникальных значений"
return "Целевой признак не найден."
# 3. Проверка на дубликаты
def check_duplicates(dataframe):
duplicate_percentage = dataframe.duplicated().mean() * 100
return f"Количество дубликатов: {duplicate_percentage:.2f}%"
# 4. Проверка на выбросы
def check_outliers(dataframe, column):
if column in dataframe.columns:
Q1 = dataframe[column].quantile(0.25)
Q3 = dataframe[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outlier_count = dataframe[(dataframe[column] < lower_bound) | (dataframe[column] > upper_bound)].shape[0]
total_count = dataframe.shape[0]
outlier_percentage = (outlier_count / total_count) * 100
return f"Выбросы по {column}: {outlier_percentage:.2f}%"
return f"Признак {column} не найден."
# 5. Проверка на просачивание данных
def check_data_leakage(dataframe, target_column):
if target_column in dataframe.columns:
correlation_matrix = dataframe.select_dtypes(include=[np.number]).corr()
leakage_info = correlation_matrix[target_column].abs().nlargest(10)
leakage_report = ", ".join([f"{feature}: {value:.2f}" for feature, value in leakage_info.items() if feature != target_column])
return f"Признаки просачивания данных: {leakage_report}"
return "Целевой признак не найден."
noise_columns = check_noise(strokes)
bias_info = check_bias(strokes, 'avg_glucose_level')
duplicate_count = check_duplicates(strokes)
outliers_data = check_outliers(strokes, 'avg_glucose_level')
leakage_info = check_data_leakage(strokes, 'stroke')
print(noise_columns)
print(bias_info)
print(duplicate_count)
print(outliers_data)
print(leakage_info)
##Машины
noise_columns = check_noise(auto)
bias_info = check_bias(auto, 'Price')
duplicate_count = check_duplicates(auto)
outliers_data = check_outliers(auto, 'Prod. year')
leakage_info = check_data_leakage(auto, 'Airbags')
print(noise_columns)
print(bias_info)
print(duplicate_count)
print(outliers_data)
print(leakage_info)
noise_columns = check_noise(shop)
bias_info = check_bias(shop, 'Items_Available')
duplicate_count = check_duplicates(shop)
outliers_data = check_outliers(shop, 'Store_Sales')
leakage_info = check_data_leakage(shop, 'Daily_Customer_Count')
print(noise_columns)
print(bias_info)
print(duplicate_count)
print(outliers_data)
print(leakage_info)
9. Устранить проблему пропущенных данных. Для каждого набора данных использовать разные методы: удаление, подстановка константного значения (0 или подобное), подстановка среднего значения¶
# Инсульт
strokes.isnull().sum()
strokes['bmi'] = strokes['bmi'].fillna(strokes['bmi'].mean())
strokes.isnull().sum()
auto.isnull().sum()
shop.isnull().sum()
# удалить
shop = shop.dropna()
# заполнить значением
shop['Items_Avialable'] = shop['Items_Avialable'].fillna(5000)
10. Выполнить разбиение каждого набора данных на обучающую, контрольную и тестовую выборки¶
from sklearn.model_selection import train_test_split
# Разбиение shop
original_shop_size = len(shop)
train_shop, temp_shop = train_test_split(shop, test_size=0.2, random_state=42)
val_shop, test_shop = train_test_split(temp_shop, test_size=0.5, random_state=42)
print("Shop Dataset:")
print(f"Train: {len(train_shop)/original_shop_size*100:.2f}%")
print(f"Validation: {len(val_shop)/original_shop_size*100:.2f}%")
print(f"Test: {len(test_shop)/original_shop_size*100:.2f}%\n")
# Разбиение strokes
original_strokes_size = len(strokes)
train_strokes, temp_strokes = train_test_split(strokes, test_size=0.2, random_state=42)
val_strokes, test_strokes = train_test_split(temp_strokes, test_size=0.5, random_state=42)
print("Strokes Dataset:")
print(f"Train: {len(train_strokes)/original_strokes_size*100:.2f}%")
print(f"Validation: {len(val_strokes)/original_strokes_size*100:.2f}%")
print(f"Test: {len(test_strokes)/original_strokes_size*100:.2f}%\n")
# Разбиение auto
original_auto_size = len(auto)
train_auto, temp_auto = train_test_split(auto, test_size=0.2, random_state=42)
val_auto, test_auto = train_test_split(temp_auto, test_size=0.5, random_state=42)
print("Auto Dataset:")
print(f"Train: {len(train_auto)/original_auto_size*100:.2f}%")
print(f"Validation: {len(val_auto)/original_auto_size*100:.2f}%")
print(f"Test: {len(test_auto)/original_auto_size*100:.2f}%")
11. Оценить сбалансированность выборок для каждого набора данных. Оценить необходимость использования методов приращения (аугментации) данных.¶
12. Выполнить приращение данных методами выборки с избытком (oversampling) и выборки с недостатком (undersampling). Должны быть представлены примеры реализации обоих методов для выборок каждого набора данных.¶
def plot_sample_balance(y, sample_name):
plt.figure(figsize=(8, 5))
sns.histplot(y, bins=30, kde=True)
plt.title(f'Распределение целевой переменной для {sample_name}')
plt.xlabel(sample_name)
plt.ylabel('Частота')
plt.show()
# Оценка сбалансированности выборок
plot_sample_balance(train_shop['Store_Sales'], 'Train Shop')
plot_sample_balance(val_shop['Store_Sales'], 'Validation Shop')
plot_sample_balance(test_shop['Store_Sales'], 'Test Shop')
Распределения выборок у данного датасета выглядят схоже. Это говорит о сбалансированности выборок.
plot_sample_balance(train_strokes['stroke'], 'Train Strokes')
plot_sample_balance(val_strokes['stroke'], 'Validation Strokes')
plot_sample_balance(test_strokes['stroke'], 'Test Strokes')
Выборки выглядят схоже, но у всех трех имеется явный дисбаланс классов. Это проблема, т.к в дальнейшем не сможем обучить какую-либо модель.
plot_sample_balance(train_auto['Price'], 'Train Auto')
plot_sample_balance(val_auto['Price'], 'Validation Auto')
plot_sample_balance(test_auto['Price'], 'Test Auto')
Распределения выборок у данного датасета выглядят схоже. Это говорит о сбалансированности выборок. Однако в тренировочной выборке значительно больший размах значений
12. Выполнить приращение данных методами выборки с избытком (oversampling) и выборки с недостатком (undersampling). Должны быть представлены примеры реализации обоих методов для выборок каждого набора данных¶
Инсультики¶
from imblearn.over_sampling import SMOTE
X_strokes = strokes.drop('stroke', axis=1)
y_strokes = strokes['stroke']
# Кодирование категориальных признаков
for column in X_strokes.select_dtypes(include=['object']).columns:
X_strokes[column] = X_strokes[column].astype('category').cat.codes
# Теперь применяем SMOTE
smote = SMOTE(random_state=42)
X_resampled_strokes, y_resampled_strokes = smote.fit_resample(X_strokes, y_strokes)
# Получаем результаты
print(f'После oversampling (strokes): {pd.Series(y_resampled_strokes).value_counts()}')
from imblearn.under_sampling import RandomUnderSampler
# Undersampling для strokes
undersample = RandomUnderSampler(random_state=42)
X_under_strokes, y_under_strokes = undersample.fit_resample(X_strokes, y_strokes)
print(f'После undersampling (strokes): {pd.Series(y_under_strokes).value_counts()}')
Машины¶
from imblearn.over_sampling import SMOTE
X_strokes = strokes.drop('stroke', axis=1)
y_strokes = strokes['stroke']
# Кодирование категориальных признаков
for column in X_strokes.select_dtypes(include=['object']).columns:
X_strokes[column] = X_strokes[column].astype('category').cat.codes
# Теперь применяем SMOTE
smote = SMOTE(random_state=42)
X_resampled_strokes, y_resampled_strokes = smote.fit_resample(X_strokes, y_strokes)
# Получаем результаты
print(f'После oversampling (strokes): {pd.Series(y_resampled_strokes).value_counts()}')
from imblearn.under_sampling import RandomUnderSampler
# Undersampling для strokes
undersample = RandomUnderSampler(random_state=42)
X_under_strokes, y_under_strokes = undersample.fit_resample(X_strokes, y_strokes)
print(f'После undersampling (strokes): {pd.Series(y_under_strokes).value_counts()}')
Магазины¶
X_shop = shop.drop('Store_Sales', axis=1)
y_shop = shop['Store_Sales']
# Кодирование категориальных признаков
for column in X_shop.select_dtypes(include=['object']).columns:
X_shop[column] = X_shop[column].astype('category').cat.codes
# Теперь применяем SMOTE
smote = SMOTE(random_state=42)
X_resampled_shop, y_resampled_shop = smote.fit_resample(X_shop, y_shop)
# Получаем результаты
print(f'После oversampling (strokes): {pd.Series(y_resampled_shop).value_counts()}')
from imblearn.under_sampling import RandomUnderSampler
# Undersampling для strokes
undersample = RandomUnderSampler(random_state=42)
X_under_strokes, y_under_strokes = undersample.fit_resample(X_strokes, y_strokes)
print(f'После undersampling (strokes): {pd.Series(y_under_strokes).value_counts()}')
В данном случае у нас есть только один датасет, предназначенный для решения задачи классификации (инсульт). Проблему дисбаланса в нем мы решили применив undersampling & oversampling.
Два остальных датасета не содержат классов, т.к предназначены для решения задачи регрессии (предсказания цен на автомобили или на чек в супермаркете), поэтому выполнять приращение данных не требуется.