Введение

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

В этом блоге мы обсудим, как предсказать возможность инсульта на основе набора данных Health-DataSet-Stroke-Dataset с использованием языка программирования R. Мы начнем с объяснения кода, а затем рассмотрим, как его выполнять, результаты и выводы, сделанные в результате анализа. Наконец, мы поговорим о возможных будущих улучшениях и завершим блог.

Код

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

# install.packages("tidyverse")
# install.packages("corrplot")
# install.packages("randomForest")
library(tidyverse) 
library(psych)
library(dplyr)
library(GGally)
library(corrplot)
library(caret)
library(randomForest)
library(glmnet)

Для выполнения кода необходимо установить следующие пакеты:

  1. Tidyverse
  2. Псих
  3. Дплыр
  4. Ггалли
  5. Корр.участок
  6. каре
  7. RandomForest
  8. Глмнет

Вы можете установить их с помощью функции install.packages(). После того, как вы установили пакеты, вы можете скопировать код в R и запустить его. Убедитесь, что в вашем рабочем каталоге есть набор данных «healthcare-dataset-stroke-data.csv».

# read dataset
healthData <- read.csv("./healthcare-dataset-stroke-data.csv", na.strings=c("", "NA"), header=TRUE)

Этот блок считывает CSV-файл с медицинскими данными и сохраняет его в переменной с именем healthData. Аргумент na.strings указывает, как отсутствующие значения представлены в наборе данных.

Описательный анализ набора данных

cat("Sample Rows from Health Dataset post preprocessing\n")
print(head(healthData))

cat("Descriptive analysis of dataset\n")
cat("Dimensions\n")
print(dim(healthData))
cat("NA values check\n")
print(sum(is.na(healthData)))
cat("Summary\n")
print(summary(healthData))

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

Обработка данных

# Data wrangling
cat("Data wrangling is started\n")
# converting "N/A" to NA
healthData$bmi <- ifelse(healthData$bmi == 'N/A', NA, healthData$bmi)
healthData$stroke <- ifelse(healthData$stroke == 1, 'stroke', 'no stroke') 
healthData$hypertension <- ifelse(healthData$hypertension == 1, 'hypertension', 'no hypertension') 
healthData$heart_disease <- ifelse(healthData$heart_disease == 1, 'heart disease', 'no heart disease') 

# converting into numeric value
healthData$bmi <- as.numeric(healthData$bmi)
healthData$stroke <- factor(healthData$stroke)
healthData$hypertension <- factor(healthData$hypertension)
healthData$heart_disease <- factor(healthData$heart_disease)

Этот блок кода выполняет некоторые задачи обработки данных в исходном наборе данных healthData. Он преобразует строку «Н/Д» в столбце bmi в Н/П. Он также преобразует числовое значение столбцов stroke, hypertension и heart_disease в значение категориального фактора с двумя уровнями; инсульт и отсутствие инсульта, гипертония и отсутствие гипертонии, болезни сердца и отсутствие болезней сердца. Наконец, он преобразует столбец bmi из символьного в числовой.

ЭДА

# EDA
cat("EDA\n")
p <- healthData %>% filter(gender != 'Other') %>% group_by(gender, stroke) %>% 
  summarise(n = n(), 'Average age' = median(age), 
            'smoking share' = round(sum(smoking_status %in% c('formerly smoked', 'smokes')) / n(), 2)  
            )
print(p)
cat("\n")

print(round(describe(healthData[, c('age', 'avg_glucose_level', 'bmi')]), 2))
cat("\n")

p <- healthData %>% filter(work_type == 'children', stroke == 'stroke')
print(p)

cat("\n")
str(healthData)

# Data visualization
p <- ggplot(healthData, aes(x = age)) + geom_histogram(fill = 'gray', col = 'black', binwidth = 7, size = 2)
print(p)

p <- ggplot(healthData, aes(x = age, fill = stroke)) + geom_density(alpha = 0.3)
print(p)

p <- healthData %>% select(age, avg_glucose_level, bmi) %>% drop_na() %>% ggpairs()
print(p)

p <- ggplot(healthData, aes(x=!!sym('smoking_status'), y=age, fill = !!sym('smoking_status'))) + geom_boxplot()
print(p)

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

Статистические тесты

# basic statistic tests
# Gender and stroke frequency
cat("\n")
cat("Gender and stroke frequency\n")
p <- healthData %>% filter(gender != 'Other') %>% select(gender, stroke)  %>% table()
print(p)

cat("\n")
cat("Fisher statistics \n")
marriage_data <- healthData %>% filter(age >= 35, age <= 50) %>% select(ever_married, stroke)
p <- fisher.test(marriage_data$ever_married, marriage_data$stroke)
print(p)

