228 KiB
import pandas as pd
df = pd.read_csv('../data/heart_2022_no_nans.csv')
df.info()
df.describe().transpose()
df_cleaned = df.drop(columns=list(filter(lambda x: not pd.api.types.is_numeric_dtype(df[x]), df.columns)))
df_cleaned['MentalHealthDays'].unique()
Создадим лингвистические переменные
Входные:
- SleepHours - количество часов сна
- BMI - индекс массы тела
Выходные:
- HadStroke - наличие сердечного приступа
import numpy as np
from skfuzzy import control as ctrl
sleep = ctrl.Antecedent(np.arange(df["SleepHours"].min(), df["SleepHours"].max() + 1, 1), "SleepHours")
bmi = ctrl.Antecedent(np.arange(df["BMI"].min(), df["BMI"].max() + 0.1, 0.1), "BMI")
had_stroke = ctrl.Consequent(np.arange(0, 1 + 0.01, 0.01), "HadStroke")
Произведем настройку
import skfuzzy as fuzz
sleep["low"] = fuzz.zmf(sleep.universe, 0, 6)
sleep["normal"] = fuzz.trapmf(sleep.universe, [4, 6, 10, 12])
sleep["much"] = fuzz.smf(sleep.universe, 10, 14)
sleep.view()
bmi["low"] = fuzz.zmf(bmi.universe, 12, 18.5)
bmi["normal"] = fuzz.trapmf(bmi.universe, [16, 18.5, 25, 30])
bmi["much"] = fuzz.smf(bmi.universe, 25, 40)
bmi.view()
# Определение функций принадлежности для had_stroke
had_stroke["low"] = fuzz.zmf(had_stroke.universe, 0, 0.01) # Низкая вероятность (менее 1%)
had_stroke["moderate"] = fuzz.trapmf(had_stroke.universe, [0.005, 0.01, 0.05, 0.1]) # Умеренная вероятность (1% - 5%)
had_stroke["high"] = fuzz.trapmf(had_stroke.universe, [0.01, 0.05, 0.1, 0.2]) # Высокая вероятность (5% - 10%)
had_stroke["very_high"] = fuzz.smf(had_stroke.universe, 0.1, 0.15) # Очень высокая вероятность (более 10%)
# Визуализация функций принадлежности
had_stroke.view()
Сформируем базу нечетких правил
Объяснение правил:
Правило 1: Если часы сна низкие (low) и ИМТ низкий (low), то вероятность сердечного приступа низкая (low).
Правило 2: Если часы сна низкие (low) и ИМТ нормальный (normal), то вероятность сердечного приступа умеренная (moderate).
Правило 3: Если часы сна низкие (low) и ИМТ высокий (much), то вероятность сердечного приступа высокая (high).
Правило 4: Если часы сна нормальные (normal) и ИМТ низкий (low), то вероятность сердечного приступа низкая (low).
Правило 5: Если часы сна нормальные (normal) и ИМТ нормальный (normal), то вероятность сердечного приступа низкая (low).
Правило 6: Если часы сна нормальные (normal) и ИМТ высокий (much), то вероятность сердечного приступа умеренная (moderate).
Правило 7: Если часы сна высокие (much) и ИМТ низкий (low), то вероятность сердечного приступа низкая (low).
Правило 8: Если часы сна высокие (much) и ИМТ нормальный (normal), то вероятность сердечного приступа низкая (low).
Правило 9: Если часы сна высокие (much) и ИМТ высокий (much), то вероятность сердечного приступа умеренная (moderate).
rules = [
ctrl.Rule(sleep['low'] & bmi['low'], had_stroke['low']),
ctrl.Rule(sleep['low'] & bmi['normal'], had_stroke['moderate']),
ctrl.Rule(sleep['low'] & bmi['much'], had_stroke['high']),
ctrl.Rule(sleep['normal'] & bmi['low'], had_stroke['low']),
ctrl.Rule(sleep['normal'] & bmi['normal'], had_stroke['low']),
ctrl.Rule(sleep['normal'] & bmi['much'], had_stroke['moderate']),
ctrl.Rule(sleep['much'] & bmi['low'], had_stroke['low']),
ctrl.Rule(sleep['much'] & bmi['normal'], had_stroke['low']),
ctrl.Rule(sleep['much'] & bmi['much'], had_stroke['moderate']),
]
Создадим нечеткую систему и добавим созданные нечеткие правила в ее базу знаний
# Создание системы управления и добавление правил
stroke_ctrl = ctrl.ControlSystem(rules)
# Создание симуляции
stroke_simulation = ctrl.ControlSystemSimulation(stroke_ctrl)
# Теперь можно визуализировать правила
stroke_ctrl.view()
stroke_simulation.input['SleepHours'] = 5 # Пример входного значения
stroke_simulation.input['BMI'] = 22 # Пример входного значения
stroke_simulation.compute()
stroke_simulation.print_state()
stroke_p = stroke_simulation.output['HadStroke']
print(stroke_p)
Визуализация функции принадлежности для выходной переменной had_stroke
Функция получена в процессе аккумуляции и используется для дефаззификации значения выходной переменной had_stroke
had_stroke.view(sim=stroke_simulation)
Сравним результаты работы системы с реальными данными
def fuzzy_pred(row):
stroke_simulation.input["SleepHours"] = row["SleepHours"]
stroke_simulation.input["BMI"] = row["BMI"]
stroke_simulation.compute()
return stroke_simulation.output["HadStroke"]
result = df.copy()
result = result.sample(frac=0.01)
result["HadStrokePredicted"] = result.apply(fuzzy_pred, axis=1)
result["HadStroke"] = result.apply(lambda x: 1 if x["HadStroke"] == 'Yes' else 0, axis=1)
result.loc[:, ["SleepHours", "BMI", "HadStroke", "HadStrokePredicted"]]
Оценим эти результаты с помощью метрик для задачи регрессии
import math
from sklearn import metrics
rmetrics = {}
rmetrics["RMSE"] = math.sqrt(
metrics.mean_squared_error(result["HadStroke"], result["HadStrokePredicted"])
)
rmetrics["RMAE"] = math.sqrt(
metrics.mean_absolute_error(result["HadStroke"], result["HadStrokePredicted"])
)
rmetrics["R2"] = metrics.r2_score(
result["HadStroke"], result["HadStrokePredicted"]
)
rmetrics
Вывод
Значение RMSE=0.1889 может считаться относительно небольшим, но его интерпретация зависит от масштаба данных. Если целевая переменная (например, вероятность сердечного приступа) находится в диапазоне от 0 до 1, то ошибка в 0.1889 может быть значительной.
Значение RMAE=0.2516 также указывает на значительные ошибки в предсказаниях. Это подтверждает, что модель плохо справляется с задачей.
Отрицательное значение R² (-0.0014) указывает на то, что модель работает хуже, чем простое предсказание среднего значения целевой переменной. Это явный признак того, что модель не подходит для данных.
Заключение
Исходя из оценок, модель неэффективна для предсказания сердечного приступа на основе выбранных признаков. Необходимо пересмотреть подход к построению модели, улучшить данные и, возможно, использовать более подходящие методы машинного обучения.