1 . Introduction

Dans ce rapport, nous allons analyser les données du célèbre dataset Titanic disponible sur Kaggle. L’objectif est de prédire si un passager a survécu ou non en utilisant des méthodes de machine learning, en réalisant une exploration approfondie des données et en effectuant des visualisations interactives.

Le machine learning est une méthode d’analyse où nous entraînons des modèles sur un sous-ensemble de données (ensemble d’entraînement) et testons leurs performances sur un autre sous-ensemble (ensemble de test). Ce processus permet de faire des prédictions sur de nouvelles données, ici pour prédire la survie des passagers du Titanic.

Vous trouverez la base de données Kaggle via le lien suivant : https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv

2 . Importation et exploration initiale

Nous importons d’abord les bibliothèques nécessaires à notre analyse :

Nous commençons par importer les données et examiner un aperçu du jeu de données.

# Importation des données depuis un fichier CSV sur GitHub
titanic_raw <- read_csv("https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv")

# Affichage d'un aperçu des données pour mieux comprendre leur structure
glimpse(titanic_raw)
## Rows: 891
## Columns: 12
## $ PassengerId <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,…
## $ Survived    <dbl> 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1…
## $ Pclass      <dbl> 3, 1, 3, 1, 3, 3, 1, 3, 3, 2, 3, 1, 3, 3, 3, 2, 3, 2, 3, 3…
## $ Name        <chr> "Braund, Mr. Owen Harris", "Cumings, Mrs. John Bradley (Fl…
## $ Sex         <chr> "male", "female", "female", "female", "male", "male", "mal…
## $ Age         <dbl> 22, 38, 26, 35, 35, NA, 54, 2, 27, 14, 4, 58, 20, 39, 14, …
## $ SibSp       <dbl> 1, 1, 0, 1, 0, 0, 0, 3, 0, 1, 1, 0, 0, 1, 0, 0, 4, 0, 1, 0…
## $ Parch       <dbl> 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 0, 0, 5, 0, 0, 1, 0, 0, 0…
## $ Ticket      <chr> "A/5 21171", "PC 17599", "STON/O2. 3101282", "113803", "37…
## $ Fare        <dbl> 7.2500, 71.2833, 7.9250, 53.1000, 8.0500, 8.4583, 51.8625,…
## $ Cabin       <chr> NA, "C85", NA, "C123", NA, NA, "E46", NA, NA, NA, "G6", "C…
## $ Embarked    <chr> "S", "C", "S", "S", "S", "Q", "S", "S", "S", "C", "S", "S"…

Nous affichons un tableau interactif des premières lignes du jeu de données

DT::datatable(head(titanic_raw), options = list(pageLength = 5))
cat("Le jeu de données Titanic contient", nrow(titanic_raw), "lignes\n")
## Le jeu de données Titanic contient 891 lignes
cat("Le jeu de données Titanic contient", ncol(titanic_raw), "colonnes\n")
## Le jeu de données Titanic contient 12 colonnes

Notre jeu de données comporte ainsi 891 colonnes et 12 colonnes. Il contient des variables qualitatives et quantitatives.

3 . Prétraitement et nettoyage des données

Les données brutes contiennent des informations inutiles pour notre analyse. Nous allons donc nettoyer les données en supprimant des colonnes non pertinentes, en gérant les valeurs manquantes et en convertissant certaines colonnes en types appropriés.

# Sélection des variables pertinentes et transformation des variables catégorielles
titanic <- titanic_raw %>%
  select(-PassengerId, -Name, -Ticket, -Cabin) %>%  # Suppression des colonnes inutiles
  mutate(
    Survived = factor(Survived, labels = c("Non", "Oui")),  # Conversion de la variable Survived en facteur
    Pclass = factor(Pclass),  # Conversion de la classe du passager en facteur
    Sex = factor(Sex),  # Conversion du sexe en facteur
    Embarked = factor(Embarked)  # Conversion du port d'embarquement en facteur
  ) %>%
  drop_na()  # Suppression des lignes avec des valeurs manquantes

