Обнаружение диабета с высокой точностью для лучшего лечения пациента

Диабет — это хроническое (длительное) состояние здоровья, которое влияет на то, как ваше тело превращает пищу в энергию. Ваше тело расщепляет большую часть пищи, которую вы едите, на сахар (глюкозу) и выбрасывает его в кровоток. Когда уровень сахара в крови повышается, это сигнализирует поджелудочной железе о необходимости высвобождения инсулина. Инсулин действует как ключ, позволяющий сахару крови проникать в клетки вашего тела для использования в качестве энергии.

При диабете ваше тело не вырабатывает достаточно инсулина или не может использовать его должным образом. Когда инсулина не хватает или клетки перестают реагировать на инсулин, в крови остается слишком много сахара. Со временем это может вызвать серьезные проблемы со здоровьем, такие как болезнь сердца, потеря зрения и заболевание почек.

Диабет в Индии

Диабет — это хроническое заболевание, которое возникает либо тогда, когда поджелудочная железа не вырабатывает достаточного количества инсулина, либо когда организм не может эффективно использовать вырабатываемый им инсулин. Инсулин – это гормон, который регулирует уровень сахара в крови. Гипергликемия, или повышенный уровень сахара в крови, является частым следствием неконтролируемого диабета и со временем приводит к серьезному повреждению многих систем организма, особенно нервов и кровеносных сосудов.

В Индии по оценкам, 77 миллионов человек старше 18 лет страдают диабетом (тип 2) и почти 25 миллионов находятся в преддиабетическом состоянии (с повышенным риском развития диабет в ближайшем будущем). Более 50% людей не знают о своем диабетическом статусе, что приводит к осложнениям со здоровьем, если его не выявить и не лечить на ранней стадии.

Взрослые с диабетом имеют в два-три раза повышенный риск сердечных приступов и инсультов. В сочетании со сниженным кровотоком невропатия (повреждение нервов) в ногах увеличивает вероятность язв стопы, инфекции и, в конечном итоге, необходимости ампутации конечности. Диабетическая ретинопатия является важной причиной слепоты и возникает в результате длительного накопленного повреждения мелких кровеносных сосудов сетчатки. Диабет является одной из ведущих причин почечной недостаточности.

Лекарства от диабета пока нет, но похудение, здоровое питание и активный образ жизни могут действительно помочь. Другие вещи, которые вы можете сделать, чтобы помочь:

Таким образом, Раннее выявление диабета может спасти множество жизней. Давайте приступим к процессу, с помощью которого мы можем создать модель для прогнозирования диабета с помощью некоторых показателей организма (таких как беременность, глюкоза, кровяное давление, возраст и т. д.), используя Машинное обучение.

Сбор данных:

Набор данных, используемый для этого проекта, взят из Kaggle. Этот исходный набор данных был предоставлен Национальным институтом диабета, болезней органов пищеварения и почек. Набор данных и код для этого проекта доступны в моем репозитории GitHub. Этот набор данных используется для прогнозирования вероятности развития диабета у пациента на основе таких входных параметров, как возраст, уровень глюкозы, кровяное давление, инсулин, ИМТ и т. д. Каждая строка данных содержит соответствующую информацию о пациенте. Давайте прочитаем набор данных.

diabetes = pd.read_csv("D:/Software/DATA SCIENCE/All Courses/Capstone Projects/Data-Science-Capstone-Projects-master/Project 2/Healthcare - Diabetes/health_care_diabetes.csv")

Описание набора данных: переменные с описанием

Беременности: количество беременностей

Глюкоза: Концентрация глюкозы в плазме через 2 часа в пероральном тесте на толерантность к глюкозе.

Артериальное давление: диастолическое артериальное давление (мм рт.ст.)

Толщина кожи: Толщина кожной складки трицепса (мм)

Инсулин: 2-часовой сывороточный инсулин (мЕд/мл)

ИМТ: индекс массы тела (вес в кг/(рост в м)²)

Функция родословной диабета: указывает функцию, которая оценивает вероятность диабета на основе семейного анамнеза.

Возраст: Возраст (лет)

Результат: переменная класса (0 или 1) 268 из 768 равны 1, остальные равны 0

(Для подробного кода с выводом посетите мой GitHub)

Исследуйте набор данных:

Давайте изучим набор данных для некоторых полезных идей.

diabetes.info()
# Performing descriptive analysis
diabetes.describe()

