<jupyter_start><jupyter_text>Tarea N°02 Instrucciones1.- Completa tus datos personales (nombre y rol USM) en siguiente celda.**Nombre**: <NAME>**Rol**: 201673563-12.- Debes pushear este archivo con tus cambios a tu repositorio personal del curso, incluyendo datos, imágenes, scripts, etc.3.- Se evaluará:- Soluciones- Código- Que Binder esté bien configurado.- Al presionar  `Kernel -> Restart Kernel and Run All Cells` deben ejecutarse todas las celdas sin error.  I.- Clasificación de dígitosEn este laboratorio realizaremos el trabajo de reconocer un dígito a partir de una imagen. ![rgb](https://www.wolfram.com/language/11/neural-networks/assets.en/digit-classification/smallthumb_1.png) El objetivo es a partir de los datos, hacer la mejor predicción de cada imagen. Para ellos es necesario realizar los pasos clásicos de un proyecto de _Machine Learning_, como estadística descriptiva, visualización y preprocesamiento. * Se solicita ajustar al menos tres modelos de clasificación: * Regresión logística * K-Nearest Neighbours  * Uno o más algoritmos a su elección [link](https://scikit-learn.org/stable/supervised_learning.htmlsupervised-learning) (es obligación escoger un _estimator_ que tenga por lo menos un hiperparámetro).   * En los modelos que posean hiperparámetros es mandatorio buscar el/los mejores con alguna técnica disponible en `scikit-learn` ([ver más](https://scikit-learn.org/stable/modules/grid_search.htmltuning-the-hyper-parameters-of-an-estimator)).* Para cada modelo, se debe realizar _Cross Validation_ con 10 _folds_ utilizando los datos de entrenamiento con tal de determinar un intervalo de confianza para el _score_ del modelo.* Realizar una predicción con cada uno de los tres modelos con los datos _test_ y obtener el _score_. * Analizar sus métricas de error (**accuracy**, **precision**, **recall**, **f-score**)  Exploración de los datosA continuación se carga el conjunto de datos a utilizar, a través del sub-módulo `datasets` de `sklearn`.<jupyter_code>import numpy as np
import pandas as pd
from sklearn import datasets
import matplotlib.pyplot as plt
import seaborn as sns

%matplotlib inline
digits_dict = datasets.load_digits()
print(digits_dict["DESCR"])
digits_dict.keys()
digits_dict["target"]<jupyter_output><empty_output><jupyter_text>A continuación se crea dataframe declarado como `digits` con los datos de `digits_dict` tal que tenga 65 columnas, las 6 primeras a la representación de la imagen en escala de grises (0-blanco, 255-negro) y la última correspondiente al dígito (`target`) con el nombre _target_.<jupyter_code>digits = (
    pd.DataFrame(
        digits_dict["data"],
    )
    .rename(columns=lambda x: f"c{x:02d}")
    .assign(target=digits_dict["target"])
    .astype(int)
)

digits.head()<jupyter_output><empty_output><jupyter_text>Ejercicio 1**Análisis exploratorio:** Realiza tu análisis exploratorio, no debes olvidar nada! Recuerda, cada análisis debe responder una pregunta.Algunas sugerencias:* ¿Cómo se distribuyen los datos?* ¿Cuánta memoria estoy utilizando?* ¿Qué tipo de datos son?* ¿Cuántos registros por clase hay?* ¿Hay registros que no se correspondan con tu conocimiento previo de los datos? Primero se verá de que tipo de datos son:<jupyter_code>digits.dtypes
if True not in digits.isnull().any().values:
    print('No hay datos nulos en ninguna columna')<jupyter_output>No hay datos nulos en ninguna columna<jupyter_text>Ahora, se verán los valores que hay en cada columna en el siguiente Dataframe, por ejemplo, en la columna c00 solo se encuentra el valor 0, y en c01 esta el 0, 2, 5, 1, 6, 3, 4, 7 y 8, mientras que los valores NaN son para dimensionar de forma correcta el dataframe.<jupyter_code>#Lista con los valores unicos en cada columna
list_val_cols = [list(digits[column].unique()) for column in digits.columns]
dict_df = {}

#Maximo largo de un arreglo de valores en la lista
max_len = max([len(i) for i in list_val_cols])

#Se dejan todos los arreglos en la lista con el mismo largo y se agrega a un dict
for i in range(0, len(digits.columns)):
    if len(list_val_cols[i]) != max_len:
        for j in range(len(list_val_cols[i]), max_len):
            list_val_cols[i].append(None)
    dict_df[digits.columns[i]] = list_val_cols[i]

#Con el dict se forma el DataFrame
df = pd.DataFrame(dict_df)
df<jupyter_output><empty_output><jupyter_text>Ahora, se verá la cantidad de datos que se tiene para cada valor en `target`:<jupyter_code>for i in range(0, len(digits.target.value_counts())):
    print('El valor en target', digits.target.value_counts().index[i] 
          ,'tiene la siguiente frecuencia:', digits.target.value_counts().values[i])<jupyter_output>El valor en target 3 tiene la siguiente frecuencia: 183
El valor en target 5 tiene la siguiente frecuencia: 182
El valor en target 1 tiene la siguiente frecuencia: 182
El valor en target 6 tiene la siguiente frecuencia: 181
El valor en target 4 tiene la siguiente frecuencia: 181
El valor en target 9 tiene la siguiente frecuencia: 180
El valor en target 7 tiene la siguiente frecuencia: 179
El valor en target 0 tiene la siguiente frecuencia: 178
El valor en target 2 tiene la siguiente frecuencia: 177
El valor en target 8 tiene la siguiente frecuencia: 174<jupyter_text>Con esto se puede ver que el que tiene mayor frecuencia es 3 y menor frecuencia es 8. Esto se puede ver, a través del siguiente histograma:<jupyter_code>def annotate(**kwargs):
    g=plt.gca()
    for p in g.patches:
        g.annotate(format(p.get_height(), ',.1f'),
                   ((p.get_x() + p.get_width() / 2.) ,p.get_height()),
                   ha = 'center', va = 'center', xytext = (0, 10),
                   textcoords = 'offset points')
g = sns.catplot(x = 'target',
                data = digits, 
                kind = 'count',
                height = 10,
                aspect = 3)
g.map(annotate)
sns.set_context('talk')
print('La media de los valores en target es:',
        round(sum(digits.target.value_counts().index[i] * digits.target.value_counts().values[i]
              for i in range(0, len(digits.target.value_counts()))) / sum(digits.target.value_counts()),2))<jupyter_output>La media de los valores en target es: 4.49<jupyter_text>Ejercicio 2**Visualización:** Para visualizar los datos utilizaremos el método `imshow` de `matplotlib`. Resulta necesario convertir el arreglo desde las dimensiones (1,64)  a (8,8) para que la imagen sea cuadrada y pueda distinguirse el dígito. Superpondremos además el label correspondiente al dígito, mediante el método `text`. Esto nos permitirá comparar la imagen generada con la etiqueta asociada a los valores. Realizaremos lo anterior para los primeros 25 datos del archivo.<jupyter_code>digits_dict["images"][0]<jupyter_output><empty_output><jupyter_text>Visualiza imágenes de los dígitos utilizando la llave `images` de `digits_dict`. Sugerencia: Utiliza `plt.subplots` y el método `imshow`. Puedes hacer una grilla de varias imágenes al mismo tiempo!<jupyter_code>fig, ax = plt.subplots(1, 1, figsize=(5,5))

#Se excribe superpuesto el numero, en el numero a reconocer con imshow
ax.text(3 , 4.3 , str(0), fontsize = 70, color = 'b')
ax.imshow(digits_dict["images"][0], cmap = 'gray' )
nx, ny = 5, 5
fig, axs = plt.subplots(nx, ny, figsize=(12, 12))

#Print los subplots y superpuesto el numero
for i in range(0, nx):
    for j in range(0, ny):
        axs[i, j].text(1.5 , 4.8 , str((5 * i + j) % 10 ), fontsize = 70, color = 'b')
        axs[i, j].imshow(digits_dict["images"][5 * i + j], cmap = 'gray' )<jupyter_output><empty_output><jupyter_text>Ejercicio 3**Machine Learning**: En esta parte usted debe entrenar los distintos modelos escogidos desde la librería de `sklearn`. Para cada modelo, debe realizar los siguientes pasos:* **train-test**  * Crear conjunto de entrenamiento y testeo (usted determine las proporciones adecuadas). * Imprimir por pantalla el largo del conjunto de entrenamiento y de testeo.  * **modelo**: * Instanciar el modelo objetivo desde la librería sklearn. * *Hiper-parámetros*: Utiliza `sklearn.model_selection.GridSearchCV` para obtener la mejor estimación de los parámetros del modelo objetivo.* **Métricas**: * Graficar matriz de confusión. * Analizar métricas de error.__Preguntas a responder:__* ¿Cuál modelo es mejor basado en sus métricas?* ¿Cuál modelo demora menos tiempo en ajustarse?* ¿Qué modelo escoges?<jupyter_code>X = digits.drop(columns = "target").values
y = digits["target"].values
X
y<jupyter_output><empty_output><jupyter_text>Se toma el 15% de los datos, como datos de test.<jupyter_code>from sklearn.model_selection import train_test_split

#Conjunto de train y test
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.18, random_state = 42)
print('Los datos de train en total son:', len(X_train))
print('Los datos de test en total son:',len(X_test))
#Regresion Logistica
from metrics_classification import *
from sklearn.metrics import r2_score
from sklearn.linear_model import LogisticRegression

