Trabajo Práctico 2: Análisis con KNN - Organización de Datos

Alumnos y Padrón

  • Grassano, Bruno - 103855
  • Romero, Adrián - 103371

https://github.com/brunograssano/TP-Organizacion-de-datos

Configuraciones iniciales

Cargamos las bibliotecas que se van a estar usando a lo largo de este notebook.

In [1]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
In [2]:
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import KFold, StratifiedKFold
In [3]:
from sklearn.neighbors import KNeighborsClassifier
In [4]:
from preprocessing import prepararSetDeDatos
from preprocessing import prepararSetDeValidacion
from preprocessing import prepararSetDeHoldout
from preprocessing import expansionDelDataset
from preprocessing import conversionAVariablesNormalizadas
In [5]:
from funcionesAuxiliares import mostrarAUCScore
from funcionesAuxiliares import mostrarROCCurve
from funcionesAuxiliares import mostrarMatrizDeConfusion
from funcionesAuxiliares import escribirPrediccionesAArchivo
from funcionesAuxiliares import obtenerDatasets
from funcionesAuxiliares import obtenerHoldout

Carga y preparación del set de datos

In [6]:
X, y = obtenerDatasets()

X = prepararSetDeDatos(X)
y = prepararSetDeValidacion(y)

Funciones auxiliares

In [7]:
def obtenerMejoresHiperparametros(datosPreprocesados):
    mejor_distancia = None
    mejor_metrica = None
    mejor_valor = 0
    mejor_k = None
    y_array=np.array(y)
    for distancia in ['uniform', 'distance']:
        for metrica in ['minkowski','cosine','chebyshev','correlation']:
            for k in [2,3,4,5,6,7,8,9,10,15,20,25,30]:
                kf = StratifiedKFold(n_splits=8)
                metricas = []
                for fold_idx, (train_index, test_index) in enumerate(kf.split(datosPreprocesados, y_array)):
                    knn = KNeighborsClassifier(n_neighbors = k, weights = distancia,  metric = metrica, n_jobs = -1)
                    knn.fit(datosPreprocesados[train_index], y_array[train_index].ravel())
                    predicciones = knn.predict_proba(datosPreprocesados[test_index])[:, 1]
                    score_obtenida = roc_auc_score(y_array[test_index],predicciones)
                    metricas.append(score_obtenida)

                if np.mean(metricas) >= mejor_valor:
                    mejor_valor = np.mean(metricas)
                    mejor_k = k
                    mejor_distancia = distancia
                    mejor_metrica = metrica
            
    return mejor_valor, mejor_k, mejor_distancia, mejor_metrica

KNN

KNN es un método de aprendizaje supervisado que consiste en predecir la clase de una instancia en base a sus vecinos.

Antes de comenzar a realizar predicciones, tenemos que tener en cuenta varias cuestiones. Estas son: qué clase de preprocesamiento necesita este metodo, y cuáles son los hiperparámetros que debemos de buscar para obtener mejores resultados.

Para el procesamiento de los datos, decidimos primero realizar la conversión a variables categóricas mediante OneHotEncoding de las variables detectadas en la primera parte del trabajo. También se le aplica a los datos en este método una normalización, de forma tal de que algún feature no tenga demasiado peso, cosa que puede afectar a las distancias al momento de calcularlas.

In [8]:
X_knn = conversionAVariablesNormalizadas(X)

Para la búsqueda de los hiperparámetros, consideramos que los mas relevantes son los siguientes:

  • K
  • El peso de las distancias
  • La métrica utilizada

K: Determina la cantidad de vecinos para una instancia. Comenzamos evaluando con cantidades bajas y lo vamos aumentando hasta tener una cantidad mayor.

El peso: Este es el peso que se le da a las distancias entre los puntos. Analizamos con 'uniform' (Todas las distancias valen lo mismo), y con 'distance' (Pesa a los puntos con la inversa de la distancia, mientras mas cerca, mas valor tenes).

Metrica: Es la forma en que se va a calcular la distancia. Se eligieron varias de forma aleatoria de la lista que tiene sklearn.

Con el dataset sin expandir:

In [9]:
mejor_valor, mejor_k, mejor_distancia, mejor_metrica = obtenerMejoresHiperparametros(X_knn)

Observamos los valores obtenidos y procedemos a realizar el modelo definitivo de KNN con estos hiperparámetros.

In [10]:
print(mejor_valor.round(3))
print(mejor_k)
print(mejor_distancia)
print(mejor_metrica)
0.834
20
uniform
correlation

Con el data set expandido:

Ahora buscamos ver qué podemos obener aplicando otro preprocesado en este modelo. Para ello probamos con el preprocesado que expande el dataset categorizando valores numéricos y agrupando otros en clusters.

In [11]:
X_expandido = expansionDelDataset(X)

Mostramos cómo es el dataframe resultante de la expansión y buscamos los mejores hiperparámetros en este caso. Si obtenemos un mejor resultado usaremos este preprocesamiento para el modelo final de KNN.