После применения функции информации и описания к набору данных он показывает, что данные не имеют нулевых значений, но мы можем видеть, что некоторые столбцы, такие как «Глюкоза», «Кровавое давление», «Толщина кожи», «Инсулин», «ИМТ» имеют нулевые значения, что очень непрактично, поэтому они рассматриваются как пропущенные значения.

Таким образом, мы заменим эти нулевые значения каждого столбца на среднее значение этого столбца отдельно. При этом все значения выглядят как нормальные жизненные значения.

# Replacing 0 value of specific columns with their column mean
diabetes['Glucose'] = diabetes['Glucose'].replace(0,diabetes['Glucose'].mean())
diabetes['BloodPressure'] = diabetes['BloodPressure'].replace(0,diabetes['BloodPressure'].mean())
diabetes['SkinThickness'] = diabetes['SkinThickness'].replace(0,diabetes['SkinThickness'].mean())
diabetes['Insulin'] = diabetes['Insulin'].replace(0,diabetes['Insulin'].mean())
diabetes['BMI'] = diabetes['BMI'].replace(0,diabetes['BMI'].mean())

Визуальное изучение переменных и поиск распределения этих переменных с помощью гистограмм. Мы также проверим асимметрию набора данных.

diabetes['SkinThickness'].skew()
diabetes['Insulin'].skew()
diabetes['BMI'].skew()

Из приведенных выше гистограмм и значений асимметрии видно, что распределение столбцов «Толщина кожи», «Инсулин» и «ИМТ» наклонено вправо. Правостороннее распределение длиннее с правой стороны своего пика, чем с левой.

Таким образом, мы должны лечить асимметрию набора данных. Существует несколько методов, таких как логарифмическое преобразование, преобразование квадратного корня, преобразование Бокса-Кокса и т. д. Здесь мы используем логарифмическое преобразование для устранения асимметрии набора данных для всех столбцов с искаженными данными.

SkinThickness_log = np.log(diabetes['SkinThickness'])
SkinThickness_log.skew()
sns.distplot(SkinThickness_log)

Итак, мы видим, что после преобразования журнала данные теперь имеют почти нормальное распределение. Мы добавим те новые столбцы в наш набор данных, которые были созданы после обработки журнала преобразования / асимметрии.

Теперь давайте проверим выбросы с помощью визуализации Boxplot.

Из приведенного выше наблюдения видно, что почти в каждом столбце есть выбросы, поэтому мы должны их обработать. Мы будем обрабатывать выбросы с помощью метода межквартильного диапазона / метода IQR.

# IQR (Interquartile range ) technique for outlier treatment
def outlier_treatment(col_log):
    sorted(col_log)
    Q1,Q3 = np.percentile(col_log , [25,75])
    IQR = Q3 - Q1
    lower_range = Q1 - (1.5 * IQR)
    upper_range = Q3 + (1.5 * IQR)
    return lower_range,upper_range
# Treatment with IQR
lower_index = list(diabetes[ diabetes['BloodPressure'] < lower_range ].index)

upper_index = list(diabetes[ diabetes['BloodPressure'] > upper_range ].index)

total_index = list(lower_index + upper_index)
diabetes.drop(total_index, inplace = True)
sns.boxplot(x=diabetes['BloodPressure'])

Мы применим функцию outlier_treatment ко всем столбцам, в которых мы обнаружили выбросы, чтобы удалить их, как и выше для столбца артериального давления.

Теперь мы создадим график количества (частоты), описывающий типы данных и количество переменных.

for i in range(0,len(col_log)):
        plt.figure(figsize=(15,8))
        sns.countplot(diabetes[col_log[i]],hue= diabetes['Outcome'])
        plt.tight_layout()
        plt.show()

Создайте точечные диаграммы между парой переменных, чтобы понять взаимосвязь:

sns.scatterplot(data=diabetes, x="Insulin_log", y="Age", hue="Outcome", size=None ,legend='auto')

Из приведенного выше графика рассеяния видно, что с повышением уровня инсулина и в пожилом возрасте шансы заболеть диабетом увеличиваются

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

Из приведенного выше наблюдения ясно, что при низком уровне глюкозы и инсулина вероятность развития диабета в основном отсутствует, но вероятность увеличивается с увеличением уровня глюкозы

Из приведенного выше наблюдения видно, что в молодом возрасте, когда уровень ИМТ высок, есть вероятность диабета. Опять же, в более старшем возрасте и низкий ИМТ также может быть причиной диабета

Давайте проведем корреляционный анализ. Визуально изучите корреляцию между переменными с помощью тепловой карты:

plt.figure(figsize=(15,10))
sns.heatmap(diabetes.corr(),annot=True,linewidths=.5,center=0,cbar=True,cmap="YlGnBu")
plt.show()

Это все, что касается части EDA. Давайте перейдем к части построения модели.

Выбор модели и сборка:

На данный момент мы знаем, что данные несбалансированы. Позволяет визуально изучить его

Итак, мы видим, что значение 1/положительный результат почти вдвое меньше значения 0/отрицательный результат. Таким образом, набор данных несбалансирован.

Большинство алгоритмов машинного обучения работают лучше всего, когда количество выборок в каждом классе примерно равно. Это связано с тем, что большинство алгоритмов предназначены для максимальной точности и уменьшения ошибок.

Однако, если набор данных несбалансирован, то в таких случаях мы получаем довольно высокую точность, просто предсказывая класс большинства, но мы не можем захватить класс меньшинства, что чаще всего является целью создания модели в первую очередь.

Один из широко распространенных методов дисбаланса классов для работы с сильно несбалансированными наборами данных называется повторная выборка. Он заключается в удалении образцов из основного класса (недостаточная выборка) и/или добавлении дополнительных примеров из меньшинства (избыточная выборка).

Простейшей реализацией избыточной выборки является дублирование случайных записей из миноритарного класса, что может привести к переоснащению.

При недостаточной выборке простейший метод заключается в удалении случайных записей из основного класса, что может привести к потере информации.

Существует много других методов и библиотек Python, таких как imbalance-learn, которые помогают справиться с несбалансированным набором данных.

Здесь мы собираемся использовать Перевыборку, так как данных очень мало. Случайная недостаточная выборка может привести к потере важных данных.

Борьба с несбалансированным набором данных

from imblearn.over_sampling import RandomOverSampler
ros = RandomOverSampler(sampling_strategy='not majority')
x_res,y_res = ros.fit_resample(x,y)

ax = y_res.value_counts().plot.pie(autopct ='%.2f')
_ = ax.set_title('Over Sampling')

Теперь мы видим, что набор данных сбалансирован. Давайте воспользуемся набором данных для моделирования, но сначала масштабируем набор данных.

# Splitting Resampled dataset in train and test data
x_train, x_test, y_train, y_test = train_test_split(x_res, y_res, test_size=0.30, random_state=1)
# There seems to be a case of large number & small numbers - scaling / Normalization may need to be done

from sklearn import preprocessing
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import scale

scaler = StandardScaler()
scaler.fit(diabetes)
print(scaler.mean_)
scaled_diab = pd.DataFrame(scaler.transform(diabetes),columns = diabetes.columns)
scaled_diab

# scale the train and test data differently to prevent data leaking

x_train_scaled = preprocessing.scale(x_train)
x_test_scaled = preprocessing.scale(x_test)
# Calculate the p-value of logistic regression from statsmodel for feature selection
# Logistic regression pvalue is used to test the null hypothesis and its coefficient is equal to zero. 
#The lowest pvalue is <0.05 and this lowest value indicates that you can reject the null hypothesis which is good for model.

from scipy.stats import norm
def logit_pvalue(model, x_test_scaled):
   
    p = model.predict_proba(x)
    n1 = len(p)
    m1 = len(model.coef_[0]) + 1
    coefs = np.concatenate([model.intercept_, model.coef_[0]])
    x_full = np.matrix(np.insert(np.array(x), 0, 1, axis = 1))
    answ = np.zeros((m1, m1))
    for i in range(n1):
        answ = answ + np.dot(np.transpose(x_full[i, :]), x_full[i, :]) * p[i,1] * p[i, 0]
    vcov = np.linalg.inv(np.matrix(answ))
    se = np.sqrt(np.diag(vcov))
    t1 =  coefs/se  
    p = (1 - norm.cdf(abs(t1))) * 2
    return p



#print(logit_pvalue(model, x_train_scaled))

import statsmodels.api as sd
sd_model = sd.Logit(y_train, sd.add_constant(x_train_scaled)).fit(disp=0)
print(sd_model.pvalues)
sd_model.summary()

Статистические наблюдения:

Из приведенного выше наблюдения видно, что значение p меньше 0,05, поэтому мы можем отклонить нулевую гипотезу.

Здесь мы собираемся построить модель логистической регрессии и модель случайного леса. Поскольку мы знаем, что методы ансамбля, такие как деревья решений, более способны работать с несбалансированным набором данных.

