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

np.random.seed(42)

"""
Explanation: Introducción al aprendizaje automático con scikit-learn
En los últimos tiempos habrás oído hablar de machine learning, deep learning, reinforcement learning, muchas más cosas que contienen la palabra learning y, por supuesto, Big Data. Con los avances en capacidad de cálculo de los últimos años y la popularización de lenguajes de alto nivel, hemos entrado de lleno en la fiebre de hacer que las máquinas aprendan. En esta clase veremos cómo utilizar el paquete scikit-learn de Python para poder crear modelos predictivos a partir de nuestros datos de una manera rápida y sencilla.
End of explanation
"""


x = np.random.randn(50)
y = 2.0 * x + 0.8 * np.random.randn(50)

plt.scatter(x, y)

"""
Explanation: En primer lugar vamos a probar con un ejemplo muy sencillo: ajustar una recta a unos datos. Esto difícilmente se puede llamar machine learning, pero nos servirá para ver cómo es la forma de trabajar con scikit-learn, cómo se entrenan los modelos y cómo se calculan las predicciones.
En primer lugar fabricamos unos datos distribuidos a lo largo de una recta con un poco de ruido:
End of explanation
"""


from sklearn.linear_model import LinearRegression

model = LinearRegression(fit_intercept=True)

"""
Explanation: El proceso para usar scikit-learn es el siguiente:

Separar los datos en matriz de características features y variable a predecir y
Seleccionar el modelo
Elegir los hiperparámetros
Ajustar o entrenar el modelo (model.fit)
Predecir con datos nuevos (model.predict)
End of explanation
"""


features = x.reshape(-1, 1)

model.fit(features, y)

y_hat = model.predict(features)

"""
Explanation: <div class="alert alert-info">Tenemos que hacer este `reshape` para transformar nuestro vector en una matriz de columnas. Rara vez tendremos que repetir este paso, puesto que en la práctica siempre tendremos varias variables.</div>
End of explanation
"""


from sklearn import metrics

abs_error = metrics.mean_absolute_error(y, y_hat)
abs_error

"""
Explanation: Para calcular el error, en el módulo sklearn.metrics tenemos varias funciones útiles:
End of explanation
"""


x_new = np.linspace(x.min(), x.max(), 10)

y_pred = model.predict(x_new.reshape(-1, 1))

plt.scatter(x, y)

plt.plot(x_new, y_pred, 'k--')
plt.scatter(x_new, y_pred, marker='x', lw=3, zorder=10)

plt.fill_between(x_new, y_pred + abs_error, y_pred - abs_error, color="C0", alpha=0.3)

"""
Explanation: Y ahora predecimos con datos nuevos:
End of explanation
"""


from sklearn.datasets import load_digits

digits = load_digits()
print(digits["DESCR"])

"""
Explanation: ¡Y ya está! Lo básico de scikit-learn está aquí. Lo próximo será usar diferentes tipos de modelos y examinar con rigor su rendimiento para poder seleccionar el que mejor funcione para nuestros datos.
Introducción rápida al aprendizaje automático
En aprendizaje automático tenemos dos tipos de problemas:

Aprendizaje supervisado, cuando tengo datos etiquetados, es decir: conozco la variable a predecir de un cierto número de observaciones. Pasándole esta información al algoritmo, este será capaz de predecir dicha variable cuando reciba observaciones nuevas. Dependiendo de la naturaleza de la variable a predecir, tendremos a su vez:
Regresión, si es continua (como el caso anterior), o
Clasificación, si es discreta o categórica (sí/no, color de ojos, etc)
Aprendizaje no supervisado, cuando no tenemos datos etiquetados y por tanto no tengo ninguna información a priori. En este caso usaremos los algoritmos para descubrir patrones en los datos y agruparlos, pero tendremos que manualmente inspeccionar el resultado después y ver qué sentido podemos darle a esos grupos.

En función de la naturaleza de nuestro problema, scikit-learn proporciona una gran variedad de algoritmos que podemos elegir.

Clasificación
En scikit-learn tenemos disponibles muchos datasets clásicos de ejemplo que podemos utilizar para practicar. Uno de ellos es el dataset MNIST, que consiste en imágenes escaneadas de números escritos a mano por funcionarios de los EEUU. Para cargarlo, importamos la función correspondiente de sklearn.datasets:
End of explanation
"""


