<jupyter_start><jupyter_text>**KNN Flags**<jupyter_code>import pandas as pd
url = (
"https://archive.ics.uci.edu/ml/machine-learning-databases/flags/flag.data"
)
flags = pd.read_csv(url, header=None)

flags.head()<jupyter_output><empty_output><jupyter_text>Se utiliza el dataset de banderas e imprimimos las primeras 5 lineas del data set Le asignamos nombres a las columnas del data set para saber que reprensenta cada una de ellas<jupyter_code>flags.columns = [
    "name",
    "landmass",
    "zone",
    "area",
    "population",
    "language",
    "religion",
    "bars",
    "stripes",
    "colours",
    "red",
    "green",
    "blue",
    "gold",
    "white",
    "black",
    "orange",
    "mainhue",
    "circles",
    "crosses",
    "saltires",
    "quarters",
    "sunstars",
    "crescent",
    "triangle",
    "icon",
    "animate",
    "text",
    "topleft",
    "botright",
]

flags.head()<jupyter_output><empty_output><jupyter_text>Como vamos a tratar de predecir la religion de un pais en base a su tamaño y colores en su bandera, vamos a dropear columnas que no necesitamos<jupyter_code>flags = flags.drop("name", axis=1)
flags = flags.drop("landmass", axis=1)
flags = flags.drop("zone", axis=1)
flags = flags.drop("area", axis=1)
flags = flags.drop("population", axis=1)
flags = flags.drop("language", axis=1)
flags = flags.drop("mainhue", axis=1)
flags = flags.drop("topleft", axis=1)
flags = flags.drop("botright", axis=1)

flags.head()<jupyter_output><empty_output><jupyter_text>Ya que la variable de interes aqui es la religion, se genera un histograma para darnos una idea de los rangos entre cada una de ellas.0=Catholic, 1=Christian, 2=Muslim, 3=Buddhist, 4=Hindu, 5=Ethnic, 6=Marxist, 7=Others<jupyter_code>import matplotlib.pyplot as plt
flags["religion"].hist(bins=15)
plt.show()<jupyter_output><empty_output><jupyter_text>El histograma muestra que la maoyria de los paises/banderas son cristianos, catolicos y musulmanes.Ahora tenemos que ver que variables tienen una fuerte correlacion con nuestra variable de interes<jupyter_code>correlation_matrix =  flags.corr()
correlation_matrix["religion"]<jupyter_output><empty_output><jupyter_text>**KNN FROM SCRATCH**Ahora tenemos que definir el "mas cercano" calculando la distancia euclediana<jupyter_code>import numpy as np
a = np.array([2,2])
b = np.array([4,4])
np.linalg.norm(a-b)<jupyter_output><empty_output><jupyter_text>Para encontrar los vecinos mas cercanos con NumPy, necesitamos definir las distancias de los vectores de las variables independientes, por lo que vamos a pasar nuestros dataframes d epanda a arrays de NumPy<jupyter_code>X = flags.drop("religion", axis=1)
X = X.values
y = flags["religion"]
y = y.values
print(X)
print()
print(y)<jupyter_output>[[0 3 5 ... 1 0 0]
 [0 0 3 ... 0 1 0]
 [2 0 3 ... 0 0 0]
 ...
 [0 0 4 ... 1 1 0]
 [3 0 4 ... 0 1 0]
 [0 7 5 ... 1 1 0]]

