Análisis de los supervivientes del Titanic, creación de modelo de Machine Learning que predice si un pasajero sobrevivió o murió durante el naufragio.

El hundimiento del RMS Titanic es uno de los mayores naufragios de la historia. En abril de 1912 durante su viaje inaugural, el Titanic se hundió después de colisionar con un iceberg, matando 1502 de 2224 pasajeros y tripulación. Después de esta tragedia sensacional la comunidad internacional impulsó la creación de mejores regularizaciones para una optima seguridad de los barcos.

Una de las razones de que el naufragio tuviese muchas pérdidas de vidas fue que tenían suficientes salvavidas para los pasajeros y tripulación. A pesar de que hubo mucha suerte involucrada en la supervivencia del hundimiento, algunos grupos tuvieron mayor probabilidad de sobrevivir que otros, tales como mujeres, niños y la clase alta.


Primeras 5 filas de los datos a analizar valores de los datos

PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
0 1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S
1 2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 PC 17599 71.2833 C85 C
2 3 1 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2. 3101282 7.9250 NaN S
3 4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 113803 53.1000 C123 S
4 5 0 3 Allen, Mr. William Henry male 35.0 0 0 373450 8.0500 NaN S

Completar los datos

Buscaremos cuantos valores nulos se encuentran en el conjunto de datos

PassengerId 0
Survived 0
Pclass 0
Name 0
Sex 0
Age 263
SibSp 0
Parch 0
Ticket 0
Fare 1
Cabin 1014
Embarked 2
dtype: int64

Se encuentran 263 datos nulos en la columna de edad, 1014 en la columna de la cabina, 2 en la ciudad donde embarcaron y uno en la tarifa del boleto.

Sustituiremos los valores nulos de edad y de la tarifa usando la media de edad de los datos que sí conocemos de sus respectivas columnas, mientras que en la columna de embarcamiento los remplazaremos con 0’s.

Borraremos la columna de identificación del pasajero y del ticket ya que no nos provee de información util, también eliminaremos la cabina, pero esta vez lo haremos ya que más de la mitad de los datos de esa columna están perdidos.

for dataset in data_cleaner:    

    dataset['Age'].fillna(dataset['Age'].median(), inplace = True)

    dataset['Embarked'].fillna(dataset['Embarked'].mode()[0], inplace = True)

    dataset['Fare'].fillna(dataset['Fare'].median(), inplace = True)
    
drop_column = ['PassengerId','Cabin', 'Ticket']
data1.drop(drop_column, axis=1, inplace = True)

Verifiquemos que no hay más datos nulos.

Survived 0
Pclass 0
Name 0
Sex 0
Age 0
SibSp 0
Parch 0
Fare 0
Embarked 0
dtype: int64

Crearemos una columna que nos cuente el tamaño de su familia a bordo, usando las columnas hermanos/cónyuge y padres/hijos, también se hará una columna que diga si el pasajero viaja solo o no, adicionalmente crearemos una columna con el titulo de la persona, obtendremos este campo con la columna de nombre; Además crearemos grupos de edades y de tarifas para que sea más fácil para el modelo interpretar los datos.

for dataset in data_cleaner:    
    dataset['FamilySize'] = dataset ['SibSp'] + dataset['Parch'] + 1

    dataset['IsAlone'] = 1
    dataset['IsAlone'].loc[dataset['FamilySize'] > 1] = 0

    dataset['Title'] = dataset['Name'].str.split(", ", expand=True)[1]\
        .str.split(".", expand=True)[0]

    dataset['FareBin'] = pd.qcut(dataset['Fare'], 4)

    dataset['AgeBin'] = pd.cut(dataset['Age'].astype(int), 5)

Miraremos las primeras 5 filas del conjunto de datos modificado.

Survived Pclass Name Sex Age SibSp Parch Fare Embarked FamilySize IsAlone Title FareBin AgeBin
0 0 3 Braund, Mr. Owen Harris male 22.0 1 0 7.2500 S 2 0 Mr (-0.001, 7.91] (16.0, 32.0]
1 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 71.2833 C 2 0 Mrs (31.0, 512.329] (32.0, 48.0]
2 1 3 Heikkinen, Miss. Laina female 26.0 0 0 7.9250 S 1 1 Miss (7.91, 14.454] (16.0, 32.0]
3 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 53.1000 S 2 0 Mrs (31.0, 512.329] (32.0, 48.0]
4 0 3 Allen, Mr. William Henry male 35.0 0 0 8.0500 S 1 1 Mr (7.91, 14.454] (32.0, 48.0]