#KNeighbors
from sklearn.neighbors import KNeighborsClassifier
from sklearn import neighbors
from sklearn import preprocessing

#Tree Classifier
from sklearn.tree import DecisionTreeClassifier

#Matriz de Confusion
from sklearn.metrics import confusion_matrix<jupyter_output><empty_output><jupyter_text>Regresion logistica<jupyter_code>#Iniciar modelo
reg_log = LogisticRegression(max_iter = 5000)

#Entrenar
reg_log.fit(X_train, y_train)
#Metricas
metrics_reg_log = summary_metrics(pd.DataFrame({'y': y_test, 'yhat': reg_log.predict(X_test)}))
metrics_reg_log
#Matriz de Confusion
matrix_conf = confusion_matrix(list(y_test),list(reg_log.predict(X_test)))
matrix_conf
print('Se acertó en:',sum(y_test == reg_log.predict(X_test)) ,'datos.')
def count_fp_fn(matrix):
    
    #Lista de la suma de valores en la parte triangular superior
    list_false_positive = [sum(matrix_conf.T[i][i + 1:]) for i in range(0,matrix_conf.shape[0] - 1) 
                                                       if sum(matrix_conf.T[i][i + 1:]) != 0]
    
    #Lista de la suma de valores en la parte triangular inferior
    list_false_negative = [sum(matrix_conf[i][i + 1:]) for i in range(0,matrix_conf.shape[0] - 1) 
                                                       if sum(matrix_conf[i][i + 1:]) != 0]
    count_fp = sum(list_false_positive)
    count_fn = sum(list_false_negative)
    return (count_fp, count_fn)