In [12]:
X_expandido.head()
Out[12]:
sufijo tipo_de_sala genero edad amigos parientes precio_ticket fila nombre_sede autocompletamos_edad 2_clusters 4_clusters 10_clusters cantidad_total_invitados total_pagado pago_categorizado edades_estratificadas categoria_invitados
0 Señor 4d hombre 73.0 0 0 1 No responde fiumark_quilmes False 1 1 4 0 1 Pago poco mayor Fue solo
1 Señora 4d mujer 35.0 1 1 2 No responde fiumark_quilmes False 0 2 0 2 6 Pago normal adulto Fue en grupo
2 Señor normal hombre 32.0 0 0 3 No responde fiumark_chacarita True 0 2 0 0 3 Pago normal adulto Fue solo
3 Señor 4d hombre 32.0 0 0 1 No responde fiumark_palermo True 0 2 0 0 1 Pago poco adulto Fue solo
4 Señorita 4d mujer 4.0 1 1 2 No responde fiumark_palermo False 0 3 3 2 6 Pago normal ninio Fue en grupo
In [13]:
columnas_codificables_extra = ['pago_categorizado','edades_estratificadas','categoria_invitados']
columnas_numericas_extra = ['2_clusters','4_clusters','10_clusters','cantidad_total_invitados','total_pagado']

X_knn_expandido = conversionAVariablesNormalizadas(X_expandido,columnas_codificables_extra,columnas_numericas_extra)
In [14]:
mejor_valor_exp, mejor_k_exp, mejor_distancia_exp, mejor_metrica_exp = obtenerMejoresHiperparametros(X_knn_expandido)
In [15]:
print(mejor_valor_exp.round(3))
print(mejor_k_exp)
print(mejor_distancia_exp)
print(mejor_metrica_exp)
0.805
8
uniform
correlation

Obtenemos un promedio peor para el mejor valor de AUC-ROC al utilizar todas las columnas nuevas generadas.

Con el data set expandido (subconjunto de columnas):

Probamos ahora construir un modelo de KNN que no utilice todas las nuevas columnas que tiene el dataset expandido sino sólo un subconjunto de ellas.

In [16]:
columnas_codificables_extra = ['edades_estratificadas','categoria_invitados']
columnas_numericas_extra = ['2_clusters','4_clusters','10_clusters']

X_knn_expandido = conversionAVariablesNormalizadas(X_expandido,columnas_codificables_extra,columnas_numericas_extra)
In [17]:
mejor_valor_exp, mejor_k_exp, mejor_distancia_exp, mejor_metrica_exp = obtenerMejoresHiperparametros(X_knn_expandido)
In [18]:
print(mejor_valor_exp.round(3))
print(mejor_k_exp)
print(mejor_distancia_exp)
print(mejor_metrica_exp)
0.823
6
uniform
correlation

Vemos que mejoró levemente pero aún así no supero el preprocesamiento sin la información extra.

Modelo definitivo de KNN

Entrenamos ahora un modelo definitivo con los primeros hiperparámetros que se encontraron al utilizar el dataset sin expandir para entrenar al modelo.

In [19]:
knn = KNeighborsClassifier(n_neighbors = mejor_k, weights = mejor_distancia, metric = mejor_metrica)
In [20]:
X_train, X_test, y_train, y_test = train_test_split(X_knn, y, test_size=0.25, random_state=0)
In [21]:
knn.fit(X_train, y_train)
Out[21]:
KNeighborsClassifier(metric='correlation', n_neighbors=20)

Evaluación de métricas

Relizamos la predicción sobre los datos de evaluación, y evaluamos las métricas del modelo.

In [22]:
y_pred = knn.predict(X_test)
In [23]:
print(classification_report(y_test, y_pred, target_names=['No vuelve','Vuelve']))
              precision    recall  f1-score   support

   No vuelve       0.78      0.85      0.81       121
      Vuelve       0.74      0.64      0.68        80

    accuracy                           0.77       201
   macro avg       0.76      0.74      0.75       201
weighted avg       0.76      0.77      0.76       201

Matriz de confusión

In [24]:
mostrarMatrizDeConfusion(y_pred,y_test)

Curva ROC

Graficamos ahora la curva ROC.

In [25]:
mostrarROCCurve(knn,"KNN",X_test, X_train, y_test, y_train)
In [26]:
mostrarAUCScore(knn,"KNN",X_test,y_test)
AUC para KNN: 0.811

Podemos observar que KNN tuvo un rendimiento bueno considerando que es un método relativamente sencillo.

Predicciones sobre el nuevo archivo

Obtenemos y preparamos el nuevo archivo realizando el mismo preprocesamiento realizado anteriormente.

In [27]:
holdout = obtenerHoldout()
ids_usuarios = np.array(holdout['id_usuario'])
holdout = prepararSetDeHoldout(holdout)
holdout_knn = conversionAVariablesNormalizadas(holdout)

Finalmente, realizamos las predicciones y escribimos al archivo en el formato CSV pedido.

In [28]:
predicciones_holdout = knn.predict(holdout_knn)
In [29]:
escribirPrediccionesAArchivo(predicciones_holdout,"KNN",ids_usuarios)