# Affichage du résumé des données nettoyées
summary(titanic)
##  Survived  Pclass      Sex           Age            SibSp      
##  Non:424   1:184   female:259   Min.   : 0.42   Min.   :0.000  
##  Oui:288   2:173   male  :453   1st Qu.:20.00   1st Qu.:0.000  
##            3:355                Median :28.00   Median :0.000  
##                                 Mean   :29.64   Mean   :0.514  
##                                 3rd Qu.:38.00   3rd Qu.:1.000  
##                                 Max.   :80.00   Max.   :5.000  
##      Parch             Fare        Embarked
##  Min.   :0.0000   Min.   :  0.00   C:130   
##  1st Qu.:0.0000   1st Qu.:  8.05   Q: 28   
##  Median :0.0000   Median : 15.65   S:554   
##  Mean   :0.4326   Mean   : 34.57           
##  3rd Qu.:1.0000   3rd Qu.: 33.00           
##  Max.   :6.0000   Max.   :512.33

4 . Analyse exploratoire

Dans cette section, nous allons explorer les données pour comprendre les distributions des variables et les relations entre elles.

4.1 . Distribution des variables

Dans un premier temps, examinons la distribution de l’âge des passagers.

library(ggplot2)

ggplot(titanic, aes(x = Age)) +
  geom_histogram(binwidth = 5, fill = "#0073C2FF", color = "white", alpha = 0.8) +
  theme_minimal() +
  labs(title = "Distribution des âges des passagers",
       x = "Âge",
       y = "Nombre de passagers")

La majorité des passagers ont entre 20 et 30 ans. C’est dans cette tranche que l’on trouve le plus grand nombre de passagers. La distribution est asymétrique, avec une queue vers la droite (donc un nombre décroissant de passagers à mesure que l’âge augmente). Il y a très peu de passagers âgés de plus de 60 ans, et encore moins au-delà de 70 ans. On remarque aussi une présence notable de passagers jeunes, y compris des enfants (moins de 10 ans).

Ensuite, nous examinons la distribution de l’âge des passagers en fonction de leur survie.

library(plotly)

# Histogramme interactif de l'âge selon la survie, avec couleurs vives
plot_ly(data = titanic, x = ~Age, color = ~Survived, type = "histogram",
        colors = c("Oui" = "#00C49A", "Non" = "#FF5733"),
        opacity = 0.75) %>%
  layout(
    title = "Distribution de l'âge selon la survie",
    xaxis = list(title = "Âge"),
    yaxis = list(title = "Nombre de passagers"),
    barmode = "overlay"
  )

La majorité des passagers avaient entre 20 et 40 ans, avec peu de très jeunes ou très âgés. Les jeunes adultes (15-30 ans) montrent une proportion plus élevée de non-survivants, tandis que les enfants ont des taux de survie et de non-survie plus similaires, bien qu’en faible nombre. En général, l’âge semble être un facteur corrélé à la survie, mais une analyse plus approfondie avec d’autres variables serait nécessaire pour établir des conclusions plus définitives.

ggplot(titanic, aes(x = Pclass, y = Fare, fill = Pclass)) +
  geom_boxplot() +
  theme_minimal() +
  labs(title = "Distribution des tarifs par classe",
       x = "Classe",
       y = "Tarif (Fare)")

Le graphique montre que les tarifs sont très différents selon la classe. Les passagers de la première classe ont payé en général des tarifs beaucoup plus élevés, la plupart se situant autour de 100, et certains payant même plus de 500. Les tarifs des deuxième et troisième classes sont nettement plus bas, avec la majorité des tarifs en dessous de 25. Voyager en première classe coûte significativement plus cher que dans les autres classes.

Nous avons visualisé la distribution de plusieurs variables fondamentales à notre dataset.

Nous allons désormais analyser le taux de survie des passagers selon différentes catégories.

4.2 . Taux de survie par catégorie

Dans cette section, nous allons explorer les données pour comprendre les taux de survie et les relations entre eux.

titanic %>%
  group_by(Sex, Survived) %>%
  summarise(Count = n()) %>%
  ggplot(aes(x = Sex, y = Count, fill = Survived)) +
  geom_col(position = "dodge") +
  labs(title = "Taux de survie par sexe")

Parmi les individus, il y a beaucoup plus de femmes qui ont survécu que de femmes qui n’ont pas survécu. En revanche, il y a beaucoup plus d’hommes qui n’ont pas survécu que d’hommes qui ont survécu. En résumé, les femmes ont un taux de survie plus élevé que les hommes.