#Contaremos los falsos positivos y los falsos negativos
falses_pos_neg = count_fp_fn(matrix_conf)

print('Se tiene que los falsos positivos en este caso son:', falses_pos_neg[0])
print('Se tiene que los falsos negativos en este caso son:', falses_pos_neg[1])<jupyter_output>Se tiene que los falsos positivos en este caso son: 6
Se tiene que los falsos negativos en este caso son: 2<jupyter_text>K NearestNeighbors<jupyter_code>#Iniciar modelo
KNN = neighbors.KNeighborsClassifier()

#Entrenar
KNN.fit(X_train, y_train)
#Metricas
metrics_KNN = summary_metrics(pd.DataFrame({'y':y_test, 'yhat':KNN.predict(X_test)}))
metrics_KNN
#Matriz de Confusion
matrix_conf = confusion_matrix(list(y_test), list(KNN.predict(X_test)))
matrix_conf
print('Se acertó en:',sum(y_test == KNN.predict(X_test)) ,'datos.')
#Contaremos los falsos positivos y los falsos negativos
falses_pos_neg = count_fp_fn(matrix_conf)

print('Se tiene que los falsos positivos en este caso son:', falses_pos_neg[0])
print('Se tiene que los falsos negativos en este caso son:', falses_pos_neg[1])<jupyter_output>Se tiene que los falsos positivos en este caso son: 2
Se tiene que los falsos negativos en este caso son: 2<jupyter_text>Arbol de Decision<jupyter_code>#Iniciar modelo
tree_dec = DecisionTreeClassifier(max_depth = 10)

#Entrenar
tree_dec.fit(X_train, y_train)
#Metricas
metrics_tree_dec = summary_metrics(pd.DataFrame({'y': y_test, 'yhat': tree_dec.predict(X_test)}))
metrics_tree_dec
#Matriz de Confusion
matrix_conf = confusion_matrix(list(y_test),list(tree_dec.predict(X_test)))
matrix_conf
print('Se acertó en:',sum(y_test == tree_dec.predict(X_test)) ,'datos.')
#Contaremos los falsos positivos y los falsos negativos
falses_pos_neg = count_fp_fn(matrix_conf)