from sklearn.model_selection import train_test_split

# X_train, X_test, Y_train, Y_test =
X_train, X_test, Y_train, Y_test = train_test_split(digits.data, digits.target, train_size=0.75)

# preserve
X_train.shape, Y_train.shape

# preserve
X_test.shape, Y_test.shape

"""
Explanation: Ya tenemos los datos separados en matriz de características y vector de predicción. En este caso, tendré 64 = 8x8 características (un valor numérico por cada pixel de la imagen) y mi variable a predecir será el número en sí.
Siempre que se hace aprendizaje supervisado, se ha de dividir el dataset en una parte para entrenamiento y otra para test (incluso a veces hay una partición más para validación)
End of explanation
"""


num_ = X_test[42]
label_ = Y_test[42]
num_

num_.reshape(8, 8).astype(int)

plt.figure(figsize=(2, 2))
plt.imshow(num_.reshape(8, 8), cmap=plt.cm.gray_r)

label_

"""
Explanation: Para visualizar estas imágenes tendremos que hacer un .reshape:
End of explanation
"""


from sklearn.linear_model import LogisticRegression

# Inicializamos el modelo
model = LogisticRegression()

# Lo entrenamos
model.fit(X_train, Y_train)

"""
Explanation: Ten en cuenta que nosotros sabemos qué número es cada imagen porque somos humanos y podemos leerlas. El ordenador lo sabe porque están etiquetadas, pero ¿qué pasa si viene una imagen nueva? Para eso tendremos que construir un modelo de clasificación. En este caso aplicaremos la regresión logística
End of explanation
"""


# Vemos los resultados para los datos de test
predictions = model.predict(X_test)

"""
Explanation: Y una vez que hemos ajustado el modelo, comprobemos cuáles son sus predicciones usando los mismos datos de entrenamiento:
End of explanation
"""


metrics.accuracy_score(predictions, Y_test)

"""
Explanation: De nuevo usamos sklearn.metrics para medir la eficacia del algoritmo:
End of explanation
"""


metrics.confusion_matrix(predictions, Y_test)

plt.imshow(metrics.confusion_matrix(predictions, Y_test), cmap=plt.cm.Blues_r)

"""
Explanation: ¡Parece que hemos acertado prácticamente todas! Más tarde volveremos sobre este porcentaje de éxito, que bien podría ser engañoso. De momento, representemos otra medida de éxito que es la matriz de confusión:
End of explanation
"""


# preserve
# https://github.com/amueller/scipy-2016-sklearn/blob/master/notebooks/05%20Supervised%20Learning%20-%20Classification.ipynb
from sklearn.datasets import make_blobs

# preserve
features, labels = make_blobs(centers=[[6, 0], [2, -1]], random_state=0)
features.shape

# preserve
plt.scatter(features[:, 0], features[:, 1], c=labels)

"""
Explanation: Clustering y reducción de dimensionalidad
Una vez que hemos visto los dos tipos de problemas supervisados, vamos a ver cómo se trabajan los problemas no supervisados. En primer lugar vamos a fabricar dos nubes de puntos usando la función make_blobs:
End of explanation
"""


from sklearn.cluster import KMeans

model = KMeans()
model

"""
Explanation: Hemos creado dos grupos y algunos puntos se solapan, pero ¿qué pasaría si no tuviésemos esta información visual? Vamos a emplear un modelo de clustering para agrupar los datos: en este caso KMeans
End of explanation
"""


model.fit(features)

"""
Explanation: Observa que por defecto tenemos 8 clusters. Veamos qué ocurre:
End of explanation
"""