Convertir formatos

Usaremos un codificador de etiquetas para las columnas con variables categóricas.

label = LabelEncoder()
for dataset in data_cleaner:    
    dataset['Sex_Code'] = label.fit_transform(dataset['Sex'])
    dataset['Embarked_Code'] = label.fit_transform(dataset['Embarked'])
    dataset['Title_Code'] = label.fit_transform(dataset['Title'])
    dataset['AgeBin_Code'] = label.fit_transform(dataset['AgeBin'])
    dataset['FareBin_Code'] = label.fit_transform(dataset['FareBin'])

Columnas originales

Original X Y ‘Survived’ ‘Sex’ ‘Pclass’ ‘Embarked’ ‘Title’ ‘SibSp’
  ‘Parch’ ‘Age’ ‘Fare’ ‘FamilySize’ ‘IsAlone’  

Columnas en cajas

Bin X Y ‘Survived’ ‘Sex_Code’ ‘Pclass’ ‘Embarked_Code’
  ‘Title_Code’ ‘FamilySize’ ‘AgeBin_Code’ ‘FareBin_Code’

Columnas con variables ficticias

Dummy X Y ‘Survived’ ‘Pclass’ ‘SibSp’ ‘Parch’ ‘Age’
  ‘Fare’ ‘FamilySize’ ‘IsAlone’ ‘Sex_female’ ‘Sex_male’
  ‘Embarked_C’ ‘Embarked_Q’ ‘Embarked_S’ ‘Title_Master’ ‘Title_Misc’
  ‘Title_Miss’ ‘Title_Mr’ ‘Title_Mrs’    

Primeras 5 filas del conjunto de datos con las variables ficticias.

Pclass SibSp Parch Age Fare FamilySize IsAlone Sex_female Sex_male Embarked_C Embarked_Q Embarked_S Title_Master Title_Misc Title_Miss Title_Mr Title_Mrs
0 3 1 0 22.0 7.2500 2 0 0 1 0 0 1 0 0 0 1 0
1 1 1 0 38.0 71.2833 2 0 1 0 1 0 0 0 0 0 0 1
2 3 0 0 26.0 7.9250 1 1 1 0 0 0 1 0 0 1 0 0
3 1 1 0 35.0 53.1000 2 0 1 0 0 0 1 0 0 0 0 1
4 3 0 0 35.0 8.0500 1 1 0 1 0 0 1 0 0 0 1 0

Dividir los datos

Dividiremos el conjunto original en entrenamiento y pruebas

Datos originales: (891, 19)
Datos de entrenamiento: (668, 19)
Datos de prueba: (223, 19)

Realizar análisis exploratorio

Porcentaje de hombres y mujeres que sobrevivieron.

png

Porcentaje de personas de cierta clase social que sobrevivieron.

png

Porcentaje de personas que sobrevivieron según su zona de embarque.

png

Porcentaje de personas que sobrevivieron según su titulo.

png

Porcentaje de personas que sobrevivieron dependiendo del número de hermanos/cónyuges a bordo

png

Porcentaje de personas que sobrevivieron dependiendo del número de padres e hijos a bordo

png

Porcentaje de personas que sobreviveron dependiendo del número de integrantes de su familia a bordo.

png

Porcentaje de personas que sobrevivieron dependiendo si viajaban solas o no.

png

Algunos gráficos extra.

png

Mapa de correlación de las columnas del conjunto de datos.

png

Modelar los datos

Tabla que nos indica el rendimiento de los diversos modelos utilizados para resolver el problema.