print('Se tiene que los falsos positivos en este caso son:', falses_pos_neg[0])
print('Se tiene que los falsos negativos en este caso son:', falses_pos_neg[1])
metrics_reg_log.loc[1] = metrics_KNN.loc[0]
metrics_reg_log.loc[2] = metrics_tree_dec.loc[0]
metrics_reg_log['model'] = ['reg_log', 'knn', 'tree_dec']
metrics = metrics_reg_log.set_index('model')
metrics
metrics.loc[metrics['fscore'] == max(metrics['fscore'])]<jupyter_output><empty_output><jupyter_text>Luego, se puede ver que todos los modelos son bastante acertados, pero el que sobre sale es el `knn`, el cual no solo tiene el mejor `fscore`, sino que tiene los mejores valores en todas las metricas.  Gridsearch<jupyter_code>from sklearn.model_selection import GridSearchCV

#
model = DecisionTreeClassifier()

# rango de parametros
rango_criterion = ['gini','entropy']
rango_max_depth = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 20, 30, 40, 50, 70, 90, 120, 150])
param_grid = dict(criterion = rango_criterion, max_depth = rango_max_depth)
param_grid
gs = GridSearchCV(estimator = model, 
                  param_grid = param_grid,
                  scoring = 'accuracy',
                  cv = 10,
                  n_jobs = -1)