centroids = model.cluster_centers_
centroids

labels_pred = model.predict(features)

"""
Explanation: Ahora no pasamos la información de las etiquetas al algoritmo a la hora de entrenar. En la práctica por supuesto no la tendremos.
End of explanation
"""


# preserve
xmin, xmax = features[:, 0].min(), features[:, 0].max()
ymin, ymax = features[:, 1].min(), features[:, 1].max()

xx, yy = np.meshgrid(
    np.linspace(xmin, xmax),
    np.linspace(ymin, ymax)
)

mesh = np.c_[xx.ravel(), yy.ravel()]
mesh

Z = model.predict(mesh)

# http://pybonacci.org/2015/01/14/introduccion-a-machine-learning-con-python-parte-1/
plt.pcolormesh(xx, yy, Z.reshape(xx.shape))

plt.scatter(features[:, 0], features[:, 1], marker='x', color='k') 
plt.scatter(centroids[:, 0], centroids[:, 1], marker='+', color='r', lw=2)

"""
Explanation: Y ahora preparamos el código para representar todas las regiones:
End of explanation
"""


from ipywidgets import interact

# preseve
@interact(n=(2, 6))
def cluster(n=3):
    model = KMeans(n_clusters=n)
    model.fit(features)
    labels_pred = model.predict(features)
    centroids = model.cluster_centers_

    Z = model.predict(mesh)

    plt.pcolormesh(xx, yy, Z.reshape(xx.shape))
    plt.scatter(features[:, 0], features[:, 1], marker='x', color='k')
    plt.scatter(centroids[:, 0], centroids[:, 1], marker='+', color='r', lw=2)

    plt.show()

"""
Explanation: Si lo metemos todo en una función interactiva:
End of explanation
"""


from sklearn.manifold import Isomap

model = Isomap(n_components=2)

model.fit(digits.data)

"""
Explanation: Reducción de dimensionalidad
Vamos a rescatar nuestro dataset de los dígitos y tratar de visualizarlo en dos dimensiones, lo que se conoce como reducción de dimensionalidad.
End of explanation
"""


digits_proj = model.transform(digits.data)

plt.scatter(digits_proj[:, 0], digits_proj[:, 1],
            c=digits.target, cmap=plt.cm.Vega10, alpha=0.5)
plt.colorbar()

"""
Explanation: Y ahora proyectamos los datos usando .transform:
End of explanation
"""


# preserve
import pandas as pd

def load_iris_df():
    from sklearn.datasets import load_iris

    iris = load_iris()
    features, labels = iris.data, iris.target

    df = pd.DataFrame(features, columns=iris.feature_names)
    df["species"] = pd.Categorical.from_codes(iris.target, categories=iris.target_names)

    #df = df.replace({'species': {0: iris.target_names[0], 1: iris.target_names[1], 2: iris.target_names[2]}})

    return df

iris_df = load_iris_df()

# preserve
iris_df.head()

# preserve
_ = pd.tools.plotting.scatter_matrix(iris_df, c=iris_df["species"].cat.codes, figsize=(10, 10))

"""
Explanation: Ejercicio

Visualiza el dataset de las flores (load_iris) utilizando las funciones que tienes más abajo. ¿Hay alguna forma clara de separar las tres especies de flores?
Separa el dataset en matriz de características features y vector de etiquetas labels. Conviértelos a arrays de NumPy usando .as_matrix().
Reduce la dimensionalidad del dataset a 2 usando sklearn.manifold.Isomap o sklearn.decomposition.PCA y usa un algoritmo de clustering con 3 clusters. ¿Se parecen los clusters que aparecen a los grupos originales?
Predice el tipo de flor usando un algoritmo de clasificación. Visualiza la matriz de confusión. ¿Cuál es el porcentaje de aciertos del algoritmo? ¿Es más certero en algún tipo de flor en concreto? ¿Concuerda esto con lo que pensaste en el apartado 1?
End of explanation
"""