[2 6 2 1 0 5 1 1 0 0 1 0 1 2 2 1 0 1 5 1 3 0 5 0 1 2 6 5 3 5 1 1 0 1 5 5 0
 6 0 2 5 1 0 6 1 6 1 2 1 0 0 2 0 5 1 1 1 1 1 0 0 0 5 5 6 1 5 1 1 1 1 1 0 2
 5 4 0 0 3 6 1 4 2 2 2 0 7 0 5 1 7 2 3 5 1 2 6 2 5 5 2 0 0 1 5 2 2 2 0 1 2
 4 0 1 0 6 1 2 5 1 4 1 1 1 0 2 2 1 6 2 1 2 2 0 5 0 0 0 6 0 0 2 6 5 0 0 2 2
 1 5 3 1 2 1 7 2 0 3 1 1 1 1 2 1 1 1 1 2 3 5 3 7 1 1 2 2 1 1 2 5 1 0 1 1 6
 1 0 0 6 1 6 5 5 5]<jupyter_text>Ahora tenemos 2 objetos que contienen nuestra data.X es la matriz de variables independientes.y es la matriz de la variable independiente. Vamos a aplicar KNN con K=3 Por lo que se crea un nuevo array de NumPy para un dataPoint con los siguientes datos:*3,0,4,1,1,0,0,1,0,1,0,0,0,0,0,0,0,0,1,0*<jupyter_code>new_data_point= np.array([
3,0,4,1,1,0,0,1,0,1,0,0,0,0,0,0,0,0,1,0,
])<jupyter_output><empty_output><jupyter_text>Calculamos las distancias entre el nuevo data point y cada uno de los data opint del dataset de banderas<jupyter_code>distances = np.linalg.norm(X - new_data_point, axis=1)<jupyter_output><empty_output><jupyter_text>Ahora tenemos un vector de distancias, y tenemos que encontrar cuales son nuestros K=3 vecinos mas cercanos. Para hacer esto, necesitamos encontrar los IDs de las distancias minimas. Utilizamos el metodo *argsort()* para ordenar el array de menor a mayor, y tomar los primeros k = 3 elementos para obtener los indices de los K mas cercanos.<jupyter_code>k = 3
nearest_neighbor_ids = distances.argsort()[:k]
nearest_neighbor_ids<jupyter_output><empty_output><jupyter_text>Esto nos dice cuales son los 3 vecinos mas cercanos a nuestro nuevo data point.Ahora necesitamos combinar estos 3 vecinos en una prediccion para nuesto nuvo data point Primero tenemos que encontrar en que clases se clasifican estos 3 vecinos<jupyter_code>nearest_neighbor_flags = y[nearest_neighbor_ids]
nearest_neighbor_flags<jupyter_output><empty_output><jupyter_text>**Donde 0 = Catholic y 5 = Ethnic** Ahora que tenemos los valores de las clases de los 3 vecinos, los combinaremos en una sola prediccion, **una para regresion y otra para clasificacion**  **Promedio para regresion**En los problemas de regresion, la variable objetivo debe ser un valor numerico, se combinan varios vecinos en una sola prediccion tomando el promedio de sus valores.<jupyter_code>prediction = nearest_neighbor_flags.mean()
prediction<jupyter_output><empty_output><jupyter_text>La prediccion nos regresa un valor de 3.33...Esto significa que la prediccion para los 3 vecinos mas cercanos para el nuevo datapoint es de **3**  **Moda para la clasificacion**En los problemas de clasificacion, la variable objetivo es categorica, en la regresion se utilizaba el promedio para la prediccion, aqui utilizamos la moda.Podemos calcular la moda utilizando la funcion *mode()* de SciPy<jupyter_code>import scipy.stats
class_neighbors = nearest_neighbor_flags
scipy.stats.mode(class_neighbors)<jupyter_output><empty_output><jupyter_text>En base a la moda, la clase que se repite mas veces es la 5, por lo tanto la prediccion nos dice que nuestro nuevo datapoint pertence a la **clase 5 = Ethnic**    **Entrenar KNN en python utilizando scikit-learn** Ahora implementaremos KNN con la libreria de scikit-learn, una de las mas usadas en el area de machine learning.Se evaluara la calidad del modelo de KNN que hicimos previamente, para hacer la evaluacion se utilizara *train-split*.Esto divide nuestro dataset en 2 partes:-**Data para entrenamiento:** para KNN, significa que la data sera utilizada como los vecinos.-**Data para pruebas:** esta es utilizada para evaluar el modelo.Podemos divir la data en estos 2 sets utilizando la funcion *train_test_split()* de scikit-learn<jupyter_code>from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=12345)<jupyter_output><empty_output><jupyter_text>El *test_size* se reifere al porcentaje del dataset que quieres usar para entrenar y probarComo esta en 0.2, entonces nuestro porcentaje del dataset para probar sera del 20%, y el 80% restante sera para entrenar.El *random_state* es un parametro que permite obtener el mismo resultado cada vez que el codigo es ejecutado.  **Entrenar KNN regresion con scikit-learn**Para entrenar el modelo de scikit-learn, se cream un modelo de las clases correctas, tambien necesitamos elegir el valor para k.<jupyter_code>from sklearn.neighbors import KNeighborsRegressor
knn_model = KNeighborsRegressor(n_neighbors=3)<jupyter_output><empty_output><jupyter_text>Tenemos nuestro modelo sin entrenar, vamos a entrenar el modelo con el porcentaje del data set que seleccionamos para entrenar. Este modelo utilizara los k=3 vecinos mas cercanos para predecir el valor de algun futuro data point nuevo.<jupyter_code>knn_model.fit(X_train, y_train)<jupyter_output><empty_output><jupyter_text>Utilizando la funcion *fit()* permite al modelo "aprender" de la data.  Ahora el modelo *knn_model* tiene todo lo necesario para hacer predicciones  **Usando Scikit-learn para inspeccionar el entrenamiento del modelo**Funciones para evaluar nuestro modelo, hay muchas metricas para evaular la regresion, una de las mas utilizadas es *root mean square error (RMSE)* El RMSE de una prediccion evalua lo siguiente:1.Calcula la diferencia entre cada valor del data point y el valor predecido.2.A cada diferencia se le saca la raiz cuadrada.3.Se suman todas la raices.4.Se calcula la raiz de la suma de todas las raices. Se evaluara el margen de error en la prediccion en la data de entrenamiento.<jupyter_code>from sklearn.metrics import mean_squared_error 
from math import sqrt
train_preds = knn_model.predict(X_train)
mse = mean_squared_error(y_train, train_preds)
rmse = sqrt(mse)
rmse<jupyter_output><empty_output><jupyter_text>El RMSE calculado de nuestro modelo *knn_model* fue sobre la data de entrenamiento, ahora sera sobre la data de prueba.<jupyter_code>test_preds = knn_model.predict(X_test)
mse = mean_squared_error(y_test, test_preds)
rmse = sqrt(mse)
rmse<jupyter_output><empty_output><jupyter_text>**Graficando el entrenamiento del modelo**<jupyter_code>import seaborn as sns
cmap = sns.cubehelix_palette(as_cmap=True)
f, ax = plt.subplots()
points = ax.scatter(X_test[:, 0], X_test[:, 1], c=test_preds, s=50, cmap=cmap)
f.colorbar(points)
plt.show()<jupyter_output><empty_output><jupyter_text>**Graficando la prueba del modelo**<jupyter_code>cmap = sns.cubehelix_palette(as_cmap=True)
f, ax = plt.subplots()
points = ax.scatter(X_test[:, 0], X_test[:, 1], c=y_test, s=50, cmap=cmap)
f.colorbar(points)
plt.show()<jupyter_output><empty_output><jupyter_text>**Mejorando KNN en python con scikit-learn con GridSearchCV**Para optimizarlo se utiliza *GridSearchCV*, es una herramienta que se utiliza frecuentemente para mejorar los hyper parametros de los modelos de machine learning. En nuestro caso, nos ayudara a encontrar el mejor valor para K en nuestro dataset.<jupyter_code>from sklearn.model_selection import GridSearchCV
parameters = {"n_neighbors": range(1, 50)}
gridsearch = GridSearchCV(KNeighborsRegressor(), parameters)
gridsearch.fit(X_train, y_train)<jupyter_output><empty_output><jupyter_text>Utilizamos GridSearchCV para entrenar de manera repeitda el modelo, el hacerlo de manera repetida da un estaimado confiable para cada uno de los valores de K, en esta caso de prueban valores de 1 a 50.<jupyter_code>gridsearch.best_params_<jupyter_output><empty_output><jupyter_text>Al final, este mantendra el valor que tuvo el mejor K de rendimiento para predicciones, en nuestra caso fue **K=14**  Ahora que sabemos cual es el mejor valor para nuestra K, podemos ver como afecta esto nuesto entrenamiento y pruebas<jupyter_code>train_preds_grid = gridsearch.predict(X_train)
train_mse = mean_squared_error(y_train, train_preds_grid)
train_rmse = sqrt(train_mse)
test_preds_grid = gridsearch.predict(X_test)
test_mse = mean_squared_error(y_test, test_preds_grid)
test_rmse = sqrt(test_mse)