gs = gs.fit(X_train, y_train)
# imprimir resultados
gs.best_score_
gs.best_params_<jupyter_output><empty_output><jupyter_text>Ejercicio 4__Comprensión del modelo:__ Tomando en cuenta el mejor modelo entontrado en el `Ejercicio 3`, debe comprender e interpretar minuciosamente los resultados y gráficos asociados al modelo en estudio, para ello debe resolver los siguientes puntos: * **Cross validation**: usando **cv** (con n_fold = 10), sacar una especie de "intervalo de confianza" sobre alguna de las métricas estudiadas en clases:   * $\mu \pm \sigma$ = promedio $\pm$ desviación estandar * **Curva de Validación**: Replica el ejemplo del siguiente [link](https://scikit-learn.org/stable/auto_examples/model_selection/plot_validation_curve.htmlsphx-glr-auto-examples-model-selection-plot-validation-curve-py) pero con el modelo, parámetros y métrica adecuada. Saque conclusiones del gráfico. * **Curva AUC–ROC**: Replica el ejemplo del siguiente  [link](https://scikit-learn.org/stable/auto_examples/model_selection/plot_roc.htmlsphx-glr-auto-examples-model-selection-plot-roc-py) pero con el modelo, parámetros y métrica adecuada. Saque conclusiones del gráfico.<jupyter_code>#Cross validation

from sklearn.model_selection import cross_val_score

#Validation curve
from sklearn.model_selection import validation_curve

# AUC-ROC curve
from sklearn.metrics import roc_curve, auc
#Modelo
model = KNeighborsClassifier()
#Cross Validation
precision = cross_val_score(estimator = model, 
                            X = X_train,
                            y = y_train,
                            cv = 10)
#Media y Desviacion estandar
media_model = precision.mean()
std_model = precision.std()
print('El intervalo de confianza es: (', (media_model - std_model).round(4),
     ', ',(media_model + std_model).round(4), ').')
KNN.get_params()
parameters = np.arange(1,10)
train_scores, test_scores = validation_curve(model,
                                             X_train,
                                             y_train,
                                             param_name = 'n_neighbors',
                                             param_range = parameters,
                                             scoring = 'accuracy',
                                             n_jobs = -1)
train_scores_mean = np.mean(train_scores, axis = 1)
train_scores_std = np.std(train_scores, axis = 1)
test_scores_mean = np.mean(test_scores, axis = 1)
test_scores_std = np.std(test_scores, axis = 1)

plt.figure(figsize=(20,8))
plt.title('Validation Curve (KNeighbors)')
plt.xlabel('n_neighbors')
plt.ylabel('scores')

#Train
plt.semilogx(parameters,
             train_scores_mean,
             label = 'Training Score',
             color = 'red',
             lw =2)
plt.fill_between(parameters,
                 train_scores_mean - train_scores_std,
                 train_scores_mean + train_scores_std,
                 alpha = 0.2,
                 color = 'red',
                 lw = 2)

#Test
plt.semilogx(parameters,
             test_scores_mean,
             label = 'Cross Validation Score',
             color = 'navy',
             lw =2)
plt.fill_between(parameters,
                 test_scores_mean - test_scores_std,
                 test_scores_mean + test_scores_std,
                 alpha = 0.2,
                 color = 'navy',
                 lw = 2)

plt.legend(loc = 'Best')
plt.show()<jupyter_output>/home/pablo/anaconda3/envs/mat281/lib/python3.7/site-packages/ipykernel_launcher.py:45: MatplotlibDeprecationWarning: Unrecognized location 'Best'. Falling back on 'best'; valid locations are
	best
	upper right
	upper left
	lower left
	lower right
	right
	center left
	center right
	lower center
	upper center
	center
This will raise an exception in 3.3.<jupyter_text>Ejercicio 5__Reducción de la dimensión:__ Tomando en cuenta el mejor modelo encontrado en el `Ejercicio 3`, debe realizar una reducción de dimensionalidad del conjunto de datos. Para ello debe abordar el problema ocupando los dos criterios visto en clases:  * **Selección de atributos*** **Extracción de atributos**__Preguntas a responder:__Una vez realizado la reducción de dimensionalidad, debe sacar algunas estadísticas y gráficas comparativas entre el conjunto de datos original y el nuevo conjunto de datos (tamaño del dataset, tiempo de ejecución del modelo, etc.)<jupyter_code>#Se escogera por medio de prueba chi cuadrado

from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
data_X = pd.DataFrame(X)
data_X.columns = [f'c{k}' for k in range(0,X.shape[1])]
data_X['target']=y
data_X.head()
#Conjuntos de entrenamiento

X_train = data_X.drop(['target',], axis=1)
y_train = data_X['target']
# Aplicando prueba de Chi2

cols = list(X_train.columns.values)
select = SelectKBest(chi2, k = 45).fit(X_train, y_train) #Se seleccionan 45 elementos
for selects in list(select.get_support().nonzero()[0]):
    print(f'c{selects}')
data_X = data_X[[cols[i] for i in list(select.get_support().nonzero()[0])]]
data_X.shape<jupyter_output><empty_output><jupyter_text>Con esto ya está reducida la dimensión del data.<jupyter_code>from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
transform = StandardScaler().fit_transform(data_X)

#Dataframe normalizado
df_normalizado = pd.DataFrame(transform, columns = data_X.columns)
df_normalizado.head()
fig = plt.figure(figsize = (25,25))
sns.set(font_scale=0.7)
mask = np.triu(np.ones_like(df_normalizado.corr(), dtype = bool))

sns.heatmap(df_normalizado.corr().round(2), #Redondeo
            mask = mask,
            annot = True, fmt = 'g',        #Valores
            cmap = 'Blues')                 #Color
plt.show()
#PCA
pca = PCA(data_X.shape[1])
principalComponents = pca.fit_transform(transform)

varianzas = np.round(pca.explained_variance_ratio_ * 100, decimals = 2)
columns = df_normalizado.columns

plt.figure(figsize=(25,8))
plt.bar(x = range(0, len(columns)),
        height = varianzas,
        tick_label = columns)

plt.tick_params(labelsize = 13)
plt.ylabel('Percentate of Variance Explained', **{'size':'20'})
plt.xlabel('Principal Component', **{'size':'20'})
plt.title('PCA Scree Plot', **{'size': '25'})
plt.show()
varianzas_acum = np.cumsum(varianzas)
columns_acum = [columns[0]]
#Etiquetas PCA acumulados
for i in range(1, len(columns)):
    columns_acum.append(columns[0] + '+...+' + columns[i])

#Grafico
plt.figure(figsize=(25,25))
plt.bar(x = range(1, len(columns) + 1),          #Eje x
        height = varianzas_acum,  #Valores
        color = 'green',
        tick_label = columns_acum)     #Etiquetas eje x

#Etiquetas
plt.tick_params(labelsize = 13)
plt.xticks(range(len(columns_acum)), columns_acum, rotation = 30)
plt.ylabel('Percentate of Variance Explained', **{'size':'20'})
plt.xlabel('Principal Component', **{'size':'20'})
plt.title('PCA Scree Plot', **{'size': '25'})
plt.show()
#Probando nuevamente el modelo
X_train, X_test, y_train, y_test =  train_test_split(data_X, y_train, test_size=0.18, random_state=42)
KNN_reduc = neighbors.KNeighborsClassifier()
KNN_reduc.fit(X_train,y_train)
metrics_KNN_reduc = summary_metrics(pd.DataFrame({'y':y_test, 'yhat':KNN_reduc.predict(X_test)}))
metrics_KNN_reduc
matrix_conf = confusion_matrix(list(y_test), list(KNN_reduc.predict(X_test)))
matrix_conf
print('Se acertó en:',sum(y_test == KNN_reduc.predict(X_test)) ,'datos.')
#Contaremos los falsos positivos y los falsos negativos
falses_pos_neg = count_fp_fn(matrix_conf)

print('Se tiene que los falsos positivos en este caso son:', falses_pos_neg[0])
print('Se tiene que los falsos negativos en este caso son:', falses_pos_neg[1])<jupyter_output>Se tiene que los falsos positivos en este caso son: 2
Se tiene que los falsos negativos en este caso son: 2<jupyter_text>Luego, es fácil notar que se obtuvieron los mismos resultados que cuando se tenian todos los datos.  Ejercicio 6__Visualizando Resultados:__ A continuación se provee código para comparar las etiquetas predichas vs las etiquetas reales del conjunto de _test_.<jupyter_code>def mostar_resultados(digits,model,nx=5, ny=5,label = "correctos"):
    """
    Muestra los resultados de las prediciones de un modelo 
    de clasificacion en particular. Se toman aleatoriamente los valores
    de los resultados.
    
    - label == 'correcto': retorna los valores en que el modelo acierta.
    - label == 'incorrecto': retorna los valores en que el modelo no acierta.

    
    Observacion: El modelo que recibe como argumento debe NO encontrarse
    'entrenado'.
    
    
    :param digits: dataset 'digits'
    :param model: modelo de sklearn
    :param nx: numero de filas (subplots)
    :param ny: numero de columnas (subplots)
    :param label: datos correctos o incorrectos
    :return: graficos matplotlib
    """
    
    
    X = digits.drop(columns = "target").values
    y = digits["target"].values
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 42) 
    model.fit(X_train, y_train) # ajustando el modelo
    y_pred = model.predict(X_test)

    # Mostrar los datos correctos
    if label == "correctos":
        mask = (y_pred == y_test)
        color = "green"
        
    # Mostrar los datos correctos
    elif label == "incorrectos":
        mask = (y_pred != y_test)
        color = "red"
    
    else:
        raise ValueError("Valor incorrecto")
    

    X_aux = X_test[mask]
    y_aux_true = y_test[mask]
    y_aux_pred = y_pred[mask]

    # We'll plot the first 100 examples, randomly choosen
    fig, ax = plt.subplots(nx, ny, figsize=(12,12))
    for i in range(nx):
        for j in range(ny):
            index = j + ny * i
            data  = X_aux[index, :].reshape(8,8)
            label_pred = str(int(y_aux_pred[index]))
            label_true = str(int(y_aux_true[index]))
            ax[i][j].imshow(data, interpolation = 'nearest', cmap = 'gray_r')
            ax[i][j].text(0, 0, label_pred, horizontalalignment = 'center', verticalalignment = 'center', fontsize = 10, color = color)
            ax[i][j].text(7, 0, label_true, horizontalalignment = 'center', verticalalignment = 'center', fontsize = 10, color = 'blue')
            ax[i][j].get_xaxis().set_visible(False)
            ax[i][j].get_yaxis().set_visible(False)
    plt.show()<jupyter_output><empty_output><jupyter_text>**Pregunta*** Tomando en cuenta el mejor modelo entontrado en el `Ejercicio 3`, grafique los resultados cuando: * el valor predicho y original son iguales * el valor predicho y original son distintos * Cuando el valor predicho y original son distintos ,  ¿Por qué ocurren estas fallas?<jupyter_code>mostar_resultados(digits, neighbors.KNeighborsClassifier(), nx=5, ny=5,label = "correctos")
mostar_resultados(digits, neighbors.KNeighborsClassifier(), nx=2, ny=2,label = "incorrectos")<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10269846509324972, 'fraction_numerical': 0.014441326951642184, 'mean_word_length': 4.18216805644644, 'pattern_counts': {'":': 2, '<': 71, '<?xml version=': 0, '>': 72, 'https://': 5, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 19, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '19', 'dir': 'jupyter-structured-clean-dedup', 'id': '486813', 'license': 'MIT', 'n_tokens_mistral': 8615, 'n_tokens_neox': 8236, 'n_words': 2387, 'path': 'Tareas/tarea_02/tarea_02.ipynb', 'repo_name': 'PabloCalcumil/mat281_portafolio', 'size': '24252'}