Prédire les chances de survie au naufrage du Titanic

Cette compétition proposée sur kaggle, consiste à construire un modèle permettant de prédire les chances de survie lors du naufrage du Titanic.

C’est un excellent premier exercice pour mettre en oeuvre ses talents de data scientist.

Nous disposons d’une partie de la liste des passagers  et de certaines informations : a-t-il péri dans le naufrage, nom, age, sexe, classe, prix du billet, etc.

Le but est de construire un modèle à partir de ces données et de l’appliquer sur la partie de la liste des passagers pour lesquels nous n’avons pas l’information de survie.

1 : Première observation des données à disposition

Regardons tout d’abord la structure des données :

#%% IMPORTATIONS
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
from pylab import pcolor, show, colorbar, xticks, yticks
import seaborn as sns
import sklearn
from sklearn.model_selection import train_test_split, cross_val_score, GridSearchCV
from sklearn import preprocessing
from sklearn.ensemble import RandomForestClassifier, AdaBoostClassifier
from sklearn.pipeline import Pipeline
from sklearn.metrics import precision_recall_curve, classification
from sklearn import metrics
import timeit
import time
#%% Importation des données
data_validation = pd.read_csv("test.csv")
data = pd.read_csv("train.csv")
data.head()

Les colonnes contiennent les informations suivantes :

  • PassengerId : numéro du passager
  • Survived : 1 si le passager a survécu, sinon 0
  • Pclass : La classe dans laquelle le passager a voyagé (1, 2 ou 3)
  • Name : Le titre et le nom
  • Sex : Homme ou femme
  • Age :
  • SibSp : Nombre de conjoints et frères/sœurs à bord
  • Parch : Nombre de parents/enfants à bord
  • Ticket : référence du ticket
  • Fare : prix payé
  • Cabin : numéro de cabine (pour ceux qui en ont)
  • Embarked : Port de départ (S = Southampton, C = Cherbourg, Q = Queenstown)

Afin de déterminer quelles sont les données pertinentes pour l’analyse, regardons d’abord les variables continues pour lesquelles je peux tracer la matrice de corrélation :

def plot_correlation_map( df ):
    corr = df.corr()
    _ , ax = plt.subplots( figsize =( 12 , 10 ) )
    cmap = sns.diverging_palette( 220 , 10 , as_cmap = True )
    _ = sns.heatmap(
        corr, 
        cmap = cmap,
        square=True, 
        cbar_kws={ 'shrink' : .9 }, 
        ax=ax, 
        annot = True, 
        annot_kws = { 'fontsize' : 12 }
    )

plot_correlation_map(data)

L’analyse de cette image nous permet de déterminer quels sont les paramètres (numériques) les plus pertinents à intégrer au modèle. La survie est négativement corrélée à la classe (les voyageurs en 3ème, ont moins survécu) et positivement au prix payé. L’âge et la taille des familles ont également, mais dans une moindre mesure, une influence sur les chances de survie.

Tous sexes confondus, on peut voir sur le graphe ci-dessous que les chances de survie sont plus élevées pour les jeunes (<15 ans environ) et pour les personnes âgées.

Regardons maintenant les variables catégorielles.

Les femmes et les enfants d’abord !” : on peut supposer que les chances de survie sont plus élevées pour les femmes. Qu’en est-il ?

def plot_cat(data, x_axis, y_axis, hue):
    plt.figure()    
    sns.barplot(x=x_axis, y=y_axis, hue=hue, data=data)
    sns.set_context("notebook", font_scale=1.6)
    plt.legend(loc="upper right", fontsize="medium")

plot_cat(data,"Sex", "Survived", None) 

Le graphe ci-dessus confirme cette intuition : seuls 20% des hommes ont survécu contre plus de 70% des femmes.

La classe étant le paramètre le plus corrélé à la survie, regardons de plus près :

plot_cat(data,"Pclass", "Survived", "Sex") 
plot_cat(data,"Pclass", "Survived", None)

Le taux de survie est bien plus élevé pour les passagers de première classe (>60%) que pour ceux de troisième classe (~ 25%)

Nous voyons également de fortes disparités entre hommes et femmes quelle que soit la classe.

Nous pouvons vérifier que la majorité des gens sans cabine ont voyagé en troisième ou deuxième classe. Les passagers n’ayant pas de cabines sont répartis comme suit : 40 en 1ère, 168 en 2nde et 479 en 3ème. Ce que l’on peut aussi représenter ci-dessous.

2 : Traitement des données manquantes

Afin de pouvoir établir un modèle plus précis, il faut traiter les données manquantes :

pd.isnull(data).sum()

PassengerId 0
Survived 0
Pclass 0
Name 0
Sex 0
Age 177
SibSp 0
Parch 0
Ticket 0
Fare 0
Cabin 687
Embarked 2