Затем мы сравним точность двух моделей с моделью классификации KNN.

Перекрестная проверка в основном используется в прикладном машинном обучении для оценки навыков модели машинного обучения на невидимых данных. То есть использовать ограниченную выборку, чтобы оценить, как модель, как ожидается, будет работать в целом, когда она используется для прогнозирования данных, не используемых во время обучения модели.

Здесь мы будем использовать K-кратную перекрестную проверку, потому что наш набор данных несбалансирован. Если точность используется для измерения качества модели, модель, которая классифицирует все тестовые образцы как «0», будет иметь превосходную точность, но, очевидно, эта модель не предоставит нам никакой ценной информации. Итак, мы проверим случайным образом с помощью K-fold и увидим точность модели.

Создание модели логистической регрессии для точного прогнозирования диабета:

# Fit/Train the model with scaled data

model1 = LogisticRegression()
model1.fit(x_train_scaled, y_train)

# Predicting result from the model 
y_predict = model1.predict(x_test_scaled)
train_model_score = model1.score(x_train_scaled, y_train)
test_model_score = model1.score(x_test_scaled, y_test)
print("LogisticRegression Model Efficiency Percentage in training data = ",train_model_score.mean()*100)
print("LogisticRegression Model Efficiency Percentage in testing data = ",test_model_score.mean()*100)

Процент эффективности модели логистической регрессии в обучающих данных = 75% Процент эффективности модели логистической регрессии в данных тестирования = 77%

from sklearn.model_selection import cross_val_score
from sklearn import model_selection
from sklearn.model_selection import KFold

# Cross Validation with K-fold
kfold = model_selection.KFold(n_splits=10, random_state=7,shuffle=True)
results = model_selection.cross_val_score(model1, x, y, cv=kfold)
print("Cross validation score of LogisticRegression Model with K-fold : " ,results.mean()*100)

Оценка перекрестной проверки модели логистической регрессии с K-кратностью: 75%

# calculate Confusion Matrix
# IMPORTANT: first argument is true values, second argument is predicted values
# this produces a 2x2 numpy array (matrix)
cm = confusion_matrix(y_test, y_predict)
disp = ConfusionMatrixDisplay(confusion_matrix=cm)
disp.plot()
plt.show()

Анализ матрицы путаницы

True Positives (TP): Модель правильно предсказала диабет для 104 человек.

True Negatives (TN): модель правильно предсказала отсутствие диабета для 102 человек.

Ложные срабатывания (FP): модель неправильно предсказала, что у людей действительно есть диабет («ошибка I типа») для 20 человек.

Ложноотрицательные результаты (FN): модель неверно предсказала, что у людей нет диабета («ошибка II типа») для 42 человек.

### Чтобы оценить, насколько хорошо модель логистической регрессии соответствует набору данных, мы можем рассмотреть следующие две метрики:

Чувствительность: вероятность того, что модель предсказывает положительный результат для наблюдения, когда результат действительно положительный. Это также называется «истинно положительным показателем».

Специфичность: вероятность того, что модель предсказывает отрицательный результат для наблюдения, когда результат действительно отрицательный. Это также называется «истинной отрицательной ставкой».

Один из способов визуализировать эти две метрики — создать кривую ROC, которая означает кривую «рабочей характеристики приемника».

Это график, отображающий чувствительность по оси ординат и (1 — специфичность) по оси абсцисс.

Один из способов количественно оценить, насколько хорошо модель логистической регрессии справляется с классификацией данных, — рассчитать AUC, что означает «площадь под кривой».

Чем ближе AUC к 1, тем лучше модель.

#calculate ROC and AUC of LR model
fpr, tpr, _ = metrics.roc_curve(y_test,  y_predict)
auc = metrics.roc_auc_score(y_test, y_predict)

#print AUC score
print("\nScore of Area under Curve / AUC Score for the model is:= ",auc*100)
print("\n")

#create ROC curve
plt.figure(figsize=(10,8))
plt.plot(fpr,tpr,label="AUC="+str(auc),color="blue")
plt.ylabel('True Positive Rate/Sensitivity')
plt.xlabel('False Positive Rate/Specificity')
plt.title("Graph of Sensitivity vs Specificity\n")
plt.legend(loc=4)
plt.show()

Оценка площади под кривой / AUC для модели составляет около 77.

Как известно, чем ближе AUC к 1, тем лучше модель. Здесь AUC составляет около 76–77% для модели, поэтому мы можем интерпретировать модель, это хорошо для прогнозирования диабета.

