Análisis de supervivencia en el RMS Titanic: Proyecto S-Titanic

Índice

Análisis de supervivencia en el RMS Titanic:

El RMS Titanic 1 fue un transatlántico británico, el mayor barco del mundo al finalizar su construcción, que se hundió en la noche del 14 a la madrugada del 15 de abril de 1912 durante su viaje inaugural desde Southampton a Nueva York. En el hundimiento del Titanic murieron 1514 personas de las 2223 que iban a bordo, lo que convierte a esta tragedia en uno de los mayores naufragios de la historia ocurridos en tiempo de paz. Construido entre 1909 y 1912 en el astillero Harland and Wolff de Belfast, el Titanic era el segundo de los tres transatlánticos que formaban la clase Olympic, propiedad de la naviera White Star Line, junto al RMS Olympic y, posteriormente, el HMHS Britannic.
A continuación, se utilizara un set de datos (train.csv y test.csv) el cual contiene un listado de los pasajeros que estuvieron abordo del Titanic para anlizar la supervivenvia de los pasajeros segun ciertas caracteristicas (Sexo, edad, cabina, embarcación, entre otras).



Ademas, utilizaremos la librería Scikit-Learn para el procesamiento de datos, y la predicción de nuevos datos sin etiquetar (Determinar la probabilidad de supervivencia de un nuevo pasajero, según nuevas categorías ingresadas) los cuales se encuentran en el conjunto de datos de prueba.
Para este ejercicio, es necesario que el lector tengo conocimientos básicos sobre el Machine learning. En el blog, pueden encontrar una serie de artículos enfocados al Machine Learning y sus fundamentos. 
Las librerias que se utilizaran a lo largo del ejercicio son: Skcikit-Learn, Numpy, Pandas y Seaborn.

Conjuntos de datos:

En el repositorio en el cual se encuentra este pequeño ejercicio (https://github.com/LuisAlejandroSalcedo/Proyecto-S-Titanic/), se encuentran dos archivos de formato CSV (Valores Separados por Coma) los cuales contienen los datos que utilizaremos en este ejercicio. Los dos archivos que contiene los datos son:

  1. train.csv (https://github.com/LuisAlejandroSalcedo/Proyecto-S-Titanic/blob/master/train.csv): En este conjunto de datos, se encuentran la lista de pasajeros a los cuales analizaremos gráficamente. Y al final, lo utilizaremos para entrenar un modelo de aprendizaje.
  2. test.csv (https://github.com/LuisAlejandroSalcedo/Proyecto-S-Titanic/blob/master/test.csv): Este conjunto de datos, contiene una lista de pasajeros, a los cuales no se les a clasificado su supervivencia. Esta lista, la utilizaremos al final para determinar la supervivencia de dichos pasajeros según sus ciertas características (Sexo, edad, cabina, embarcación, entre otras) .

Representación gráfica de los datos:

Perfecto, una vez que tengamos todo lo necesario, podemos comenzar.

Comencemos por importar las librerías necesarias:

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline

Luego de esto, procedemos a cargar nuestros datos de los archivos descargados anteriormente. Esto lo lograremos con el método "read_csv", el cual nos proporciona "pandas":

data_train = pd.read_csv('train.csv') # Datos de entrenamiento
data_test = pd.read_csv('test.csv') # Datos de Prueba

De esta manera, en las variables "data_train" y "data_test" se encontraran los datos de los respectivos archivos.

"Pandas" guarda la información de los datos csv en forma de tabla. Podemos ver un pequeño vistazo de su contenido usando el método "sample" y pasando le como argumento la cantidad de ejemplos que queremos visualizar:

data_train.sample(3)


El resultado seria:





Muy bien. También debemos tener en cuenta de que cada vez que se llama al metodo "samples", nos devolverá datos aleatorios, por ende existen pocas posibilidades de que les muestra la tabla que me dio como resultado.


Perfecto, tenemos la información ordenada en una tabla la cual nos facilitara su manipulación. Como podemos observar, a cada pasajero se le asignan ciertas características: Su respectivo ID, sobrevivió (0 para los que no, y 1 para los que sí), clase, nombre del pasajero, sexo, edad (en numero con punto decimal), entre otros.


Con estos datos, podemos representar de forma gráfica el numero de sobrevivientes según ciertas características. En este caso, las características que usare, sera el sexo y la embarcación.


Para representar de forma grafico estos datos, podemos usar librerías como matplotlib. Pero en este caso, utilizaremos una librería llamada "seaborn", la cual es muy sencilla de utilizar. Pero queda de tu parte escoger tu librería de preferencia.


Para representar datos gráficamente, existen distintas formas. Una de ellas es el diagrama de barras. La cual podemos generar con "seaborn" con un solo método:

sns.barplot(x="Embarked", y="Survived", hue="Sex", data=data_train)

Out[36]:
<matplotlib.axes._subplots.AxesSubplot at 0xc12f350>







Como podemos ver en el diagrama, la mayoría de pasajeros que sobrevivieron eran mujeres.


Para generar este diagrama con "seaborn", solo debemos llamar al método "borplot" y pasarle como argumentos la información que queremos graficar.

A parte de los diagramas de barras, podemos generar otros tipos de diagramas, como el que les muestro a continuación:


sns.pointplot(x="Pclass", y="Survived", hue="Sex", data=data_train,
                 palette={"male": "blue", "female": "pink"},
                  markers=["*", "o"], linestyles=["-", "--"])

Out[37]:
<matplotlib.axes._subplots.AxesSubplot at 0xc1ce210>

Transformando Características:

Perfecto, ya hemos visto datos graficados según sus características.



Estas características podemos simplificarlas. Vemos que en edad hay mucha variedad. Simplificar este tipo de características nos ayudara, por ejemplo a analizar los sobreviviente menores de edad.  


Para la simplificación de las características (ages, cabins, fares y name), escribiremos una función por cada simplificación. Todo nos quedaría algo así: 


def simplify_ages(df):
    df.Age = df.Age.fillna(-0.5)
    bins = (-1, 0, 5, 12, 18, 25, 35, 60, 120)
    group_names = ['Unknown', 'Baby', 'Child', 'Teenager', 'Student', 'Young Adult', 'Adult', 'Senior']
    categories = pd.cut(df.Age, bins, labels=group_names)
    df.Age = categories
    return df

def simplify_cabins(df):
    df.Cabin = df.Cabin.fillna('N')
    df.Cabin = df.Cabin.apply(lambda x: x[0])
    return df

def simplify_fares(df):
    df.Fare = df.Fare.fillna(-0.5)
    bins = (-1, 0, 8, 15, 31, 1000)
    group_names = ['Unknown', '1_quartile', '2_quartile', '3_quartile', '4_quartile']
    categories = pd.cut(df.Fare, bins, labels=group_names)
    df.Fare = categories
    return df

def format_name(df):
    df['Lname'] = df.Name.apply(lambda x: x.split(' ')[0])
    df['NamePrefix'] = df.Name.apply(lambda x: x.split(' ')[1])
    return df    
    
def drop_features(df):
    return df.drop(['Ticket', 'Name', 'Embarked'], axis=1)

def transform_features(df):
    df = simplify_ages(df)
    df = simplify_cabins(df)
    df = simplify_fares(df)
    df = format_name(df)
    df = drop_features(df)
    return df

data_train = transform_features(data_train)
data_test = transform_features(data_test)
data_train.head()




Muy bien, los nombres de la funciones son muy obvias y muy predecibles. En la primera ("simplify_ages") simplificamos las edades, sustituimos números por texto. Cuando la edad es "-1" decimos que es "Unknown" (desconocido), cundo es  0 decimos que es un "baby" (un bebe), cuando es 5 decimos que es un "child" (un niño) y así sucesivamente. Las simplificaciones las he escrito en ingles por la costumbre, pero no hay problema en colocarlas en español.


Luego de crear una función para cada simplificación, declaramos la función "transform_features" la cual llama a todas las funciones anteriores y nos devuelve los datos simplificados. Al final asignamos nuevos valores a las variables "data_train" y "data_test" llamando a la función "transform_features".


Con la función "head", pandas nos mostrara las primeras columnas de los datos simplificados. Al ejecutar el código anterior, nos devolverá algo así:







La diferencia es notoria. Ahora podemos graficar estos datos simplificados. Podemos utilizar el diagrama de barras utilizado anteriormente:


sns.barplot(x="Age", y="Survived", hue="Sex", data=data_train)

Out[39]:
<matplotlib.axes._subplots.AxesSubplot at 0xc219350>

De esta forma, vemos los sobrevivientes según su edad.


También podemos ver los sobrevivientes, basados en las otras características simplificadas:


sns.barplot(x="Cabin", y="Survived", hue="Sex", data=data_train)

Out[40]:
<matplotlib.axes._subplots.AxesSubplot at 0xc285310>



sns.barplot(x="Fare", y="Survived", hue="Sex",data=data_train)

Out[41]:
<matplotlib.axes._subplots.AxesSubplot at 0xc2f8090>


Preprocesamineto de datos:

Muy bien, con las simplificaciones de los datos se nos hace mejor trabajar con ellos. Ye hemos graficado suficientes datos ¿No?. Los invito a que no se limiten y utilizan las otras características.

Llegamos a la parte final. Como menciona al principio, en esta parte utilizaremos el set de datos "test.csv" el cual tiene una lista de pasajeros a los cuales no se les a clasificado como vivos o muertos. Lo que haremos sera entrenar a un clasificador que mediante ciertas características de estos pasajeros, pueda determinar la posibilidad de que estos pasajeros sobrevivieran o no.

Muy bien, en esta parte estaremos utilizando métodos de Aprendizaje Automático. Sabemos que estos métodos son matemáticos y analíticos, por lo cual solo se manejan con números. Para ello usaremos métodos de preprocesamiento de datos para transformar las características de los pasajeros, a numero. Como la característica survived, la cual nos indica si un pasajero sobrevivió mediante 0 (no sobrevivió) y 1 (sí sobrevivió). Esto lo haremos con las características como: Sex, Age, LName, etc.

El preprocesamiento lo podemos lograr con la libreria scikit-learn. Lo podemos hacer de la siguiente manera:

from sklearn import preprocessing
def encode_features(df_train, df_test):
    features = ['Fare', 'Cabin', 'Age', 'Sex', 'Lname', 'NamePrefix']
    df_combined = pd.concat([df_train[features], df_test[features]])
    
    for feature in features:
        le = preprocessing.LabelEncoder()
        le = le.fit(df_combined[feature])
        df_train[feature] = le.transform(df_train[feature])
        df_test[feature] = le.transform(df_test[feature])
    return df_train, df_test
    
data_train, data_test = encode_features(data_train, data_test)
data_train.head()

El resultado del código anterior es:







Como podemos observar, todo texto ahora sera representado por números.

Perfecto, ahora lo que haremos sera dividir los datos en: Conjunto de entrenamiento y Conjunto de prueba.


En la variable x_all almacenare todas las características, menos la que queremos predecir (Si sobrevivió).


En la variable y_all almacenare la característica que queremos predecir.


from sklearn.model_selection import train_test_split

X_all = data_train.drop(['Survived', 'PassengerId'], axis=1)
y_all = data_train['Survived']

num_test = 0.20
X_train, X_test, y_train, y_test = train_test_split(X_all, y_all, test_size=num_test, random_state=23)





Muy bien, luego de tener nuestros datos bien distribuidos, ya podemos empezar a entrenar al algoritmo en este caso un clasificador. El clasificador que utilizaremos para  este ejercicio es "RandomForest". A continuación, declarare al clasificador para luego entrenarlo:


from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import make_scorer, accuracy_score
from sklearn.model_selection import GridSearchCV

# Choose the type of classifier. 
clf = RandomForestClassifier()

# Choose some parameter combinations to try
parameters = {'n_estimators': [4, 6, 9], 
              'max_features': ['log2', 'sqrt','auto'], 
              'criterion': ['entropy', 'gini'],
              'max_depth': [2, 3, 5, 10], 
              'min_samples_split': [2, 3, 5],
              'min_samples_leaf': [1,5,8]
             }

# Type of scoring used to compare parameter combinations
acc_scorer = make_scorer(accuracy_score)

# Run the grid search
grid_obj = GridSearchCV(clf, parameters, scoring=acc_scorer)
grid_obj = grid_obj.fit(X_train, y_train)

# Set the clf to the best combination of parameters
clf = grid_obj.best_estimator_

# Fit the best algorithm to the data. 
clf.fit(X_train, y_train)




Luego, podemos que tan bien aprendio nuestro algoritmo:


predicton = clf.predict(X_test)
accuracy_score(y_test ,predicton)

Out[29]:
0.79329608938547491



Esto nos devolverá el porcentaje de las veces en las que el algoritmo acertó en sus predicciones. En este caso fue del 79%. Este numero podemos subirlo cambiando algunos valores de los parámetros del clasificador.

Por ultimo, hacemos las predicciones las cuales guardare en un archivo llamado "titanic-predictions.csv".


ids = data_test['PassengerId']
predictions = clf.predict(data_test.drop('PassengerId', axis=1))


output = pd.DataFrame({ 'PassengerId' : ids, 'Survived': predictions })
output.to_csv('titanic-predictions.csv', index = False)
output.head(10)



El resultado seria algo así:






Ademas del archivo generado con todas las predicciones.

Todos los archivos mostrados en este articulo puedes encontrarlos en mi repositorio: https://github.com/LuisAlejandroSalcedo/Proyecto-S-Titanic.


Este ejercicio los hago con motivos educativos. Pero no debemos olvidar que esta tragedia, se cobra mas de mil vidas.


Mi nombre es Luis, y fue un placer compartir mis conocimientos con todos ustedes 😀

  1. Jose Luis dice:

    Hola, estoy aprendiendo python y he encontrado tu blog, y me ha gustado mucho. Una pregunta. Estoy intentando repetir el ejercicio tal y como lo propones y tengo dos problemas,

    En primer lugar, la linea %matplotlib inline me da error.
    Si comento la línea, las sentencias barplot y pointplot no producen error, pero en la salida no se ve nada.
    Estoy usando python 3.6 desde sublime text y el sublime repl.

    Muchas gracias y enhorabuena por tu blog.

    1. mariapazpaz dice:

      Pudiste solucionarlo?

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir
White Monkey