# Création d'une variable "Famille"
titanic <- titanic %>% mutate(Famille = SibSp + Parch)

ggplot(titanic, aes(x = Famille, fill = Survived)) +
  geom_bar(position = "fill") +
  scale_y_continuous(labels = scales::percent) +
  labs(title = "Survie en fonction du nombre de proches à bord",
       x = "Nombre de membres de la famille (SibSp + Parch)",
       y = "Proportion de survie") +
  theme_minimal()

Les personnes voyageant seules (0 proches) ont un faible taux de survie. La survie augmente pour ceux avec 1 à 3 proches, atteint un pic avec 3 proches, puis diminue fortement avec plus de 3 proches. En résumé, avoir un petit nombre de proches semble augmenter les chances de survie, mais avoir une grande famille les diminue.

titanic %>%
  group_by(Pclass, Survived) %>%
  summarise(Nombre = n()) %>%
  ggplot(aes(x = Pclass, y = Nombre, fill = Survived)) +
  geom_col(position = "fill") +
  scale_y_continuous(labels = scales::percent) +
  labs(title = "Taux de survie par classe",
       x = "Classe",
       y = "Proportion",
       fill = "Survécu") +
  theme_minimal()

La survie était la plus élevée en première classe, diminuant progressivement en deuxième puis en troisième classe. La classe sociale a clairement influencé les chances de survie.

4.3 . Visualisations interactives supplémentaires

Nous allons visualiser la répartition des hommes et des femmes.

library(highcharter)
library(dplyr)

# Préparation des données pour le treemap
titanic_sex_count <- titanic %>%
  count(Sex) %>%
  rename(name = Sex, value = n)

# Visualisation treemap
highchart() %>%
  hc_chart(type = "treemap") %>%
  hc_title(text = "Répartition des passagers par sexe") %>%
  hc_add_series(
    data = list_parse(titanic_sex_count),
    layoutAlgorithm = "squarified",
    allowDrillToNode = TRUE,
    dataLabels = list(enabled = TRUE)
  )

Nous recensons ainsi 259 femmes et 453 hommes dans notre dataset.

Pour mieux comprendre la relation entre l’âge, le tarif du billet et la survie, nous allons créer un graphique interactif.

# Graphique interactif montrant la relation entre âge, tarif et survie
highchart() %>%
  hc_add_series(data = titanic, type = "scatter", hcaes(x = Age, y = Fare, group = Survived)) %>%
  hc_title(text = "Relation entre âge, tarif et survie")

La plupart des passagers, survivants ou non, avaient moins de 50 ans et payé des tarifs bas (moins de 100). Quelques passagers ont payé des tarifs élevés (plus de 200), et un très petit nombre a payé des tarifs extrêmement élevés (autour de 500).

5 . Régression logistique : prédire la survie

5.1 . Pourquoi la régression logistique ?

La régression logistique est utilisée quand on cherche à prédire une variable binaire (ici : survie oui/non). Elle modélise la probabilité qu’un événement se produise (survivre), en fonction de variables explicatives (âge, sexe, etc.).

5.2 . Création des jeux d’entraînement et de test

set.seed(42)
split <- createDataPartition(titanic$Survived, p = 0.8, list = FALSE)
train <- titanic[split, ]
test <- titanic[-split, ]

5.3 . Construction du modèle

On va utiliser les variables Sex, Age et Pclass pour prédire la survie.

# Création du modèle
model_logit <- glm(Survived ~ Sex + Age + Pclass, data = train, family = "binomial")

# Résumé du modèle (coefficients, p-valeurs)
summary(model_logit)
## 
## Call:
## glm(formula = Survived ~ Sex + Age + Pclass, family = "binomial", 
##     data = train)
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  3.40440    0.43165   7.887 3.10e-15 ***
## Sexmale     -2.43044    0.22617 -10.746  < 2e-16 ***
## Age         -0.03200    0.00846  -3.782 0.000155 ***
## Pclass2     -1.12154    0.30231  -3.710 0.000207 ***
## Pclass3     -2.32435    0.30318  -7.667 1.77e-14 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 770.64  on 570  degrees of freedom
## Residual deviance: 536.37  on 566  degrees of freedom
## AIC: 546.37
## 
## Number of Fisher Scoring iterations: 4

5.4 . Interprétation des coefficients