MLA Name MLA Parameters MLA Train Accuracy Mean MLA Test Accuracy Mean MLA Test Accuracy 3*STD MLA Time
7 SVC {'C': 1.0, 'break_ties': False, 'cache_size': ... 0.835206 0.827612 0.0409157 0.136107
4 RandomForestClassifier {'bootstrap': True, 'ccp_alpha': 0.0, 'class_w... 0.895131 0.826493 0.0633724 0.658607
12 XGBClassifier {'objective': 'binary:logistic', 'base_score':... 0.890449 0.826493 0.0617704 0.356233
8 NuSVC {'break_ties': False, 'cache_size': 200, 'clas... 0.834082 0.826119 0.0456629 0.15811
2 ExtraTreesClassifier {'bootstrap': False, 'ccp_alpha': 0.0, 'class_... 0.895131 0.824254 0.0593284 0.466217
3 GradientBoostingClassifier {'ccp_alpha': 0.0, 'criterion': 'friedman_mse'... 0.866667 0.822015 0.0529916 0.341489
9 DecisionTreeClassifier {'ccp_alpha': 0.0, 'class_weight': None, 'crit... 0.895131 0.820896 0.0579501 0.00795968
1 BaggingClassifier {'base_estimator': None, 'bootstrap': True, 'b... 0.890075 0.81903 0.0631744 0.0723112
6 KNeighborsClassifier {'algorithm': 'auto', 'leaf_size': 30, 'metric... 0.850375 0.813806 0.0690863 0.00837021
0 AdaBoostClassifier {'algorithm': 'SAMME.R', 'base_estimator': Non... 0.820412 0.81194 0.0498606 0.307621
5 GaussianProcessClassifier {'copy_X_train': True, 'kernel': None, 'max_it... 0.871723 0.810448 0.0492537 0.668614
11 QuadraticDiscriminantAnalysis {'priors': None, 'reg_param': 0.0, 'store_cova... 0.821536 0.80709 0.0810389 0.00862453
10 ExtraTreeClassifier {'ccp_alpha': 0.0, 'class_weight': None, 'crit... 0.895131 0.806716 0.0557008 0.0073771

Gráfico de barras con la exactitud de cada uno de los modelos utilizados.

png

Evaluar el rendimiento del modelo

Porcentaje de sobrevivientes femeninos dependiendo de su clase social, la ciudad de embarcamiento y la tarifa de su boleto.

Sex Pclass Embarked FareBin Surviving percentage
female 1 C (14.454, 31.0] 0.666667
      (31.0, 512.329] 1.000000
    Q (31.0, 512.329] 1.000000
    S (14.454, 31.0] 1.000000
      (31.0, 512.329] 0.955556
  2 C (7.91, 14.454] 1.000000
      (14.454, 31.0] 1.000000
      (31.0, 512.329] 1.000000
    Q (7.91, 14.454] 1.000000
    S (7.91, 14.454] 0.875000
      (14.454, 31.0] 0.916667
      (31.0, 512.329] 1.000000
  3 C (-0.001, 7.91] 1.000000
      (7.91, 14.454] 0.428571
      (14.454, 31.0] 0.666667
    Q (-0.001, 7.91] 0.750000
      (7.91, 14.454] 0.500000
      (14.454, 31.0] 0.714286
    S (-0.001, 7.91] 0.533333
      (7.91, 14.454] 0.448276
      (14.454, 31.0] 0.357143
      (31.0, 512.329] 0.125000

Porcentaje de sobrevivientes masculinos dependiendo de su titulo.

Sex Title Surviving percentage
male Master 0.575000
  Misc 0.250000
  Mr 0.156673

Rendimiento del modelo con Cross-Validation

Evaluación del árbol de decision antes de Cross-Validation

BEFORE DT Parameters:  {'ccp_alpha': 0.0, 'class_weight': None,
                        'criterion': 'gini', 'max_depth': None, 
                        'max_features': None, 'max_leaf_nodes': None, 
                        'min_impurity_decrease': 0.0, 'min_impurity_split': None, 
                        'min_samples_leaf': 1, 'min_samples_split': 2, 
                        'min_weight_fraction_leaf': 0.0, 'presort': 'deprecated', 
                        'random_state': 0, 'splitter': 'best'}
BEFORE DT Training w/bin score mean: 89.51
BEFORE DT Test w/bin score mean: 82.09
BEFORE DT Test w/bin score 3*std: +/- 5.57

Evaluación del árbol de decision después de Cross-Validation

AFTER DT Parameters:  {'criterion': 'gini', 'max_depth': 4,
                       'max_features': None, 'min_samples_leaf': 5, 
                       'min_samples_split': 2, 'random_state': 0, 
                       'splitter': 'best'}
AFTER DT Training w/bin score mean: 89.25
AFTER DT Test w/bin score mean: 87.68
AFTER DT Test w/bin score 3*std: +/- 6.00

Evaluación del árbol de decision antes de recursive feature elimination Cross-Validation

BEFORE DT RFE Training Shape Old:  (891, 7)
BEFORE DT RFE Training Columns Old:  ['Sex_Code' 'Pclass' 'Embarked_Code' 'Title_Code' 'FamilySize'
 'AgeBin_Code' 'FareBin_Code']
BEFORE DT RFE Training w/bin score mean: 89.51
BEFORE DT RFE Test w/bin score mean: 82.09
BEFORE DT RFE Test w/bin score 3*std: +/- 5.57

Evaluación del árbol de decision después de recursive feature elimination Cross-Validation

AFTER DT RFE Training Shape New:  (891, 6)
AFTER DT RFE Training Columns New:  ['Sex_Code' 'Pclass' 'Title_Code' 
                                     'FamilySize' 'AgeBin_Code' 'FareBin_Code']
AFTER DT RFE Training w/bin score mean: 88.16
AFTER DT RFE Test w/bin score mean: 83.06
AFTER DT RFE Test w/bin score 3*std: +/- 6.22

Evaluación del árbol de decision después de Cross-Validation usando las nuevas columnas

AFTER DT RFE Tuned Parameters:  {'criterion': 'gini', 'max_depth': 8,
                                 'max_features': None, 'min_samples_leaf': 5,
                                 'min_samples_split': 2, 'random_state': 0, 
                                 'splitter': 'best'}
AFTER DT RFE Tuned Training w/bin score mean: 89.23
AFTER DT RFE Tuned Test w/bin score mean: 87.82
AFTER DT RFE Tuned Test w/bin score 3*std: +/- 6.81
----------

Visualización del árbol de decisión creado por el modelo.

svg

Validar e implementar el modelado de datos

Votación sin hyper-parametrización

Hard Voting Training w/bin score mean: 87.90
Hard Voting Test w/bin score mean: 82.01
Hard Voting Test w/bin score 3*std: +/- 3.50
----------
Soft Voting Training w/bin score mean: 88.65
Soft Voting Test w/bin score mean: 82.95
Soft Voting Test w/bin score 3*std: +/- 5.82
----------
Classifier Parameter_name Parameter_value
AdaBoostClassifier ‘algorithm’ ‘SAMME.R’
  ‘learning_rate’ 0.1
  ‘n_estimators’ 300
  ‘random_state’ 0
  with a runtime of 302.35 seconds.  
ExtraTreesClassifier ‘criterion’ ‘entropy’
  ‘max_depth’ 6
  ‘n_estimators’ 100
  ‘random_state’ 0
  with a runtime of 281.81 seconds.  
GradientBoostingClassifier    
  ‘criterion’ ‘friedman_mse’
  ‘learning_rate’ 0.05
  ‘loss’ ‘deviance’
  ‘max_depth’ 2
  ‘n_estimators’ 300
  ‘random_state’ 0
  with a runtime of 3145.16 seconds.  
RandomForestClassifier ‘criterion’ ‘entropy’
  ‘max_depth’ 6
  ‘n_estimators’ 100
  ‘oob_score’ True
  ‘random_state’ 0
  with a runtime of 466.24 seconds.  
GaussianProcessClassifier ‘max_iter_predict’ 10
  ‘random_state’ 0
  with a runtime of 43.35 seconds.  
KNeighborsClassifier ‘algorithm’ ‘brute’
  ‘n_neighbors’ 7
  ‘weights’ ‘uniform’
  with a runtime of 14.40 seconds.  
SVC ‘C’ 2
  ‘decision_function_shape’ ‘ovo’
  ‘gamma’ 0.1
  ‘probability’ True
  ‘random_state’ 0
  with a runtime of 86.27 seconds.  
XGBClassifier ‘learning_rate’ 0.01
  ‘max_depth’ 4
  ‘n_estimators’ 300
  ‘seed’ 0
  with a runtime of 271.92 seconds.  
  Total optimization time was 76.86 minutes.  

Resultados finales con hyperparametros

Hard Voting w/Tuned Hyperparameters Training w/bin score mean: 85.52
Hard Voting w/Tuned Hyperparameters Test w/bin score mean: 82.46
Hard Voting w/Tuned Hyperparameters Test w/bin score 3*std: +/- 4.90
----------
Soft Voting w/Tuned Hyperparameters Training w/bin score mean: 85.22
Soft Voting w/Tuned Hyperparameters Test w/bin score mean: 82.46
Soft Voting w/Tuned Hyperparameters Test w/bin score 3*std: +/- 5.88
----------

Para ver el repositorio completo ¡Click aquí!