2024-10-19 00:25:57 +04:00

188 KiB
Raw Blame History

Лабораторная 2

Первый датасет: информация о состоянии людей

In [121]:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

df = pd.read_csv("..\\static\\csv\\heart_2020_cleaned.csv")
print(df.columns)
Index(['HeartDisease', 'BMI', 'Smoking', 'AlcoholDrinking', 'Stroke',
       'PhysicalHealth', 'MentalHealth', 'DiffWalking', 'Sex', 'AgeCategory',
       'Race', 'Diabetic', 'PhysicalActivity', 'GenHealth', 'SleepTime',
       'Asthma', 'KidneyDisease', 'SkinCancer'],
      dtype='object')

Столбцы на русском:

HeartDisease - сердечная недостаточность
BMI - ИМТ
Smoking - курящий ли человек
AlcoholDrinking - выпивающий ли человек
Stroke - был ли инсульт
PhysicalHealth - физическое здоровье
MentalHealth - ментальное здоровье
DiffWalking - проблемы с ходьбой
Sex - пол
AgeCategory - возрастная категория
Race - раса
Diabetic - диабетик ли человек
PhysicalActivity - физическая активность
GenHealth - общее здоровье
SleepTime - время сна
Asthma - астматик ли человек
KidneyDisease - нефропатия
SkinCancer - рак кожи

In [122]:
df.info()
df.head()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 319795 entries, 0 to 319794
Data columns (total 18 columns):
 #   Column            Non-Null Count   Dtype  
---  ------            --------------   -----  
 0   HeartDisease      319795 non-null  object 
 1   BMI               319795 non-null  float64
 2   Smoking           319795 non-null  object 
 3   AlcoholDrinking   319795 non-null  object 
 4   Stroke            319795 non-null  object 
 5   PhysicalHealth    319795 non-null  float64
 6   MentalHealth      319795 non-null  float64
 7   DiffWalking       319795 non-null  object 
 8   Sex               319795 non-null  object 
 9   AgeCategory       319795 non-null  object 
 10  Race              319795 non-null  object 
 11  Diabetic          319795 non-null  object 
 12  PhysicalActivity  319795 non-null  object 
 13  GenHealth         319795 non-null  object 
 14  SleepTime         319795 non-null  float64
 15  Asthma            319795 non-null  object 
 16  KidneyDisease     319795 non-null  object 
 17  SkinCancer        319795 non-null  object 
dtypes: float64(4), object(14)
memory usage: 43.9+ MB
Out[122]:
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 [123]:
mean_menthalhealth = df.groupby('AgeCategory')['SleepTime'].mean().reset_index()

plt.figure(figsize=(14, 6))

plt.plot(mean_menthalhealth['AgeCategory'], mean_menthalhealth['SleepTime'], marker='.')

plt.title("Диаграмма 1")
plt.xlabel("Возрастная группа")
plt.ylabel("Время сна")


plt.show()
No description has been provided for this image

Между атрибутами присутствует связь. Пример, на диаграмме 1 - связь между возрастной группой и временем сна
Примеры бизнес-целей:
1. Прогнозирование инсульта на основе ИМТ.
2. Наблюдение за изменением времени сна в зависимости от возраста.

Эффект для бизнеса: влияние количества сна на здоровье, влияние ИМТ на здоровье, влияние возраста на инсульты


Цели технического проекта:
1. Первая бизнес-цель: вход - ИМТ, целевой признак - инсульт.
2. Вторая бизнес-цель: вход - возрастная группа, целевой признак - время сна.

Проверка на выбросы

In [149]:
null_values = df.isnull().sum()
print("Пустые значения по столбцам:")
print(null_values)

print("\nСтатистический обзор данных:")
df.describe()
Пустые значения по столбцам:
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

Статистический обзор данных:
Out[149]:
BMI PhysicalHealth MentalHealth SleepTime
count 319795.000000 319795.00000 319795.000000 319795.000000
mean 28.325399 3.37171 3.898366 7.097075
std 6.356100 7.95085 7.955235 1.436007
min 12.020000 0.00000 0.000000 1.000000
25% 24.030000 0.00000 0.000000 6.000000
50% 27.340000 0.00000 0.000000 7.000000
75% 31.420000 2.00000 3.000000 8.000000
max 94.850000 30.00000 30.000000 24.000000

На основе полученной информации видно, что пустых данных нет. Проверим данные на выбросы и дубликаты:

In [150]:
for column in df.select_dtypes(include=[np.number]).columns:
    skewness = df[column].skew()
    print(f"\nКоэффициент асимметрии для столбца '{column}': {skewness}")

duplicates = df.duplicated().sum()
print(f"\nКоличество дубликатов: {duplicates}")
Коэффициент асимметрии для столбца 'BMI': 1.3324306428979513