Чем больше кривая ROC охватывает верхний левый угол графика, тем лучше модель предсказывает вероятность сердечного приступа. Таким образом, здесь мы можем сказать, что модель хороша в предсказаниях.

## Calculating the Standard-errors for logistic regression / Cross Validation
from sklearn.metrics import mean_squared_error
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_log_error

print("mean_absolute_error value for this model is = ",mean_absolute_error(y_test, y_predict))
print("mean_squared_error value for this model is = ",mean_squared_error(y_test, y_predict))
print("mean_squared_log_error value for this model is = ",mean_squared_log_error(y_test, y_predict))

Значение mean_absolute_error для этой модели = 0,23134328358208955 Значение mean_squared_error для этой модели = 0,23134328358208955 Значение mean_squared_log_error для этой модели = 0,11114957784674809

Снова мы можем создать модель для прогнозирования диабета с помощью классификатора случайного леса:

Случайный лес:это метод ансамбля, в котором в качестве моделей используются только деревья решений, и здесь можно ввести случайность (путем начальной загрузки или создания случайных данных из оригинала для бэггинга) в каждой модели DTree.

Чтобы получить наилучшие параметры для нашего классификатора Random Forest, давайте выполним некоторую настройку гиперпараметров и используем параметры, найденные в классификаторе.

# Hyper-parameter tuning by GridSearchCV to get the best hyper-parametrs for RandomForestClassifier

from sklearn.model_selection import GridSearchCV

parameters = {'bootstrap': [True],
 'max_depth': [10, 20, 30, 40, 50],
 'max_features': ['auto', 'sqrt'],
 'min_samples_leaf': [1, 2, 4, 8],
 'n_estimators': [100]}


clf = GridSearchCV(RandomForestClassifier(), parameters, cv = 5, verbose = 2, n_jobs= 4)
clf.fit(x_train_scaled, y_train)

clf.best_params_

{‘bootstrap’: True, ‘max_depth’: 30, ‘max_features’: ‘auto’, ‘min_samples_leaf’: 1, ‘n_estimators’: 100}

# Here we will use the hyper-parametrs found from above
rfcl = RandomForestClassifier(bootstrap=True,max_depth= 30,max_features='auto',min_samples_leaf= 1,n_estimators=100)
rfcl = rfcl.fit(x_train_scaled, y_train)
pred_RF = rfcl.predict(x_test_scaled)
acc_RF = accuracy_score(y_test, pred_RF)
print("Random Forest Model Efficiency Percentage is = ",acc_RF.mean()*100)

Таким образом, модель случайного леса предсказывает сердечный приступ с точностью 76 %.

Создание модели для прогнозирования диабета с помощью K ближайших соседей / классификатора KNN:

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

#Fitting K-NN classifier to the training set  
from sklearn.neighbors import KNeighborsClassifier  
classifier= KNeighborsClassifier(n_neighbors=5, metric='minkowski', p=2 )  
KNN_Model = classifier.fit(x_train_scaled, y_train)
pred_KNN = KNN_Model.predict(x_test_scaled)
acc_KNN = accuracy_score(y_test, pred_KNN)
print("KNN Model Efficiency Percentage is = ",acc_KNN.mean()*100)

Таким образом, модель K ближайших соседей предсказывает сердечный приступ с точностью 75 %

Теперь мы можем сравнить все модели в отчете о классификации.

### Comparing Accuracy Scores of Various used models

print("LogisticRegression Model Efficiency Percentage in training data = ",train_model_score.mean()*100)
print("LogisticRegression Model Efficiency Percentage in testing data = ",test_model_score.mean()*100)
print("Random Forest Model Efficiency Percentage is = ",acc_RF.mean()*100)
print("K Nearest Neighbour Model Efficiency Percentage is = ",acc_KNN.mean()*100)

Итак, мы можем считать, что наиболее эффективной моделью является модель случайного леса с эффективностью около 77%.

Когда мы наблюдаем за оценкой f1 и оценкой отзыва каждой модели, мы видим, что это почти 75%, что означает, что модель будет предсказывать 75% истинного положительного результата. Что очень хорошо, но может быть и лучше. Это связано с несбалансированным набором данных, который мы видели ранее.

Оформить панель инструментов с описанием диабета и его зависимостей с помощью Tableau Desktop: https://public.tableau.com/app/profile/sandipani.tribedi/viz/CapstoneProject-DiabetesDetection/Dashboard1?publish=yes

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

Спасибо за чтение. Будьте здоровы.