Les données sont plutôt propres, puisqu’il ne manque l’âge que pour 177 passagers et le port d’embarcation pour 2. Les 687 valeurs manquantes pour le numéro de cabine correspond aux voyageurs qui ont voyagé de manière spartiate, il ne s’agit donc pas réellement de valeur manquante.

Je vais compléter les valeurs manquantes de la manière suivante :

  • Pour l’âge, je pars du principe que les passagers de 1ère classe sont majoritairement plus âgés et plus riches que ceux de 3ème classe. Ainsi je vais faire un tri par classe et attribuer aux valeurs manquantes la médiane de l’âge des passagers de la classe correspondante
  • Pour les cabines, je vais établir un modèle simple et attribuer 0 si le passager n’a pas de cabine ou 1 s’il a voyagé en cabine (peu importe l’emplacement de la cabine).
def median_age(data,Pclass):
    med_age = round(data["Age"][data["Pclass"]==Pclass].median())
    return med_age

for i in [1,2,3]:
    data.loc[(data.Age.isnull()) & (data["Pclass"] == i), "Age"] = median_age(data,i)
data.loc[(data.Sex == "male", "Sex")]=1
data.loc[(data.Sex == "female", "Sex")]=0
data.loc[(data.Cabin.isnull()==False), "Cabin"] = 1
data.loc[(data.Cabin.isnull()), "Cabin"] = 0

L’âge médian par classe évolue bien comme pressenti : 1ere : 37 ans, 2ème : 29 ans, 3ème : 24 ans.

Enfin concernant l’âge, je vais créer une variable catégorielle, en groupant les passagers par tranche d’âge : bébés, enfants, ados, adultes, troisième âge. J’utilise simplement la fonction “pandas.cut” qui permet de réaliser cette opération en une seule ligne.

data["Agebin"] = pd.cut(data["Age"],bins=[0,4,16,60,85], labels=[1,2,3,4])

Afin que les classificateurs puissent travailler correctement, je vais effectuer une petite transformation pour le sexe en remplaçant “Male” par 1 et “Female” par 0 :

data.loc[(data.Sex == "male", "Sex")]=1
data.loc[(data.Sex == "female", "Sex")]=0

3 : Construction d’un modèle de type “Random Forest”

Pour construire mon modèle, j’ai donc retenu les paramètres suivants :

  • “Pclass”, “Sex”, “Agebin”, “Cabin”,
target = "Survived"
features = ["Pclass", "Sex","Age", "Cabin","Parch","SibSp"]

Pour évaluer la performance du modèle, je vais procéder à une cross-validation :

model = sklearn.ensemble.RandomForestClassifier(n_estimators=55,min_samples_leaf = 4)
scores_test = cross_val_score(model,data[features],data[target],cv=10) #Donne le score sur le test après avoir procédé à un fit
print("Accuracy: %0.2f (+/- %0.2f)" % (scores_test.mean(), scores_test.std() * 2))
model.fit(X_test,y_test)
print("Features importance")
for feat,i in zip(features,[j for j in range(len(features))]):
    print(feat,model.feature_importances_[i])

La précision est de 0.83 pour un recall de 0.76, ce qui signifie que ce modèle a tendance à trouver plus de faux négatifs, probablement en raison du plus grand nombre de morts que de survivants dans les données.

La dernière partie du code nous donne les paramètres par ordre d’importance :

Le score est d’environ 82%, et il ne reste plus qu’à appliquer le modèle sur les données de validation

predictions = model.predict(X_valid)

4 : Soumission des résultats et score

result = pd.DataFrame(columns=["PassengerId", "Survived"])
result["PassengerId"] = data_validation['PassengerId']
result["Survived"] = predictions_final

result.to_csv("sklearn-RF-1.csv", index=False) #On n'oublie pas d'enlever l'index

Il ne reste plus qu’à upload le fichier sur Kaggle et attendre le verdict….

Et mon score est de …

0.78947

Ce qui amène à un classement de 1584/6099.

Dans un prochain billet je montrerai les résultats obtenus avec un modèle d’arbre décisionnel simple ainsi qu’un modèle de k plus proches voisins.

2 thoughts on “Prédire les chances de survie au naufrage du Titanic”

  1. j’ai essayé d’exécuter votre programme
    l’erreur suivante apparait:
    ModuleNotFoundError: No module named ‘pandas’
    le package “pandas” existe mais comment l’installer sous windows

    1. Bonjour,
      Quelle installation de python utilisez-vous ? Si c’est anaconda c’est assez simple à installer depuis le gestionnaire.
      Autrement, en ligne de commande il suffit de taper pip install pandas

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.