Коэффициент асимметрии для столбца 'PhysicalHealth': 2.6039732622480822

Коэффициент асимметрии для столбца 'MentalHealth': 2.331111549136165

Коэффициент асимметрии для столбца 'SleepTime': 0.6790346208011537

Количество дубликатов: 18078

На основе данных выше можно сказать, что выбросы незначительны. Удаляем все дубликаты.

In [151]:
cleaned_df = df.drop_duplicates()

Очищаем данные от шумов

In [152]:
plt.figure(figsize=(10, 6))
plt.scatter(cleaned_df['PhysicalHealth'], cleaned_df['BMI'])
plt.xlabel('Физическое здоровье')
plt.ylabel('ИМТ')
plt.title('Диаграмма рассеивания перед чисткой')
plt.show()

Q1 = cleaned_df["PhysicalHealth"].quantile(0.25)
Q3 = cleaned_df["PhysicalHealth"].quantile(0.75)

IQR = Q3 - Q1

threshold = 1.5 * IQR
lower_bound = Q1 - threshold
upper_bound = Q3 + threshold

outliers = (cleaned_df["PhysicalHealth"] < lower_bound) | (cleaned_df["PhysicalHealth"] > upper_bound)

print("Выбросы в датасете:")
print(cleaned_df[outliers])

median_score = cleaned_df["PhysicalHealth"].median()
cleaned_df.loc[outliers, "PhysicalHealth"] = median_score

plt.figure(figsize=(10, 6))
plt.scatter(cleaned_df['PhysicalHealth'], cleaned_df['BMI'])
plt.xlabel('Физическое здоровье')
plt.ylabel('ИМТ')
plt.title('Диаграмма рассеивания после чистки')
plt.show()
No description has been provided for this image
Выбросы в датасете:
       HeartDisease    BMI Smoking AlcoholDrinking Stroke  PhysicalHealth  \
2                No  26.58     Yes              No     No            20.0   
4                No  23.71      No              No     No            28.0   
5               Yes  28.87     Yes              No     No             6.0   
6                No  21.63      No              No     No            15.0   
10              Yes  34.30     Yes              No     No            30.0   
...             ...    ...     ...             ...    ...             ...   
319774           No  20.36      No              No     No            30.0   
319779           No  23.38     Yes              No     No            30.0   
319782           No  31.89     Yes              No     No            30.0   
319787           No  36.54      No              No     No             7.0   
319790          Yes  27.41     Yes              No     No             7.0   

        MentalHealth DiffWalking     Sex AgeCategory      Race Diabetic  \
2               30.0          No    Male       65-69     White      Yes   
4                0.0         Yes  Female       40-44     White       No   
5                0.0         Yes  Female       75-79     Black       No   
6                0.0          No  Female       70-74     White       No   
10               0.0         Yes    Male       60-64     White      Yes   
...              ...         ...     ...         ...       ...      ...   
319774           0.0         Yes  Female       55-59  Hispanic      Yes   
319779           0.0         Yes  Female       70-74  Hispanic       No   
319782          30.0         Yes  Female       55-59  Hispanic       No   
319787           0.0          No    Male       30-34  Hispanic       No   
319790           0.0         Yes    Male       60-64  Hispanic      Yes   

       PhysicalActivity  GenHealth  SleepTime Asthma KidneyDisease SkinCancer  
2                   Yes       Fair        8.0    Yes            No         No  
4                   Yes  Very good        8.0     No            No         No  
5                    No       Fair       12.0     No            No         No  
6                   Yes       Fair        4.0    Yes            No        Yes  
10                   No       Poor       15.0    Yes            No         No  
...                 ...        ...        ...    ...           ...        ...  
319774              Yes       Fair        8.0     No            No         No  
319779              Yes       Fair        5.0     No            No         No  
319782               No       Fair        4.0     No            No         No  
319787               No       Good        9.0     No            No         No  
319790               No       Fair        6.0    Yes            No         No  

[47136 rows x 18 columns]
No description has been provided for this image

Разбиение набора данных на обучающую, контрольную и тестовую выборки

In [153]:
from sklearn.model_selection import train_test_split

train_df, test_df = train_test_split(cleaned_df, test_size=0.2, random_state=42)

train_df, val_df = train_test_split(train_df, test_size=0.25, random_state=42)

print("Размер обучающей выборки:", len(train_df))
print("Размер контрольной выборки:", len(val_df))
print("Размер тестовой выборки:", len(test_df))
Размер обучающей выборки: 181029
Размер контрольной выборки: 60344
Размер тестовой выборки: 60344

Данные недостаточно сбалансированны