print("Error Entrenamiento ")
print(train_rmse)
print("Error Pruebas ")
print(test_rmse)<jupyter_output>Error Entrenamiento 
1.9690913147087974
Error Pruebas 
1.6700645635000175<jupyter_text>**Agregando "Weighted average" de los vecinos en base a su distancia**Veremos si el rendimiento del modelo mejorara cuando se crea un prediccion utilizando un "Weighted average" en vez del promedio normal. Esto significa que estan mas lejos influenciaran menos en el resultado de la prediccion.<jupyter_code>parameters = {"n_neighbors": range(1, 50),"weights": ["uniform", "distance"],}
gridsearch = GridSearchCV(KNeighborsRegressor(), parameters)
gridsearch.fit(X_train, y_train)

gridsearch.best_params_

test_preds_grid = gridsearch.predict(X_test)
test_mse = mean_squared_error(y_test, test_preds_grid)
test_rmse = sqrt(test_mse)
test_rmse<jupyter_output><empty_output><jupyter_text>Podemos ver que no hubo una mejora con este dataset, de 1.6700645635000175 con el promedio normal a 1.6700645635000175 utilizando un "weighted average"  **Mejorando aun mas KNN con scikit-learn con Bagging**Bagging es un metodo que toma un modelo de machine learning y entrena un gran numero de esos modelos con una ligera variacion en cada entremaiento.Con scikit-learn creamos un *KNeighborsRegressor* que contiene el mejor valor para K y el "peso" que se calculo con *GridSearchCV*<jupyter_code>best_k = gridsearch.best_params_["n_neighbors"]
best_weights = gridsearch.best_params_["weights"]
bagged_knn = KNeighborsRegressor(n_neighbors=best_k, weights=best_weights)<jupyter_output><empty_output><jupyter_text>Importamos la clase BaggingRegressor y creamos una nueva instanca con 100 estimadores utilizando el modelo *bagged_knn*<jupyter_code>from sklearn.ensemble import BaggingRegressor
bagging_model = BaggingRegressor(bagged_knn, n_estimators=100)
bagging_model.fit(X_train, y_train)<jupyter_output><empty_output><jupyter_text>Ahora podemos hacer una prediccion y calcular el RMSE<jupyter_code>test_preds_grid = bagging_model.predict(X_test)
test_mse = mean_squared_error(y_test, test_preds_grid)
test_rmse = sqrt(test_mse)
test_rmse<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08409774146303862, 'fraction_numerical': 0.03299159793417097, 'mean_word_length': 5.722279792746114, 'pattern_counts': {'":': 3, '<': 107, '<?xml version=': 0, '>': 107, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 31, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '27', 'dir': 'jupyter-structured-clean-dedup', 'id': '573618', 'license': 'MIT', 'n_tokens_mistral': 4999, 'n_tokens_neox': 4632, 'n_words': 1543, 'path': 'KNNFlags/KNNFlags.ipynb', 'repo_name': 'AngelArturo13/IA-6-7', 'size': '12973'}