# On convertit les coefficients en odds ratios pour une meilleure interprétation
library(broom)
tidy(model_logit, exponentiate = TRUE, conf.int = TRUE) %>%
  kable(digits = 3) %>%
  kable_styling(full_width = F)
term estimate std.error statistic p.value conf.low conf.high
(Intercept) 30.096 0.432 7.887 0 13.246 72.130
Sexmale 0.088 0.226 -10.746 0 0.056 0.136
Age 0.969 0.008 -3.782 0 0.952 0.984
Pclass2 0.326 0.302 -3.710 0 0.179 0.585
Pclass3 0.098 0.303 -7.667 0 0.053 0.175

Remarque : un odds ratio > 1 signifie un effet positif sur la probabilité de survie, < 1 un effet négatif.

Les résultats montrent que les hommes ont beaucoup moins de chances de survivre que les femmes (odds ratio de 0.088). L’âge diminue également légèrement les chances de survie, chaque année de plus réduisant la probabilité de survie (odds ratio de 0.969). Les passagers de la classe 2 et 3 ont des chances de survie inférieures à ceux de la classe 1, avec des odds ratios de 0.326 et 0.098, respectivement. Tous ces effets sont statistiquement significatifs, ce qui suggère que le sexe, l’âge et la classe sociale sont des facteurs clés dans la survie des passagers.

6 . Prédictions et évaluation

6.1 . Prédiction sur l’ensemble de test

# Prédiction de la probabilité de survie
test$prob <- predict(model_logit, newdata = test, type = "response")
# Conversion en Oui/Non selon un seuil de 0.5
test$pred <- ifelse(test$prob > 0.5, "Oui", "Non") %>% factor(levels = c("Non", "Oui"))

6.2 . Matrice de confusion

confusionMatrix(test$pred, test$Survived)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction Non Oui
##        Non  75  15
##        Oui   9  42
##                                           
##                Accuracy : 0.8298          
##                  95% CI : (0.7574, 0.8878)
##     No Information Rate : 0.5957          
##     P-Value [Acc > NIR] : 1.904e-09       
##                                           
##                   Kappa : 0.6405          
##                                           
##  Mcnemar's Test P-Value : 0.3074          
##                                           
##             Sensitivity : 0.8929          
##             Specificity : 0.7368          
##          Pos Pred Value : 0.8333          
##          Neg Pred Value : 0.8235          
##              Prevalence : 0.5957          
##          Detection Rate : 0.5319          
##    Detection Prevalence : 0.6383          
##       Balanced Accuracy : 0.8148          
##                                           
##        'Positive' Class : Non             
## 

Cela nous permet de voir le nombre de bonnes et mauvaises prédictions (vrais positifs/négatifs, faux positifs/négatifs). Le modèle de régression logistique a une précision de 82,98%, avec une bonne capacité à détecter les survivants (sensibilité de 89,29%) et les non-survivants (spécificité de 73,68%). La valeur de Kappa (0,6405) montre une bonne concordance entre les prédictions et les résultats réels.

7 . Visualisation des courbes logistiques

7.1 . Régression logistique pour l’âge

Nous allons tracer un nuage de points avec une courbe logistique pour visualiser l’effet de l’âge sur la probabilité de survie (en filtrant par sexe par exemple).

ggplot(train, aes(x = Age, y = as.numeric(Survived == "Oui"))) +
  geom_point(alpha = 0.3, color = "steelblue") +
  geom_smooth(method = "glm", method.args = list(family = "binomial"), se = TRUE, color = "darkred") +
  labs(title = "Probabilité de survie selon l'âge (régression logistique)",
       x = "Âge", y = "Probabilité de survie")

Notre modèle suggère donc que la probabilité de survie diminue lorsque l’âge augmente.

7.2 . Régression logistique pour le tarif

Essayons une régression uniquement sur la variable Fare (tarif du billet) pour voir son impact.