cat("\n")
p <- chisq.test(healthData$Residence_type, healthData$stroke)
print(p)

Этот блок кода выполняет базовые статистические тесты для набора данных healthData. Во-первых, он отображает частоту возникновения инсульта по полу с помощью функции table(). Затем он проводит точный критерий Фишера, используя функцию fisher.test(), чтобы изучить связь между когда-либо состоящим в браке и инсультом среди людей в возрасте 35–50 лет. Наконец, он проводит тест хи-квадрат с использованием функции chisq.test() для изучения связи между типом проживания и возникновением инсульта.

Подготовка набора данных

# converting string into numerical value
numericHealthData <- healthData %>% 
  mutate(gender = case_when(gender == "Male" ~1,
                            gender == "Female" ~0,
                            gender == "Other" ~2),
         ever_married = case_when(ever_married == "Yes" ~1,
                                  ever_married == "No" ~0),
         work_type = case_when(work_type == "children" ~0,
                               work_type == "Never_worked" ~1,
                               work_type == "Private" ~2,
                               work_type == "Govt_job" ~3,
                               work_type == "Self-employed" ~4),
         Residence_type = case_when(Residence_type == "Urban" ~1,
                                    Residence_type == "Rural" ~0),
         smoking_status = case_when(smoking_status == "never smoked" ~0,
                                    smoking_status == "formerly smoked" ~1,
                                    smoking_status == "smokes" ~2,
                                    smoking_status == "Unknown" ~3),
         hypertension = case_when(hypertension =='hypertension' ~1,
                                  hypertension == 'no hypertension' ~0),
         heart_disease = case_when(heart_disease =='heart disease' ~1,
                                  heart_disease == 'no heart disease' ~0),
         stroke = case_when(stroke =='stroke' ~1,
                            stroke == 'no stroke' ~0),
  )

# replacing NA value with mean 
numericHealthData$bmi[is.na(numericHealthData$bmi)] <- mean(numericHealthData$bmi,na.rm=TRUE)

cat("checking the correlations\n")
correlations <- cor(numericHealthData, method = "pearson", use = "complete.obs")
corrplot(correlations, method="circle")

# drop id column
numericHealthData <- numericHealthData[ , !(names(numericHealthData) %in% c("id"))]

data <- numericHealthData[,c("age","hypertension","avg_glucose_level","heart_disease","smoking_status", "stroke")]

Приведенный выше код используется для преобразования категориальных переменных в числовые с помощью функции case_when(). Он также заменяет отсутствующие значения в переменной «bmi» средним значением неотсутствующих значений. Это делается потому, что для большинства статистических анализов требуется числовой ввод, а категориальный ввод должен быть преобразован. Затем выполняется корреляционный анализ результирующих числовых переменных с использованием функции cor() и визуализация с помощью функции corrplot(). Наконец, данные разбиваются на подмножества, чтобы включить только релевантные числовые переменные для дальнейшего анализа.

# splitting dataset into train and test
set.seed(1)

#use 70% of dataset as training set and 30% as test set
sample <- sample(c(TRUE, FALSE), nrow(data), replace=TRUE, prob=c(0.7,0.3))
train  <- data[sample, ]
test   <- data[!sample, ]

Приведенный выше код разбивает набор данных data на обучающий набор и тестовый набор. set.seed(1) устанавливает начальное значение генератора случайных чисел, чтобы обеспечить одно и то же случайное разделение при каждом запуске кода. Функция sample() создает логический вектор значений TRUE и FALSE с вероятностью 0,7 и 0,3 соответственно, чтобы назначить каждую строку набора данных data обучающему или тестовому набору. Результирующие наборы данных train и test составляют 70% и 30% исходного набора данных data соответственно. Цель разделения набора данных — обучить модель на части данных и оценить ее производительность на оставшихся невидимых данных.

Моделирование

cat("Modeling\n")
cat("\nLogistic Regression\n")
logit <- glm(stroke~., family = binomial,data = train)

cat("\n")
print(summary(logit))
cat("\n")
print(summary(logit$fitted.values))

cat("\n")
cat("Predict on Train\n")
pred_train <- predict(logit, type = "response")
pred_train <- ifelse(pred_train >0.25,1,0)
cat("Train Data predicted result\n")
print(head(pred_train))

cat("Predict on Test\n")
pred_test <- predict(logit, newdata = test, type = "response")
pred_test <- ifelse(pred_test >0.25,1,0)
cat("Test Data predicted result\n")
print(head(pred_test))

results <- data.frame(test, pred_test)
print(head(results))

