Dans ce projet engageant de 7 à 9 heures, nous allons endosser le rôle d’analystes de données de santé débutants au sein d’une organisation de premier plan. Notre mission consiste à développer et déployer un modèle de prédiction d’accident vasculaire cérébral (AVC) en utilisant le langage de programmation R. Les résultats de notre travail seront présentés sur notre profil Coursera, offrant ainsi aux potentiels employeurs un aperçu de nos compétences en action.
Objectif du Projet :
L’objectif principal de ce projet est d’explorer un ensemble de données riche en informations sur les patients, en mettant l’accent sur l’identification des caractéristiques cliniques les plus cruciales. En suivant une approche méthodique, nous allons ensuite développer un modèle de prédiction d’AVC, rigoureusement validé pour une utilisation clinique efficace. Enfin, le déploiement du modèle vise à améliorer la prise de décision clinique au sein de notre organisation de santé.
Aperçu des Étapes et Compétences Mises en Valeur :
Exploration des Données : Nous débuterons par une exploration approfondie du jeu de données, incluant le chargement, le nettoyage, la transformation, l’ingénierie des fonctionnalités, et l’imputation des données manquantes.
Construction du Modèle : Nous mettrons en œuvre des modèles de prédiction tels que la régression logistique, la machine à vecteurs de support, les arbres de décision, la forêt aléatoire, et XGBoost. Nous prendrons en considération des aspects tels que le déséquilibre des classes, la normalisation des données et la sélection des hyperparamètres.
Évaluation et Déploiement : Nous évaluerons les modèles en utilisant diverses mesures (précision, sensibilité, rappel, score F, ASC) et déploierons le modèle choisi, que ce soit en tant que point de terminaison d’API ou en tant que page Web.
Ce projet représente une opportunité unique pour nous de démontrer nos compétences dans l’importation, le traitement et l’analyse de données de santé, ainsi que dans la construction et le déploiement de modèles prédictifs avancés.
Alors, sommes-nous prêts à relever le défi et à laisser notre empreinte dans le domaine de l’analyse de données de santé ? Suivons les étapes fournies et démarquons-nous avec notre approche unique dans ce projet passionnant !
Ce fichier RMarkdown contient le rapport de l’analyse des données effectuée pour le projet de création et de déploiement d’un modèle de prédiction des accidents vasculaires cérébraux dans R. Il contient des analyses telles que l’exploration des données, les statistiques récapitulatives et la construction des modèles de prédiction. Le rapport final a été achevé le Sun Feb 25 14:31:51 2024.
Selon l’Organisation mondiale de la santé (OMS), l’accident vasculaire cérébral est la deuxième cause de décès dans le monde, responsable d’environ 11 % du total des décès.
Cet ensemble de données est utilisé pour prédire si un patient est susceptible de subir un accident vasculaire cérébral en fonction de paramètres d’entrée tels que le sexe, l’âge, diverses maladies et le statut tabagique. Chaque ligne des données fournit des informations pertinentes sur le patient.
# charger les packages
library(rmarkdown)
library(readr)
library(dplyr)
library(tidyverse)
library(ggplot2)
library(plotly)
library(hrbrthemes)
library(viridis)
# pour la corrélation
library(heatmaply)
library(ggcorrplot)
# pour le réchantillonnage
library(recipes)
library(themis)
# pour la construction des modèles
library(caret)
library(e1071)
library(glmnet)
library(rpart)
library(ISLR)
library(skimr)
# charger les données
strock = read.csv("/cloud/project/healthcare-dataset-stroke-data.csv")
En appliquant head() sur notre base de données des
patients, nous visualisons les premières lignes, nous offrant ainsi un
aperçu immédiat des enregistrements initiaux. C’est une manière rapide
de comprendre la structure des données et d’identifier les types
d’informations disponibles.
## visualiser les 5 premières lignes
head(strock)
## id gender age hypertension heart_disease ever_married work_type
## 1 9046 Male 67 0 1 Yes Private
## 2 51676 Female 61 0 0 Yes Self-employed
## 3 31112 Male 80 0 1 Yes Private
## 4 60182 Female 49 0 0 Yes Private
## 5 1665 Female 79 1 0 Yes Self-employed
## 6 56669 Male 81 0 0 Yes Private
## Residence_type avg_glucose_level bmi smoking_status stroke
## 1 Urban 228.69 36.6 formerly smoked 1
## 2 Rural 202.21 N/A never smoked 1
## 3 Rural 105.92 32.5 never smoked 1
## 4 Urban 171.23 34.4 smokes 1
## 5 Rural 174.12 24 never smoked 1
## 6 Urban 186.21 29 formerly smoked 1
L’utilisation de tail() sur notre base de données des
patients nous montre les dernières lignes, ce qui peut être utile pour
vérifier la cohérence des données vers la fin de l’ensemble.
## visualiser les 5 derniières lignes
tail(strock)
## id gender age hypertension heart_disease ever_married work_type
## 5105 14180 Female 13 0 0 No children
## 5106 18234 Female 80 1 0 Yes Private
## 5107 44873 Female 81 0 0 Yes Self-employed
## 5108 19723 Female 35 0 0 Yes Self-employed
## 5109 37544 Male 51 0 0 Yes Private
## 5110 44679 Female 44 0 0 Yes Govt_job
## Residence_type avg_glucose_level bmi smoking_status stroke
## 5105 Rural 103.08 18.6 Unknown 0
## 5106 Urban 83.75 N/A never smoked 0
## 5107 Urban 125.20 40 never smoked 0
## 5108 Rural 82.99 30.6 never smoked 0
## 5109 Rural 166.29 25.6 formerly smoked 0
## 5110 Urban 85.28 26.2 Unknown 0
En appliquant str() directement sur notre base de
données des patients, nous obtenons une vue structurée montrant le type
de chaque variable ainsi que les premières valeurs. Cette commande est
précieuse pour comprendre la nature des données et détecter d’éventuels
problèmes de type.
# Structure des données
str(strock)
## 'data.frame': 5110 obs. of 12 variables:
## $ id : int 9046 51676 31112 60182 1665 56669 53882 10434 27419 60491 ...
## $ gender : chr "Male" "Female" "Male" "Female" ...
## $ age : num 67 61 80 49 79 81 74 69 59 78 ...
## $ hypertension : int 0 0 0 0 1 0 1 0 0 0 ...
## $ heart_disease : int 1 0 1 0 0 0 1 0 0 0 ...
## $ ever_married : chr "Yes" "Yes" "Yes" "Yes" ...
## $ work_type : chr "Private" "Self-employed" "Private" "Private" ...
## $ Residence_type : chr "Urban" "Rural" "Rural" "Urban" ...
## $ avg_glucose_level: num 229 202 106 171 174 ...
## $ bmi : chr "36.6" "N/A" "32.5" "34.4" ...
## $ smoking_status : chr "formerly smoked" "never smoked" "never smoked" "smokes" ...
## $ stroke : int 1 1 1 1 1 1 1 1 1 1 ...
En appliquant length() sur une colonne spécifique ou sur
notre base de données des patients, nous obtenons le nombre
d’observations dans cette colonne.
length(strock)
## [1] 12
Lorsque nous utilisons summary() sur notre base de
données des patients, nous obtenons des statistiques sommaires pour les
variables numériques. Cela inclut des informations telles que la
moyenne, la médiane, les valeurs minimales et maximales, et les
quartiles.
summary(strock)
## id gender age hypertension
## Min. : 67 Length:5110 Min. : 0.08 Min. :0.00000
## 1st Qu.:17741 Class :character 1st Qu.:25.00 1st Qu.:0.00000
## Median :36932 Mode :character Median :45.00 Median :0.00000
## Mean :36518 Mean :43.23 Mean :0.09746
## 3rd Qu.:54682 3rd Qu.:61.00 3rd Qu.:0.00000
## Max. :72940 Max. :82.00 Max. :1.00000
## heart_disease ever_married work_type Residence_type
## Min. :0.00000 Length:5110 Length:5110 Length:5110
## 1st Qu.:0.00000 Class :character Class :character Class :character
## Median :0.00000 Mode :character Mode :character Mode :character
## Mean :0.05401
## 3rd Qu.:0.00000
## Max. :1.00000
## avg_glucose_level bmi smoking_status stroke
## Min. : 55.12 Length:5110 Length:5110 Min. :0.00000
## 1st Qu.: 77.25 Class :character Class :character 1st Qu.:0.00000
## Median : 91.89 Mode :character Mode :character Median :0.00000
## Mean :106.15 Mean :0.04873
## 3rd Qu.:114.09 3rd Qu.:0.00000
## Max. :271.74 Max. :1.00000
Lorsque nous utilisons dim() directement sur notre base
de données des patients, nous obtenons les dimensions, c’est-à-dire le
nombre d’observations (lignes) et de variables (colonnes).
dim(strock)
## [1] 5110 12
print(paste("Ainsi, on a dans notre base de données,", (nrow(strock)), "et", (ncol(strock)), "caractéristiques pouvant causées des accidends vasculaires cérébraux"))
## [1] "Ainsi, on a dans notre base de données, 5110 et 12 caractéristiques pouvant causées des accidends vasculaires cérébraux"
La commande names() appliquée à notre base de données
des patients nous donne les noms des colonnes.
names(strock)
## [1] "id" "gender" "age"
## [4] "hypertension" "heart_disease" "ever_married"
## [7] "work_type" "Residence_type" "avg_glucose_level"
## [10] "bmi" "smoking_status" "stroke"
id : Identifiant unique attribué à chaque patient. Il s’agit probablement d’une clé primaire permettant d’identifier de manière unique chaque enregistrement dans la base de données.
gender : Genre du patient, indiquant s’il s’agit d’un homme, d’une femme ou d’une autre catégorie de genre.
age : L’âge du patient, exprimé en années. Cette variable donne une indication sur la tranche d’âge à laquelle appartient chaque individu.
hypertension : Indique la présence (1) ou l’absence (0) d’hypertension chez le patient. L’hypertension est une condition médicale caractérisée par une pression artérielle élevée.
heart_disease :
Indique la présence (1) ou l’absence (0) de maladies cardiaques chez le patient.
ever_married :
Indique si le patient a déjà été marié (Yes) ou non (No). Cette variable offre des informations sur l’état matrimonial du patient.
work_type :
Type d’emploi du patient, fournissant des détails sur le secteur ou la nature de l’activité professionnelle du patient.
Residence_type :
Indique si le patient habite en zone urbaine (Urban) ou rurale (Rural).
avg_glucose_level :
Niveau moyen de glucose dans le sang du patient, mesuré en unités spécifiques. Cette variable peut être importante pour évaluer le risque de certaines conditions médicales.
bmi :
Indice de masse corporelle (BMI) du patient, fournissant des informations sur la composition corporelle en fonction du poids et de la taille.
smoking_status :
Indique le statut tabagique du patient, avec des catégories telles que “formerly smoked,” “never smoked,” “smokes,” ou “Unknown.”
stroke :
Variable cible indiquant la survenue (1) ou non (0) d’un accident vasculaire cérébral chez le patient. C’est probablement la variable que nous cherchons à prédire dans le cadre du projet de prédiction d’AVC.
###Distribution des variables catégorielles
Le genre des patients
# Table de fréquence des genres
eff = table(strock$gender)
# Diagramme à barres
g_genre = ggplot(data = strock, aes(x=strock$gender, fill = factor(strock$stroke))) + geom_bar() + theme(axis.text.x = element_text(angle = 45, hjust = 1)) + coord_flip() + xlab("Le genre des patients") + ylab("Effectif des patients par sexe") + ggtitle("La distribution des patients par genres") + guides(fill=guide_legend(title = "Les victimes d'AVC par sexe"))
ggplotly(g_genre)
## Warning: Use of `strock$gender` is discouraged.
## ℹ Use `gender` instead.
## Warning: Use of `strock$stroke` is discouraged.
## ℹ Use `stroke` instead.
La majorité des ptients qui foont l’objet de notre étude sont du sexe feminin soit 2994 clients et 2115 sont du sexe masculin. Seul 141 femmes sont victimes d’AVC contre 108 hommes pour l’ensemble de l’échantillon.
# Test du chi-deux pour la distribution des genres
chi_genre <- chisq.test(table(strock$gender))
print(chi_genre)
##
## Chi-squared test for given probabilities
##
## data: table(strock$gender)
## X-squared = 2778.8, df = 2, p-value < 2.2e-16
La p-valeur très faible indique que vous pouvez rejeter l’hypothèse nulle selon laquelle il n’y a aucune relation entre les variables (ici, le genre). Il y a des différences significatives entre les fréquences observées et attendues, ce qui suggère une association entre le genre et une autre variable dans votre ensemble de données.
hypertension
# Diagramme à barres pour la variable hypertension
table(strock$hypertension)
##
## 0 1
## 4612 498
g_genre = ggplot(data = strock, aes(x=strock$hypertension, fill = factor(strock$stroke))) + geom_bar() + theme(axis.text.x = element_text(angle = 45, hjust = 1)) + coord_flip() + xlab("L'hypertention") + ylab("Effectif des patients par hypertension") + ggtitle("La distribution de l'hypertension des patients") + guides(fill=guide_legend(title = "Présence ou absence d'AVC chez le patient"))
ggplotly(g_genre)
## Warning: Use of `strock$hypertension` is discouraged.
## ℹ Use `hypertension` instead.
## Warning: Use of `strock$stroke` is discouraged.
## ℹ Use `stroke` instead.
La majorité des patients de l’échantillon ne sont pas victimes d’hypertension artérille. De plus, sur les 4612 qui ne sont pas victime seulement 183 ont l’AVC et sur les 498 qui ont l’hypertension seul 66 ont l’AVC.
# Test du chi-deux pour la distribution des genres
chi_hyp <- chisq.test(table(strock$hypertension))
print(chi_hyp)
##
## Chi-squared test for given probabilities
##
## data: table(strock$hypertension)
## X-squared = 3312.1, df = 1, p-value < 2.2e-16
heart_disease
# Diagramme à barres pour la variable heart_disease
table(strock$heart_disease)
##
## 0 1
## 4834 276
g_heart = ggplot(data = strock, aes(x=strock$heart_disease, fill = factor(strock$stroke))) + geom_bar() + theme(axis.text.x = element_text(angle = 45, hjust = 1)) + coord_flip() + xlab("L'hypertention") + ylab("Effectif des patients par maladie cardiaque") + ggtitle("La distribution des patients par maladie cardiaque") + guides(fill=guide_legend(title = "Présence d'AVC chez le patient"))
ggplotly(g_heart)
## Warning: Use of `strock$heart_disease` is discouraged.
## ℹ Use `heart_disease` instead.
## Warning: Use of `strock$stroke` is discouraged.
## ℹ Use `stroke` instead.
4834 patients n’ont pas de maladie cardique contre 276 qui en sont victimes. Parmi ceux qui sont victimes de maladie cardiaque, seul 47 ont l’AVC. Et pour ceux qui n’ont pas de maladie vasculaire seul 202 patients ont l’AVC.
# Test du chi-deux pour la distribution des genres
chi_heart <- chisq.test(table(strock$heart_disease))
print(chi_hyp)
##
## Chi-squared test for given probabilities
##
## data: table(strock$hypertension)
## X-squared = 3312.1, df = 1, p-value < 2.2e-16
ever_married
# Diagramme à barres pour la variable maried
table(strock$ever_married)
##
## No Yes
## 1757 3353
g_maried = ggplot(data = strock, aes(x=strock$ever_married, fill = factor(strock$stroke))) + geom_bar() + theme(axis.text.x = element_text(angle = 45, hjust = 1)) + coord_flip() + xlab("situation") + ylab("Effectif des patients par situation matrimoniale") + ggtitle("La distribution des patients par stuation matrimoniale") + guides(fill=guide_legend(title = "Présence d'AVC chez le patient"))
ggplotly(g_maried)
## Warning: Use of `strock$ever_married` is discouraged.
## ℹ Use `ever_married` instead.
## Warning: Use of `strock$stroke` is discouraged.
## ℹ Use `stroke` instead.
chi_maried = chisq.test(table(strock$ever_married))
print(chi_maried)
##
## Chi-squared test for given probabilities
##
## data: table(strock$ever_married)
## X-squared = 498.48, df = 1, p-value < 2.2e-16
work_type
# Diagramme à barres pour la variable type de job
table(strock$work_type)
##
## children Govt_job Never_worked Private Self-employed
## 687 657 22 2925 819
g_work = ggplot(data = strock, aes(x=strock$work_type, fill = factor(strock$stroke))) + geom_bar() + theme(axis.text.x = element_text(angle = 45, hjust = 1)) + coord_flip() + xlab("Type d'emploi") + ylab("Effectif des patients par type d'emploi") + ggtitle("La distribution des patients par type d'emploi") + guides(fill=guide_legend(title = "Présence d'AVC chez le patient"))
ggplotly(g_work)
## Warning: Use of `strock$work_type` is discouraged.
## ℹ Use `work_type` instead.
## Warning: Use of `strock$stroke` is discouraged.
## ℹ Use `stroke` instead.
Residence_type
# Diagramme à barres pour la variable residence_type
table(strock$Residence_type)
##
## Rural Urban
## 2514 2596
g_residence = ggplot(data = strock, aes(x=strock$Residence_type, fill = factor(strock$stroke))) + geom_bar() + theme(axis.text.x = element_text(angle = 45, hjust = 1)) + coord_flip() + xlab("Type de résidence") + ylab("Effectif des patients par type de résidence") + ggtitle("La distribution des patients par type de résidence") + guides(fill=guide_legend(title = "Présence d'AVC chez le patient"))
ggplotly(g_residence)
## Warning: Use of `strock$Residence_type` is discouraged.
## ℹ Use `Residence_type` instead.
## Warning: Use of `strock$stroke` is discouraged.
## ℹ Use `stroke` instead.
smoking_status
# Diagramme à barres pour la variable smoking
table(strock$smoking_status)
##
## formerly smoked never smoked smokes Unknown
## 885 1892 789 1544
g_smok = ggplot(data = strock, aes(x=strock$smoking_status, fill = factor(strock$stroke))) + geom_bar() + theme(axis.text.x = element_text(angle = 45, hjust = 1)) + coord_flip() + xlab("Statut tabagique") + ylab("Effectif des patients par statut tabagique") + ggtitle("La distribution des patients par profil") + guides(fill=guide_legend(title = "Présence d'AVC chez le patient"))
ggplotly(g_smok)
## Warning: Use of `strock$smoking_status` is discouraged.
## ℹ Use `smoking_status` instead.
## Warning: Use of `strock$stroke` is discouraged.
## ℹ Use `stroke` instead.
age
g_age <- strock %>% ggplot(aes(y=age), fill="age") + geom_boxplot() + ylab("L'age des patients")+ ggtitle("Distribution de l'age des patients")
ggplotly(g_age)
On observe quelque chose de très pertinent, l’AVC augmente avec l’age. Plus les patients prennent de l’age plus la probabilié d’avoir un AVC est élevée.
De plus, 50% des patients ont moins de 45 ans et 50% on plus de 45 ans. Cependant, il y a aussi des enfants dans notre base des données.
avg_glucose_level
g_glucose = ggplot(data = strock, aes(x=strock$avg_glucose_level, fill = factor(strock$stroke))) + geom_histogram() + theme(axis.text.x = element_text(angle = 45, hjust = 1)) + xlab("Niveau moyen de glucose dans le sang des patients") + ylab("Effectif des patients") + ggtitle("La distribution des patients par niveau moyen de glucose dans le sang") + guides(fill=guide_legend(title = "Présence d'AVC chez le patient"))
ggplotly(g_glucose)
## Warning: Use of `strock$avg_glucose_level` is discouraged.
## ℹ Use `avg_glucose_level` instead.
## Warning: Use of `strock$stroke` is discouraged.
## ℹ Use `stroke` instead.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
avg_glucose_level
strock$bmi <- as.numeric(strock$bmi)
g_bmi = ggplot(data = strock, aes(strock$bmi, fill = factor(strock$stroke))) + geom_histogram() + theme(axis.text.x = element_text(angle = 45, hjust = 1)) + xlab("Indice de masse corporelle") + ylab("Effectif des patients") + ggtitle("La distribution des patients par indice de masse corporelle") + guides(fill=guide_legend(title = "Présence d'AVC chez le patient"))
ggplotly(g_bmi)
Visualisation des relations entre les variables
Genre des patients et leur age
g_age_genre = ggplot(data = strock, aes(x=strock$gender, y=strock$age, fill=strock$gender)) + geom_boxplot() + theme(axis.text.x = element_text(angle = 45, hjust = 1)) + xlab("Genre des patients") + ylab("L'age des patients") + ggtitle("La distribution de l'age des patients par sexe") + guides(fill=guide_legend(title = "Présence d'AVC chez le patient"))
ggplotly(g_age_genre)
## Warning: Use of `strock$gender` is discouraged.
## ℹ Use `gender` instead.
## Warning: Use of `strock$age` is discouraged.
## ℹ Use `age` instead.
## Warning: Use of `strock$gender` is discouraged.
## ℹ Use `gender` instead.
Les hommes sont moins jeunes que les femmes.
Relation entre le niveau moyen de glucose et l’AVC
g_avgglu_strocke = strock %>% ggplot(aes(y=avg_glucose_level, x=stroke, fill = factor(stroke))) + geom_boxplot() + theme(axis.text.x = element_text(angle = 45, hjust = 1)) + xlab("AVC") + ylab("Niveau moyen de glucose dans le sang du patient") + ggtitle("La distribution ddu ") + guides(fill=guide_legend(title = "Présence d'AVC chez le patient"))
ggplotly(g_avgglu_strocke)
La majorité de ceux qui sont victimes d’AVC ont un niveau moyenne de glucose plus élévé que ceux qui n’ont pas d’AVC, il y a des exeptions.
Relation entre l’indice de masse corporelle et l’AVC
g_bmi_strocke = strock %>% ggplot(aes(y=bmi, x=stroke, fill = factor(strock$stroke))) + geom_boxplot() + theme(axis.text.x = element_text(angle = 45, hjust = 1)) + xlab("AVC") + ylab("BMI du patient") + ggtitle("La distribution du BMI du patient") + guides(fill=guide_legend(title = "Présence d'AVC chez le patient"))
ggplotly(g_bmi_strocke)
## Warning: Use of `strock$stroke` is discouraged.
## ℹ Use `stroke` instead.
## Warning: Removed 201 rows containing non-finite values (`stat_boxplot()`).
Graphique de dispersion entre l’âge et l’indice de masse corporelle (BMI)
ggplot(data = strock, aes(y=age, x=bmi, fill = (strock$stroke))) + geom_point()
## Warning: Use of `strock$stroke` is discouraged.
## ℹ Use `stroke` instead.
## Warning: Removed 201 rows containing missing values (`geom_point()`).
strock %>%
ggplot(aes(x = avg_glucose_level, y = bmi, fill = stroke)) +
geom_point() +
labs(title = "Relation entre le niveau de glucose dans le sang et BMI", x = "Niveau moyen de glucose", y = "Indice de masse corporelle") +
theme_minimal()
## Warning: Removed 201 rows containing missing values (`geom_point()`).
Relation entre Indice de masse corporelle et le type de boulot
g_bmi_job = ggplot(data = strock, aes(x=strock$work_type, y=strock$bmi, fill=factor(strock$work_type))) + geom_violin() + theme(axis.text.x = element_text(angle = 45, hjust = 1)) + xlab("Type de boulot") + ylab("La distribution des patients par indice de masse corporelle") + ggtitle("Distribution de l'indice de masse corporelle par type de boulot ") + guides(fill=guide_legend(title = "Type de boulot"))
ggplotly(g_bmi_job)
## Warning: Use of `strock$work_type` is discouraged.
## ℹ Use `work_type` instead.
## Warning: Use of `strock$bmi` is discouraged.
## ℹ Use `bmi` instead.
## Warning: Use of `strock$work_type` is discouraged.
## ℹ Use `work_type` instead.
## Warning: Removed 201 rows containing non-finite values (`stat_ydensity()`).
Relation entre Indice de masse corporelle et statut de fumeur
g_bmi_job = ggplot(data = strock, aes(x=strock$smoking_status, y=strock$bmi, fill=factor(strock$smoking_status))) + geom_violin() + theme(axis.text.x = element_text(angle = 45, hjust = 1)) + xlab("Statut de fumeur") + ylab("La distribution de l'indice de masse corporelle des patients") + ggtitle("Répartition de l'indice de masse corporelle par type de fumeur ") + guides(fill=guide_legend(title = "Type de fumeur"))
ggplotly(g_bmi_job)
## Warning: Use of `strock$smoking_status` is discouraged.
## ℹ Use `smoking_status` instead.
## Warning: Use of `strock$bmi` is discouraged.
## ℹ Use `bmi` instead.
## Warning: Use of `strock$smoking_status` is discouraged.
## ℹ Use `smoking_status` instead.
## Warning: Removed 201 rows containing non-finite values (`stat_ydensity()`).
Tableau des fréquences
**Le genre
strock %>% count(strock$gender, name = 'nb') %>% mutate(freq=prop.table(table(strock$gender)))
## strock$gender nb freq
## 1 Female 2994 0.5859099804
## 2 Male 2115 0.4138943249
## 3 Other 1 0.0001956947
Hypertension
strock %>% count(strock$hypertension, name = 'nb') %>% mutate(freq=prop.table(prop.table(table(strock$hypertension))))
## strock$hypertension nb freq
## 1 0 4612 0.90254403
## 2 1 498 0.09745597
Maladie de coeur
strock %>% count(strock$heart_disease, name = "nb") %>% mutate(freq=prop.table(table(strock$heart_disease)))
## strock$heart_disease nb freq
## 1 0 4834 0.94598826
## 2 1 276 0.05401174
Marié
strock %>% count(strock$ever_married, name = "nb") %>% mutate(freq=prop.table(prop.table(table(strock$ever_married))))
## strock$ever_married nb freq
## 1 No 1757 0.3438356
## 2 Yes 3353 0.6561644
Type de boulot
(strock %>% count(strock$work_type, name = "nb") %>% mutate(freq= prop.table(table(strock$work_type)))
)
## strock$work_type nb freq
## 1 Govt_job 657 0.134442270
## 2 Never_worked 22 0.128571429
## 3 Private 2925 0.004305284
## 4 Self-employed 819 0.572407045
## 5 children 687 0.160273973
Type de Résidence
strock %>% count(strock$Residence_type, name = "nb") %>% mutate(freq=prop.table(table(strock$Residence_type)))
## strock$Residence_type nb freq
## 1 Rural 2514 0.4919765
## 2 Urban 2596 0.5080235
Statut de fumeur
strock %>% count(strock$smoking_status, name = "nb") %>% mutate(freq= prop.table(table(strock$smoking_status)))
## strock$smoking_status nb freq
## 1 Unknown 1544 0.1731898
## 2 formerly smoked 885 0.3702544
## 3 never smoked 1892 0.1544031
## 4 smokes 789 0.3021526
La variable cyble: stroke ou AVC
strock %>% count(strock$stroke, name = "nb") %>% mutate(freq=prop.table(table(strock$stroke)))
## strock$stroke nb freq
## 1 0 4861 0.95127202
## 2 1 249 0.04872798
On observe que les patients victimes d’AVC ne font que 4% de l’échantillon.
Corrélations entre les Variables Numériques
Matrice de corrélation entre l’âge, la glycémie moyenne et l’indice de masse corporelle (BMI)
num_var <- strock[ ,c("age", "avg_glucose_level", "bmi" )]
bmi_median <-
median(num_var$bmi, na.rm = TRUE)
num_var$bmi[is.na(num_var$bmi)] <- bmi_median
heatmaply_cor(cor(num_var),
xlab="", ylab="",
k_col = 3,
K_row=3)
A trvaers ce graphique on onbserve une corrélation positive faible entre les varibles age, niveau moyen de glucose et le BMI. Il reste à tester si cette corrélation est significative ou pas. Une corrélation parfaite n’implique pas forcement une causalité. Pour déduire une relation de cause à effet entre ces variables, il nous faut une analyse approfondie entre ces variables.
strock_echantillon <- strock
strock_echantillon$bmi[is.na(strock_echantillon$bmi)] <- bmi_median
strock_echantillon$gender[strock_echantillon$gender=="Other"] = "Female"
Transformons les variables gender, maried et résidence type en variable binaire
strock_echantillon$gender_bi <- ifelse(strock_echantillon$gender=="Male", 1, 0)
strock_echantillon$ever_married_bi <- ifelse(strock_echantillon$ever_married=="Yes", 1, 0)
strock_echantillon$Residence_type_bi <- ifelse(strock_echantillon$Residence_type=="Urban", 1, 0)
Transformons les variables work_type et
smoking_status en variable multinomiale
strock_echantillon$work_type_bi = as.numeric(factor(strock_echantillon$work_type, levels = c("children", "Govt_job", "Never_worked", "Private", "Self-employed")))-1
strock_echantillon$smoking_status_bi = as.numeric(factor(strock_echantillon$smoking_status, levels = c("formerly smoked", "never smoked", "smokes", "Unknown")))-1
strock_model <- select(strock_echantillon, -c(id, gender, hypertension, heart_disease, ever_married, work_type, Residence_type, smoking_status))
head(strock_model, 3)
## age avg_glucose_level bmi stroke gender_bi ever_married_bi Residence_type_bi
## 1 67 228.69 36.6 1 1 1 1
## 2 61 202.21 28.1 1 0 1 0
## 3 80 105.92 32.5 1 1 1 0
## work_type_bi smoking_status_bi
## 1 3 0
## 2 4 1
## 3 3 1
Après l’analyse univariée et bivariée on constate qu’il y a des variables qui n’ont aucun impact sur la varible cyble ou qui ne contribuent pas à expliquer la survenance d’AVC.
Les déséquilibre d’effectif qu’on a pour certaines modalités, nous conduit à réechantillonner notre base des données afin de résoudre les problèmes de araeté de certaines variables
Réechantillons les données à cause de la rareté de certaines modalitée des variables expliquatives et de la variable cible
Augmentons la taille de la base des données fois 10
strock_model$stroke[strock_model$stroke==1] = "OUI"
strock_model$stroke[strock_model$stroke==0] = "NON"
head(strock_model)
## age avg_glucose_level bmi stroke gender_bi ever_married_bi Residence_type_bi
## 1 67 228.69 36.6 OUI 1 1 1
## 2 61 202.21 28.1 OUI 0 1 0
## 3 80 105.92 32.5 OUI 1 1 0
## 4 49 171.23 34.4 OUI 0 1 1
## 5 79 174.12 24.0 OUI 0 1 0
## 6 81 186.21 29.0 OUI 1 1 1
## work_type_bi smoking_status_bi
## 1 3 0
## 2 4 1
## 3 3 1
## 4 3 2
## 5 4 1
## 6 3 0
On va sur-échantillonner les données pour équilibrer les données en
utilisant la fonction upsample du package
caret.
strock_model$stroke <- as.factor(strock_model$stroke)
strock_modell <- upSample(strock_model[, -4], y = strock_model$stroke)
strock_modell$Class= as.numeric(strock_modell$Class)-1
table(strock_modell$Class)
##
## 0 1
## 4861 4861
###Choix des variables dépendantes ou des variables qui contribuent à expliquer la survenance d’AVC ches un patnt
Pour cela on va utiliser l’ANOVA : l’analyse des variance qui permet de déterminer l’impact ou l’association entre les variables dépendantes et la variable cyble.
D’arès les resultats, la majorité des variables sont significatives au seuil de 5%. Ce qui veut dire qu’il y a une association entre l’AVC et l’ensemble des variables. Cela n’imlique pas une causalité.
anova = aov(cbind(strock_modell$age, strock_modell$avg_glucose_level, strock_modell$bmi, strock_modell$gender_bi, strock_modell$ever_married_bi,
strock_modell$Residence_type_bi, strock_modell$work_type_bi, strock_modell$smoking_status_bi) ~ strock_modell$Class, data=strock_modell)
summary.aov(anova)
## Response 1 :
## Df Sum Sq Mean Sq F value Pr(>F)
## strock_modell$Class 1 1618759 1618759 4943.2 < 2.2e-16 ***
## Residuals 9720 3183056 327
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Response 2 :
## Df Sum Sq Mean Sq F value Pr(>F)
## strock_modell$Class 1 1723144 1723144 601.72 < 2.2e-16 ***
## Residuals 9720 27834979 2864
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Response 3 :
## Df Sum Sq Mean Sq F value Pr(>F)
## strock_modell$Class 1 4074 4073.7 85.802 < 2.2e-16 ***
## Residuals 9720 461481 47.5
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Response 4 :
## Df Sum Sq Mean Sq F value Pr(>F)
## strock_modell$Class 1 0.3 0.29994 1.2325 0.267
## Residuals 9720 2365.5 0.24337
##
## Response 5 :
## Df Sum Sq Mean Sq F value Pr(>F)
## strock_modell$Class 1 134.38 134.381 802.16 < 2.2e-16 ***
## Residuals 9720 1628.32 0.168
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Response 6 :
## Df Sum Sq Mean Sq F value Pr(>F)
## strock_modell$Class 1 2.83 2.83440 11.373 0.000748 ***
## Residuals 9720 2422.37 0.24921
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Response 7 :
## Df Sum Sq Mean Sq F value Pr(>F)
## strock_modell$Class 1 608.4 608.38 479.51 < 2.2e-16 ***
## Residuals 9720 12332.2 1.27
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Response 8 :
## Df Sum Sq Mean Sq F value Pr(>F)
## strock_modell$Class 1 246.8 246.801 211.1 < 2.2e-16 ***
## Residuals 9720 11363.7 1.169
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
La normalisation échelonne les valeurs d’une variable dans une plage spécifique, généralement entre 0 et 1. C’est utile lorsqu’on veut ramener toutes les valeurs à une plage commune.
Standardisation (Z-score Scaling) : la standardisation met à l’échelle les valeurs de manière à ce qu’elles aient une moyenne nulle et un écart-type unitaire. Cela est souvent utilisé dans des algorithmes qui supposent une distribution normale des données.
Standardisons
standar_data = preProcess(strock_modell[, -9], method = c("center", "scale"))
strock_standardizer = predict(standar_data, newdata=strock_modell)
print(head(strock_standardizer))
## age avg_glucose_level bmi gender_bi ever_married_bi
## 1 -2.3340636 -0.4169001 -1.6540173 1.1788678 -1.789656
## 2 0.1405960 -0.5467465 1.4093976 1.1788678 0.558709
## 3 -2.1090945 -0.1309115 -1.7118176 -0.8481843 -1.789656
## 4 0.6805217 -0.8898603 0.9325453 -0.8481843 0.558709
## 5 -1.8391317 0.7829101 -1.4950665 1.1788678 -1.789656
## 6 -0.3543359 1.6836745 2.9844553 -0.8481843 0.558709
## Residence_type_bi work_type_bi smoking_status_bi Class
## 1 -1.0477875 -2.3585604 1.4250479 0
## 2 0.9542939 0.2415973 -0.4049918 0
## 3 0.9542939 0.2415973 1.4250479 0
## 4 -1.0477875 0.2415973 -1.3200116 0
## 5 -1.0477875 -0.6251219 1.4250479 0
## 6 0.9542939 0.2415973 1.4250479 0
# pour la construction des modèles
install.packages("kernlab")
library(kernlab)
install.packages("randomForest")
library(randomForest)
library(caret)
library(e1071)
library(glmnet)
library(rpart)
library(ISLR)
library(skimr)
on va construire les modèles suivants:
Regression logistique (RL)
Analyse discriminante linéaire (LDA)
Arbres de classification et de régression (CART)
k-Voisins les plus proches (kNN)
Machines à vecteurs de support (SVM) avec un noyau linéaire
Forêt aléatoire (RF)
Nous effectuerons une validation croisée 10 fois pour estimer la précision. Cela divisera notre ensemble de données en 10 parties, entraînera en 9 et testera sur 1 et publiera pour toutes les combinaisons de répartitions train-test. Nous répéterons également le processus 3 fois pour chaque algorithme avec différentes répartitions des données en 10 groupes, dans le but d’obtenir une estimation plus précise.
Avec la fonction createDataPartition de
caret
set.seed(123)
indice = createDataPartition(strock_standardizer$Class, p=0.7, list = FALSE)
data_train = strock_standardizer[indice, ]
data_test = strock_standardizer[-indice, ]
# type des données
data_train$Class <- as.factor(data_train$Class)
class(data_train$Class)
## [1] "factor"
Définir le contrôle pour train :
Utilison la fonction trainControl pour définir les
paramètres de contrôle pour l’entraînement des modèles. Nous pouvons
spécifier la méthode de validation croisée et les métriques de
performance.
# Définition du contrôle
control <- trainControl(method = "cv", number = 5)
metric <- c("Accuracy")
Modèle de régression logistique
# Modèle de régression logistique
set.seed(7)
fit.lm <- train(Class ~ ., data = data_train, method = "glm",
metric = "Accuracy",
trControl = control)
Linear algorithms
set.seed(7)
fit.lda <- train(Class ~ ., data = data_train, method = "lda", metric = metric, trControl = control)
print(fit.lda)
## Linear Discriminant Analysis
##
## 6806 samples
## 8 predictor
## 2 classes: '0', '1'
##
## No pre-processing
## Resampling: Cross-Validated (5 fold)
## Summary of sample sizes: 5445, 5445, 5444, 5445, 5445
## Resampling results:
##
## Accuracy Kappa
## 0.7650594 0.5301199
Nonlinear algorithms
# CART
set.seed(7)
fit.cart <- train(Class ~ ., data = data_train, method = "rpart", metric = metric, trControl = control)
print(fit.cart)
## CART
##
## 6806 samples
## 8 predictor
## 2 classes: '0', '1'
##
## No pre-processing
## Resampling: Cross-Validated (5 fold)
## Summary of sample sizes: 5445, 5445, 5444, 5445, 5445
## Resampling results across tuning parameters:
##
## cp Accuracy Kappa
## 0.01513371 0.7643223 0.5286924
## 0.01616221 0.7605040 0.5210381
## 0.50661181 0.5984570 0.1972667
##
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was cp = 0.01513371.
KNN modèle
# kNN
set.seed(7)
fit.knn <- train(Class ~ ., data = data_train, method = "knn", metric = metric, trControl = control)
print(fit.knn)
## k-Nearest Neighbors
##
## 6806 samples
## 8 predictor
## 2 classes: '0', '1'
##
## No pre-processing
## Resampling: Cross-Validated (5 fold)
## Summary of sample sizes: 5445, 5445, 5444, 5445, 5445
## Resampling results across tuning parameters:
##
## k Accuracy Kappa
## 5 0.8657075 0.7314188
## 7 0.8379370 0.6758818
## 9 0.8173659 0.6347392
##
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was k = 5.
ASVM algorithms
# SVM
set.seed(7)
fit.svm <- train(Class ~ ., data = data_train, method = "svmRadial", metric = metric, trControl = control)
print(fit.svm)
## Support Vector Machines with Radial Basis Function Kernel
##
## 6806 samples
## 8 predictor
## 2 classes: '0', '1'
##
## No pre-processing
## Resampling: Cross-Validated (5 fold)
## Summary of sample sizes: 5445, 5445, 5444, 5445, 5445
## Resampling results across tuning parameters:
##
## C Accuracy Kappa
## 0.25 0.7853358 0.5706730
## 0.50 0.7891557 0.5783124
## 1.00 0.7960622 0.5921245
##
## Tuning parameter 'sigma' was held constant at a value of 0.1003374
## Accuracy was used to select the optimal model using the largest value.
## The final values used for the model were sigma = 0.1003374 and C = 1.
Random Forest
**{r} set.seed(7) fit.rf <- train(Class ~ ., data = data_train, method = "rf", metric = "Accuracy", trControl = control) print(fit.rf)
Nous disposons désormais de 6 modèles et d’estimations de précision pour chacun. Nous devons comparer les modèles entre eux et sélectionner le plus précis.
Nous pouvons rendre compte de l’exactitude de chaque modèle en créant d’abord une liste des modèles créés et en utilisant la fonction de résumé.
Nous disposons désormais de 6 modèles et d’estimations de précision pour chacun. Nous devons comparer les modèles entre eux et sélectionner le plus précis.
Nous pouvons rendre compte de l’exactitude de chaque modèle en créant d’abord une liste des modèles créés et en utilisant la fonction de résumé.
Nous pouvons voir la précision de chaque classificateur ainsi que d’autres métriques comme Kappa :
# summarize accuracy of models
results <- resamples(list(lda=fit.lda, cart=fit.cart, knn=fit.knn, svm=fit.svm))
summary(results)
##
## Call:
## summary.resamples(object = results)
##
## Models: lda, cart, knn, svm
## Number of resamples: 5
##
## Accuracy
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## lda 0.7545922 0.7641440 0.7656135 0.7650594 0.7700220 0.7709251 0
## cart 0.7457752 0.7472447 0.7648788 0.7643223 0.7766348 0.7870778 0
## knn 0.8545187 0.8604993 0.8655400 0.8657075 0.8692138 0.8787656 0
## svm 0.7883909 0.7927994 0.7966226 0.7960622 0.7994122 0.8030860 0
##
## Kappa
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## lda 0.5092318 0.5282455 0.5311908 0.5301199 0.5400811 0.5418502 0
## cart 0.4916939 0.4946397 0.5297227 0.5286924 0.5532501 0.5741557 0
## knn 0.7090995 0.7209985 0.7310303 0.7314188 0.7384777 0.7574879 0
## svm 0.5767331 0.5856496 0.5932452 0.5921245 0.5987735 0.6062210 0
Nous pouvons également créer un graphique des résultats de l’évaluation du modèle et comparer la propagation et la précision moyenne de chaque modèle. Il existe une population de mesures de précision pour chaque algorithme car chaque algorithme a été évalué 5 fois (validation croisée 5 fois).
dotplot(results)
Nous pouvons constater que le meilleur modèle est: Random Forest. Nous pouvons résumer le résultat du modèle RF à :
**{r} print(fit.rf)
ous observons que pour différentes valeurs de l’hyperparamètre mtry (le nombre de variables aléatoires considérées à chaque division de l’arbre) lors de la validation croisée, le modèle Random Forest présente des performances variées. Les résultats montrent que la précision, mesurée par la proportion d’observations correctement classées, ainsi que le coefficient kappa, indiquant l’accord entre les prédictions du modèle et les classes réelles, varient selon la valeur de mtry. Nous constatons que la meilleure précision et le plus haut coefficient kappa sont obtenus lorsque mtry est égal à 5, avec des valeurs respectives de 97.99% et 95.97%.
Il est important de noter que l’évaluation du modèle a été réalisée sur différents plis de la validation croisée, fournissant des performances spécifiques pour chaque pli. Nous avons choisi le modèle optimal en se basant sur la précision, et la meilleure valeur de mtry sélectionnée était 5, comme indiqué par la ligne “Accuracy was used to select the optimal model using the largest value.”
En conclusion, nous retenons le modèle Random Forest avec mtry = 5 comme le meilleur modèle, présentant une précision de 97.99% et un coefficient kappa de 95.97% lors de la validation croisée.
Faisons la prédiction
Le RF était le modèle le plus précis. Nous voulons maintenant avoir une idée de la précision du modèle sur notre ensemble de test.
Cela nous donnera une vérification finale indépendante de l’exactitude du meilleur modèle. Il est utile de conserver un ensemble de validation au cas où vous feriez une erreur lors d’un surajustement de l’ensemble d’entraînement ou d’une fuite de données. Les deux aboutiront à un résultat trop optimiste.
Nous pouvons exécuter le modèle RF directement sur l’ensemble de validation et résumer les résultats dans une matrice de confusion.
**{r} prediction = predict(fit.rf, data_test) data_test$Class <- as.factor(data_test$Class) conf_matrix <- confusionMatrix(prediction, data_test$Class) print(conf_matrix)
Au vu des resultats de la matrice de confusion on peut dire que :
La matrice de confusion nous fournit un aperçu détaillé des performances de notre modèle Random Forest sur les données de test. Voici notre analyse des différents aspects de la matrice de confusion et des statistiques associées :
Matrice de Confusion :
Vrais Positifs (0 prédit comme 0) : 1417 Faux Positifs (1 prédit comme 0) : 0 Faux Négatifs (0 prédit comme 1) : 41 Vrais Négatifs (1 prédit comme 1) : 1458
Statistiques de Performance :
Précision (Accuracy) : 98.59% Intervalle de Confiance (95%) : Entre 98.1% et 98.99% Taux d’Erreur (No Information Rate) : 50% (comparaison avec un modèle ignorant) Valeur p (P-Value [Acc > NIR]) : Inférieure à 2.2e-16 (indique une significative amélioration par rapport au taux d’erreur) Kappa : 97.19% (mesure de l’accord entre nos prédictions et les classes réelles) Autres Statistiques :
Test de Mcnemar : la valeur p est significative (4.185e-10), suggérant une différence significative entre les erreurs de type I et de type II.
Sensibilité (Sensitivity) : 97.19% (capacité à détecter les vrais positifs)
Spécificité (Specificity) : 100% (capacité à détecter les vrais négatifs)
Valeur Prédictive Positive (Pos Pred Value) : 100% (probabilité que notre prédiction positive soit correcte)
Valeur Prédictive Négative (Neg Pred Value) : 97.26% (probabilité que notre prédiction négative soit correcte)
Prévalence : 50% (proportion d’événements positifs dans la population)
Taux de Détection (Detection Rate) : 48.59% (proportion d’événements positifs détectés par notre modèle)
Prévalence de Détection (Detection Prevalence) : 48.59% (proportion d’événements positifs détectés par notre modèle parmi tous les cas positifs réels)
Précision Équilibrée (Balanced Accuracy) : 98.59% (moyenne de la sensibilité et de la spécificité)
En résumé, notre modèle Random Forest présente d’excellentes performances sur les données de test, démontrant ainsi notre capacité à bien généraliser sur de nouvelles données.
**{r} datatable(conf_matrix$table)
Cela fera l’objet d’autres projets.
Après avoir analysé les données relatives à la prédiction de la survenance d’AVC chez les patients de l’hôpital, voici un exemple de recommandation :
À la lumière de notre analyse approfondie, plusieurs insights clés ont émergé, mettant en évidence des tendances et des facteurs significatifs liés à la survenance d’AVC chez les patients. Ces résultats peuvent orienter les interventions et les stratégies de prévention. Voici quelques recommandations :
Identification des Groupes à Risque :
Les patients présentant des antécédents d’hypertension et de maladies cardiaques sont particulièrement à risque. Les programmes de surveillance et d’éducation devraient cibler ces groupes spécifiques. Promotion du Mode de Vie Sain :
Encourager les patients à adopter un mode de vie sain, y compris une alimentation équilibrée, une activité physique régulière et l’arrêt du tabac, peut réduire significativement le risque d’AVC. Gestion de l’Hypertension et de la Glycémie :
Mettre en place des protocoles de suivi étroit pour les patients hypertendus et ceux présentant des niveaux de glucose élevés, en s’assurant qu’ils reçoivent les soins appropriés pour maintenir ces paramètres dans des plages sûres. Éducation sur les Facteurs de Risque Modifiables :
Sensibiliser les patients à l’importance de la gestion du poids, en particulier du BMI, peut avoir un impact positif sur la prévention des AVC. Surveillance Continue :
Établir un suivi régulier des patients à risque élevé, même s’ils n’ont pas encore présenté d’AVC. La détection précoce de signes précurseurs peut permettre une intervention rapide. Campagnes de Sensibilisation :
Organiser des campagnes de sensibilisation communautaire sur les facteurs de risque, les symptômes précoces et l’importance de la consultation médicale en cas de doute peut contribuer à une réduction globale des cas d’AVC. Ces recommandations, basées sur une analyse approfondie des données, visent à fournir des orientations stratégiques pour améliorer la prévention des AVC au sein de la population étudiée. Il est crucial de mettre en œuvre ces mesures de manière coordonnée avec les professionnels de la santé et les parties prenantes impliquées dans la gestion des soins de santé.
**Thank you**