In [154]:
def check_balance(df, name):
    counts = df['BMI'].value_counts()
    print(f"Распределение ИМТ в {name}:")
    print(counts)
    print()

check_balance(train_df, "обучающей выборке")
check_balance(val_df, "контрольной выборке")
check_balance(test_df, "тестовой выборке")
Распределение ИМТ в обучающей выборке:
BMI
26.63    1941
27.46    1456
27.44    1416
27.12    1258
24.41    1247
         ... 
55.95       1
54.56       1
53.72       1
32.29       1
69.88       1
Name: count, Length: 3243, dtype: int64

Распределение ИМТ в контрольной выборке:
BMI
26.63    657
27.46    494
24.41    474
27.44    463
25.10    379
        ... 
43.03      1
55.56      1
44.14      1
16.97      1
39.19      1
Name: count, Length: 2483, dtype: int64

Распределение ИМТ в тестовой выборке:
BMI
26.63    646
27.44    506
27.46    475
24.41    452
27.12    426
        ... 
16.53      1
13.54      1
41.06      1
54.28      1
39.91      1
Name: count, Length: 2539, dtype: int64

In [155]:
from imblearn.over_sampling import RandomOverSampler
from imblearn.under_sampling import RandomUnderSampler

def binning(target, bins):
    return pd.qcut(target, q=bins, labels=False)

train_df['BMI_binned'] = binning(train_df['BMI'], bins=2)
val_df['BMI_binned'] = binning(val_df['BMI'], bins=2)
test_df['BMI_binned'] = binning(test_df['BMI'], bins=2)

def oversample(df, target_column):
    X = df.drop(target_column, axis=1)
    y = df[target_column]
    
    oversampler = RandomOverSampler(random_state=42)
    x_resampled, y_resampled = oversampler.fit_resample(X, y)
    
    resampled_df = pd.concat([x_resampled, y_resampled], axis=1) 
    return resampled_df

def undersample(df, target_column):
    X = df.drop(target_column, axis=1)
    y = df[target_column]
    
    undersampler = RandomUnderSampler(random_state=42)
    x_resampled, y_resampled = undersampler.fit_resample(X, y)
    
    resampled_df = pd.concat([x_resampled, y_resampled], axis=1)
    return resampled_df

train_df_oversampled = oversample(train_df, 'BMI_binned')
val_df_oversampled = oversample(val_df, 'BMI_binned')
test_df_oversampled = oversample(test_df, 'BMI_binned')

train_df_undersampled = undersample(train_df, 'BMI_binned')
val_df_undersampled = undersample(val_df, 'BMI_binned')
test_df_undersampled = undersample(test_df, 'BMI_binned')

print("Оверсэмплинг:")
check_balance(train_df_oversampled, "обучающей выборке")
check_balance(val_df_oversampled, "контрольной выборке")
check_balance(test_df_oversampled, "тестовой выборке")

print("Андерсэмплинг:")
check_balance(train_df_undersampled, "обучающей выборке")
check_balance(val_df_undersampled, "контрольной выборке")
check_balance(test_df_undersampled, "тестовой выборке")
Оверсэмплинг:
Распределение ИМТ в обучающей выборке:
BMI
26.63    1941
27.46    1472
27.44    1432
27.12    1258
24.41    1247
         ... 
27.13       1
29.59       1
24.76       1
53.72       1
31.03       1
Name: count, Length: 3243, dtype: int64

Распределение ИМТ в контрольной выборке:
BMI
26.63    657
27.46    496
24.41    474
27.44    465
25.10    379
        ... 
46.66      1
46.76      1
68.59      1
73.39      1
54.57      1
Name: count, Length: 2483, dtype: int64

Распределение ИМТ в тестовой выборке:
BMI
26.63    646
27.44    510
27.46    479
24.41    452
27.12    426
        ... 
46.72      1
60.69      1
47.44      1
53.48      1
58.16      1
Name: count, Length: 2539, dtype: int64

Андерсэмплинг:
Распределение ИМТ в обучающей выборке:
BMI
26.63    1929
27.46    1456
27.44    1416
27.12    1238
24.41    1238
         ... 
56.30       1
41.59       1
59.08       1
61.66       1
65.19       1
Name: count, Length: 3241, dtype: int64

Распределение ИМТ в контрольной выборке:
BMI
26.63    657
27.46    494
24.41    473
27.44    463
27.12    376
        ... 
48.40      1
63.67      1
48.68      1
39.19      1
30.76      1
Name: count, Length: 2483, dtype: int64

Распределение ИМТ в тестовой выборке:
BMI
26.63    639
27.44    506
27.46    475
24.41    444
27.12    423
        ... 
34.89      1
30.75      1
41.06      1
54.28      1
39.91      1
Name: count, Length: 2539, dtype: int64