measurePrecisionRecall <- function(predict, actual_labels, name= "Confusion Matrix - Train"){
  a1 <- table(Predictions = predict, TrueLabels = actual_labels)
  fourfoldplot(a1, color = c("#CC6666", "#99CC99"), conf.level = 0, margin = 1, main = name )
  precision <- sum(predict & actual_labels) / sum(predict)
  recall <- sum(predict & actual_labels) / sum(actual_labels)
  fmeasure <- 2 * precision * recall / (precision + recall)
  cat("\n")
  cat("Results\n")
  cat('precision:  ')
  cat(precision * 100)
  cat('%')
  cat('\n')
  
  cat('recall:     ')
  cat(recall * 100)
  cat('%')
  cat('\n')
  
  cat('f-measure:  ')
  cat(fmeasure * 100)
  cat('%')
  cat('\n')
}
measurePrecisionRecall(pred_train, train$stroke)
measurePrecisionRecall(pred_test, test$stroke, "Confusion Matrix - Test")

train$stroke <- as.factor(train$stroke)

cat("\nKNN Classifier\n")
knn <- knn3(stroke ~., data = train,  k = 5)

cat("\n")
print(summary(knn))


cat("\n")
cat("Predict on Train\n")
pred_train <- predict(knn,newdata = train, type = "class")
cat("Train Data predicted result\n")
print(head(pred_train))

cat("Predict on Test\n")
pred_test <- predict(knn, newdata = test, type = "class")
cat("Test Data predicted result\n")
print(head(pred_test))
results <- data.frame(test, pred_test)
print(head(results))

train$stroke <- as.numeric(train$stroke)
test$stroke <- as.numeric(test$stroke)


measurePrecisionRecall(as.numeric(pred_train), train$stroke)
measurePrecisionRecall(as.numeric(pred_test), test$stroke, "Confusion Matrix - Test")

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

  1. Логистическая регрессия: код подбирает модель логистической регрессии к обучающему набору и печатает сводку производительности модели. Затем он прогнозирует инсульт как на тренировочном, так и на тестовом наборах с использованием подобранной модели. Предсказанные значения преобразуются в двоичные (1 или 0) с использованием порога 0,25, а результаты распечатываются. Код также вычисляет и печатает точность, полноту и F-меру прогнозов модели, используя функцию measurePrecisionRecall().
  2. Классификатор KNN: код подбирает классификатор KNN к обучающему набору и печатает сводку производительности модели. Затем он прогнозирует инсульт как на тренировочном, так и на тестовом наборах с использованием подобранной модели. Прогнозируемые значения преобразуются в двоичные (1 или 0) с использованием порога 0,5, а результаты распечатываются. Код также вычисляет и печатает точность, полноту и F-меру прогнозов модели, используя функцию measurePrecisionRecall().

Результаты и выводы

Анализ набора данных Health-DataSet-Stroke-DataSet показал, что:

  1. Средний возраст пациентов, перенесших инсульт, был выше, чем у тех, у кого инсульта не было.
  2. Пациенты, перенесшие инсульт, имели более высокий средний уровень глюкозы и более низкий ИМТ, чем те, у кого не было инсульта.
  3. Количество больных, перенесших инсульт, было выше в возрастной группе 50–70 лет.
  4. Процент курящих пациентов выше среди тех, кто перенес инсульт, чем среди тех, у кого инсульта не было.
  5. Частота инсульта у женщин выше, чем у мужчин.
  6. Частота инсульта выше среди тех, у кого есть гипертония и болезни сердца.
  7. Тест хи-квадрат показал, что существует значительная связь между типом проживания и инсультом.

Будущие улучшения

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

Еще один способ улучшить модель — настроить параметры алгоритма машинного обучения. В этом примере мы использовали параметр по умолчанию для модели случайного леса, но мы могли бы точно настроить параметры модели случайного леса, чтобы повысить точность модели.

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

Заключение

В этом сообщении блога мы обсудили, как прогнозировать инсульты на основе данных об инсульте из набора медицинских данных. Мы продемонстрировали, как использовать код R для предварительной обработки данных, выполнения исследовательского анализа данных, построения модели машинного обучения и оценки производительности модели. Точность модели оценивалась с использованием различных показателей, таких как точность, полнота, точность и оценка F1. выбор признаков и использование ансамблевого обучения.

Я надеюсь, что эта запись в блоге была информативной и помогла понять, как предсказывать инсульты с помощью программирования R.

Оставайтесь с нами, чтобы узнать больше!

Я всегда рад общаться со своими подписчиками и читателями в LinkedIn. Если у вас есть какие-либо вопросы или вы просто хотите поздороваться, пожалуйста, не стесняйтесь обращаться к нам.

https://www.linkedin.com/in/sharmasaravanan/

Приятного обучения!

Прощай, мне нравится!! 🤗🤗