Predecir la supervivencia en el Titanic

Predecir la supervivencia en el titanic aplicando machine learning forma parte de una competición de kaggle, que tiene como objetivo crear un modelo de predicción basado en regresión logística.

Análisis preliminar de los datos

La competición de kaggle proporciona dos datasets, uno para entrenamiento del modelo (train.csv) y otro para testearlo (test.csv). Las distintas variables que forman los conjuntos de datos son las siguientes:

VariableDefiniciónValores
survivalSobrevive0 = No, 1 = Yes
pclassClase1 = 1st, 2 = 2nd, 3 = 3rd
sexGénero
AgeEdad
sibspNúmero de hermanos / cónyuges a bordo del Titanic
parchNúmero de padres / hijos a bordo del Titanic
ticketNúmero de ticket
fareTarifa pasajero
cabinNúmero de cabina
embarkedPuerto de embarcaciónC = Cherbourg, Q = Queenstown, S = Southampton

Librerías utilizadas

Tratamiento de datos

El conjunto de datos ya viene dividido en conjunto de entrenamiento y test, con lo cual lo cargaremos de la siguiente forma:

train_df = pd.read_csv("./train.csv")
test_df = pd.read_csv("./test.csv")

Tratamiento de valores nulos

Ejecutando la siguiente función se puede apreciar la existencia de gran cantidad de valores nulos:

train_df.isnull().sum()

Teniendo en cuenta esto se puede presuponer que los datos necesitan un tratamiento adecuado antes de la creación del modelo. Por ello, lo primero será crear una copia del dataframe original para realizar las modificaciones pertinentes:

train_data = train_df.copy()

Para saber cual es el porcentaje de valores nulos de una variable se ha creado la siguiente función que utilizaremos posteriormente en varias ocasiones:

def missing_values_percentage(column):
    return round((column.isnull().sum()/train_df.shape[0])*100,2)

Edad

El porcentaje de valores nulos de la variable edad es de 19.9%. En la siguiente imagen se puede observar como esta variable tiene una distribución normal:

Dado que la «Edad» está sesgada (a la derecha), el uso de la media podría darnos resultados sesgados al rellenar las edades que son mayores de lo deseado. Para solucionar esto, utilizaremos la mediana para imputar los valores que faltan.

train_data["Age"].fillna(train_df["Age"].median(skipna=True), inplace=True)

Número de cabina

El porcentaje de valores nulos de esta variables es 77%, demasiado alto para utilizarla en la construcción del modelo. Por ello, la eliminaremos del conjunto de datos.

train_data.drop('Cabin', axis=1, inplace=True)

Puerto de embarcación

El porcentaje de valores nulos es muy bajo, menor a 1%. Al tratarse de variable categórica podemos observar en el siguiente gráfico los distintos valores.

Se puede observar que el valor más común es ‘S’, que será el utilizado para rellenar los valores nulos.

train_data["Embarked"].fillna(train_df['Embarked'].value_counts().idxmax(), inplace=True)

Tratamiento de variables categóricas

Según la fuente de los datos, las variables SibSP y Parch se refieren a viajar con la familia. Para simplificar y evitar problemas de multicolinealidad, se combinarán estas variables en un única variable para indicar si el individuo viajaba solo o no.

train_data['TravelAlone']=np.where((train_data["SibSp"]+train_data["Parch"])>0, 0, 1)
train_data.drop('SibSp', axis=1, inplace=True)
train_data.drop('Parch', axis=1, inplace=True)

También se han creado varias variables dummy en base a las variables categóricas Pclass, Embarked, Sex.

training=pd.get_dummies(train_data, columns=["Pclass","Embarked","Sex"])

Por último, se han eliminado algunas variables no necesarias para la creación del modelo.

training.drop('Sex_female', axis=1, inplace=True)
training.drop('PassengerId', axis=1, inplace=True)
training.drop('Name', axis=1, inplace=True)
training.drop('Ticket', axis=1, inplace=True)

final_train = training

Aplicar cambios al conjunto de test

Los cambios explicados anteriormente se aplicaron únicamente el conjunto de datos de entrenamiento. A continuación, se aplicarán al conjunto de pruebas.

test_data = test_df.copy()
test_data["Age"].fillna(train_df["Age"].median(skipna=True), inplace=True)
test_data["Fare"].fillna(train_df["Fare"].median(skipna=True), inplace=True)
test_data.drop('Cabin', axis=1, inplace=True)

test_data['TravelAlone']=np.where((test_data["SibSp"]+test_data["Parch"])>0, 0, 1)

test_data.drop('SibSp', axis=1, inplace=True)
test_data.drop('Parch', axis=1, inplace=True)

testing = pd.get_dummies(test_data, columns=["Pclass","Embarked","Sex"])
testing.drop('Sex_female', axis=1, inplace=True)
testing.drop('Name', axis=1, inplace=True)
testing.drop('Ticket', axis=1, inplace=True)

final_test = testing

Modelo de regresión logística

Lo primero será dividir el conjunto de entrenamiento en variable a predecir (y) y posibles variables predictoras (x):

features = ["Age","Fare","TravelAlone","Pclass_1","Pclass_2","Embarked_C","Embarked_S","Sex_male","IsMinor"]
x_train = final_train[features]
y_train = final_train['Survived']

A continuación, pasamos a crear el modelo:

model = LogisticRegression(max_iter=1000)
model.fit(x_train, y_train)

Una vez construido el modelo, podemos utilizarlo para predecir con los datos del conjunto de testeo:

x_test = final_test[features]
final_test['Survived'] = model.predict(x_test)

Si comparamos los resultados de nuestro modelo con los resultados proporcionados en la competición de kaggle podemos observar una precisión del 95%, o lo que es lo mismo nuestros resultados difieren únicamente en un 5% de los resultados de ejemplo.

Notebook

Fuentes

Deja un comentario