model_fare <- glm(Survived ~ Fare, data = train, family = "binomial")
summary(model_fare)
## 
## Call:
## glm(formula = Survived ~ Fare, family = "binomial", data = train)
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept) -0.82888    0.11700  -7.084 1.40e-12 ***
## Fare         0.01373    0.00259   5.302 1.14e-07 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 770.64  on 570  degrees of freedom
## Residual deviance: 731.75  on 569  degrees of freedom
## AIC: 735.75
## 
## Number of Fisher Scoring iterations: 4
ggplot(train, aes(x = Fare, y = as.numeric(Survived == "Oui"))) +
  geom_point(alpha = 0.3) +
  geom_smooth(method = "glm", method.args = list(family = "binomial"), se = TRUE, color = "darkgreen") +
  labs(title = "Probabilité de survie en fonction du tarif payé", x = "Tarif (Fare)", y = "Probabilité")

Notre modèle suggère que la probabilité de survie augmente lorsque le tarif augmente.

7.3 . Troisième régression logistique

Nous allons cette fois-ci prédire la survie des passagers selon le tarif et la classe

# Régression logistique : prédire la survie selon le tarif et la classe
model_fare_class <- glm(Survived ~ Fare + Pclass, data = train, family = binomial)

# Résumé du modèle
summary(model_fare_class)
## 
## Call:
## glm(formula = Survived ~ Fare + Pclass, family = binomial, data = train)
## 
## Coefficients:
##              Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  0.083083   0.276731   0.300   0.7640    
## Fare         0.005504   0.002912   1.890   0.0587 .  
## Pclass2     -0.244169   0.286626  -0.852   0.3943    
## Pclass3     -1.280713   0.284279  -4.505 6.63e-06 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 770.64  on 570  degrees of freedom
## Residual deviance: 700.50  on 567  degrees of freedom
## AIC: 708.5
## 
## Number of Fisher Scoring iterations: 4
# Matrice de confusion
pred_fare_class <- predict(model_fare_class, test, type = "response")
pred_class <- ifelse(pred_fare_class > 0.5, "Oui", "Non")
confusionMatrix(factor(pred_class, levels = c("Non", "Oui")), test$Survived)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction Non Oui
##        Non  73  30
##        Oui  11  27
##                                           
##                Accuracy : 0.7092          
##                  95% CI : (0.6268, 0.7826)
##     No Information Rate : 0.5957          
##     P-Value [Acc > NIR] : 0.003412        
##                                           
##                   Kappa : 0.3621          
##                                           
##  Mcnemar's Test P-Value : 0.004937        
##                                           
##             Sensitivity : 0.8690          
##             Specificity : 0.4737          
##          Pos Pred Value : 0.7087          
##          Neg Pred Value : 0.7105          
##              Prevalence : 0.5957          
##          Detection Rate : 0.5177          
##    Detection Prevalence : 0.7305          
##       Balanced Accuracy : 0.6714          
##                                           
##        'Positive' Class : Non             
## 
# Visualisation du lien entre le tarif payé et la probabilité de survie selon la classe
ggplot(train, aes(x = Fare, y = as.numeric(Survived == "Oui"), color = Pclass)) +
  geom_point(alpha = 0.5) +
  geom_smooth(method = "glm", method.args = list(family = "binomial"), se = TRUE) +
  labs(title = "Lien entre le tarif payé et la survie selon la classe",
       x = "Tarif (Fare)", y = "Probabilité de survie")

8 . Conclusion

En conclusion, nous avons utilisé une approche de régression logistique pour prédire la survie des passagers du Titanic à partir de plusieurs variables explicatives. Ce modèle est particulièrement adapté aux problèmes de classification binaire, et dans ce cas précis, il a permis de déterminer les facteurs clés influençant la survie.

L’analyse a révélé plusieurs points intéressants :

  • L’âge joue un rôle important : les jeunes passagers avaient un taux de survie plus élevé.

  • Le tarif du billet était également un indicateur important : plus le tarif était élevé, plus la probabilité de survie augmentait, ce qui pourrait être lié à la classe sociale.

Nous avons exploré différentes régressions logistiques : une première simple, une deuxième basée sur une seule variable, et une troisième basée sur le tarif payé et la classe.. Cela montre que l’ajout d’interactions entre les variables peut améliorer la performance du modèle et nous fournir des insights plus précis.

La régression logistique reste un modèle puissant et relativement facile à interpréter, particulièrement adapté aux premières étapes de modélisation prédictive en machine learning. Pour aller plus loin, on pourrait tester d’autres modèles, mais cette analyse offre déjà une bonne base pour prédire la survie à partir de données simples et compréhensibles.