#  Machine Learning con Python

## Índice
1. **[¿Qué es Machine Learning?](#1.-¿Qué-es-Machine-Learning?)**

2. **[Tipos de Machine Learning](#2.-Tipos-de-Machine-Learning)**
   * [2.1 Aprendizaje supervisado](#2.1-Aprendizaje-supervisado)
   * [2.2 Aprendizaje no supervisado](#2.2-Aprendizaje-no-supervisado)
   * [2.3 Aprendizaje por refuerzo](#2.3-Aprendizaje-por-refuerzo)

3. **[Sobreentrenamiento y validación](#3.-Sobreentrenamiento-y-validación)**
   * [3.1 ¿Qué es el sobreentrenamiento?](#3.1-¿Qué-es-el-sobreentrenamiento?)
   * [3.2 Cómo evitar el sobreentrenamiento](#3.2-Cómo-evitar-el-sobreentrenamiento)
   * [3.3 Tipos de validaciones cruzadas](#3.3-Tipos-de-validaciones-cruzadas)
       * [3.3.1 Validación cruzada de K iteraciones](#3.3.1-Validación-cruzada-de-K-iteraciones)
       * [3.3.2 Validación cruzada aleatoria](#3.3.2-Validación-cruzada-aleatoria)
       * [3.3.3 Validación cruzada dejando uno fuera](#3.3.3-Validación-cruzada-dejando-uno-fuera)
   * [3.4 Cálculo del error](#3.4-Cálculo-del-error)
       * [3.4.1 Error de la validación cruzada de K iteraciones](#3.4.1-Error-de-la-validación-cruzada-de-K-iteraciones)
       * [3.4.2 Error de la validación cruzada aleatoria](#3.4.2-Error-de-la-validación-cruzada-aleatoria)
       * [3.4.3 Error de la validación cruzada dejando uno fuera](#3.4.3-Error-de-la-validación-cruzada-dejando-uno-fuera)

4. **[Pasos para construir un modelo de machine learning](#4.-Pasos-para-construir-un-modelo-de-machine-learning)**

5. **[Machine learning en Python](#5.-Machine-learning-en-Python)**
   * [5.1 Scikit-Learn](#5.1-Scikit-Learn)
   * [5.2 Statsmodels](#5.2-Statsmodels)
   * [5.3 PyMC](#5.3-PyMC)
   * [5.4 NTLK](#5.4-NTLK)
   * [5.5 Algoritmos más utilizados](#5.5-Algoritmos-más-utilizados)
   * [5.6 Lectura recomedada](#5.6-Lectura-recomedada)

6. **[Árboles de Decisión](#6.-Árboles-de-Decisión)**
   * [6.1 ¿Cómo funciona?](#6.1-¿Cómo-funciona?)
   * [6.2 Ejemplo: Salir a Jugar Golf](#6.2-Ejemplo:-Salir-a-Jugar-Golf)

7. **[Árboles de Decisión en Python: Iris DataSet](#7.-Árboles-de-Decisión-en-Python:-Iris-DataSet)**
   * [7.1 Labores de preprocesamiento](#7.1-Labores-de-preprocesamiento)
   * [7.2 Árboles de Decisión: DecisionTreeClassifier](#7.2-Árboles-de-Decisión:-DecisionTreeClassifier)
       * [7.2.1 Visualizando el árbol](#7.2.1-Visualizando-el-árbol)
       * [7.2.2 Visualizando la importancia de las características](#7.2.2-Visualizando-la-importancia-de-las-características)
       * [7.2.3 Classification Report](#7.2.3-Classification-Report)
       * [7.2.4 Haciendo Predicciones](#7.2.4-Haciendo-Predicciones)
   * [7.3 Árboles de Decisión: RandomForestClassifier](#7.3-Árboles-de-Decisión:-RandomForestClassifier)
       * [7.3.1 Haciendo predicciones](#7.3.1-Haciendo-predicciones)
       * [7.3.2 Classification Report](#7.3.2-Classification-Report)

8. * [Ejercicios](#8.-Ejercicios)

# [1.](#Índice) ¿Qué es Machine Learning?

Una de las ramas de estudio que cada vez está ganando más popularidad dentro de las ciencias de la computación es el  ***aprendizaje automático o Machine Learning***. Muchos de los servicios que utilizamos en nuestro día a día como google, gmail, netflix, spotify o amazon se valen de las herramientas que les brinda el Machine Learning para alcanzar un servicio cada vez más personalizado y lograr así ventajas competitivas sobre sus rivales.

Pero, ¿qué es exactamente Machine Learning?. El Machine Learning es el diseño y estudio de las herramientas informáticas que utilizan la experiencia pasada para tomar decisiones futuras; es el estudio de programas que pueden aprender de los datos. **El objetivo fundamental del Machine Learning es generalizar, o inducir una regla desconocida a partir de ejemplos donde esa regla es aplicada**. El ejemplo más típico donde podemos ver el uso del Machine Learning es en el filtrado de los correos basura o spam. Mediante la observación de miles de correos electrónicos que han sido marcados previamente como basura, los filtros de spam aprenden a clasificar los mensajes nuevos. El Machine Learning combina conceptos y técnicas de diferentes áreas del conocimiento, como las matemáticas, estadísticas y las ciencias de la computación; por tal motivo, hay muchas maneras de aprender la disciplina.

# [2.](#Índice) Tipos de Machine Learning

El Machine Learning tiene una amplia gama de aplicaciones, incluyendo motores de búsqueda, diagnósticos médicos, detección de fraude en el uso de tarjetas de crédito, análisis del mercado de valores, clasificación de secuencias de ADN, reconocimiento del habla y del lenguaje escrito, juegos y robótica. Pero para poder abordar cada uno de estos temas es crucial en primer lugar distinguir los distintos tipos de problemas de Machine Learning con los que nos podemos encontrar.

## [2.1](#Índice) Aprendizaje supervisado

En los problemas de aprendizaje supervisado se enseña o entrena al algoritmo a partir de datos que ya vienen etiquetados con la respuesta correcta. Cuanto mayor es el conjunto de datos, más el algoritmo puede aprender sobre el tema. Una vez concluído el entrenamiento, se le brindan nuevos datos, ya sin las etiquetas de las respuestas correctas, y el algoritmo de aprendizaje utiliza la experiencia pasada que adquirió durante la etapa de entrenamiento para predecir un resultado. Esto es similar al método de aprendizaje que se utiliza en las escuelas, donde se nos enseñan problemas y las formas de resolverlos, para que luego podamos aplicar los mismos métodos en situaciones similares.

## [2.2](#Índice) Aprendizaje no supervisado

En los problemas de aprendizaje no supervisado el algoritmo es entrenado usando un conjunto de datos que no tiene ninguna etiqueta; en este caso, nunca se le dice al algoritmo lo que representan los datos. La idea es que el algoritmo pueda encontrar por si solo patrones que ayuden a entender el conjunto de datos. El aprendizaje no supervisado es similar al método que utilizamos para aprender a hablar cuando somos bebes, en un principio escuchamos hablar a nuestros padres y no entendemos nada; pero a medida que vamos escuchando miles de conversaciones, nuestro cerebro comenzará a formar un modelo sobre cómo funciona el lenguaje y comenzaremos a reconocer patrones y a esperar ciertos sonidos.

## [2.3](#Índice) Aprendizaje por refuerzo

En los problemas de aprendizaje por refuerzo, el algoritmo aprende observando el mundo que le rodea. Su información de entrada es el feedback o retroalimentación que obtiene del mundo exterior como respuesta a sus acciones. Por lo tanto, el sistema aprende a base de ensayo-error. Un buen ejemplo de este tipo de aprendizaje lo podemos encontrar en los juegos, donde vamos probando nuevas estrategias y vamos seleccionando y perfeccionando aquellas que nos ayudan a ganar el juego. A medida que vamos adquiriendo más practica, el efecto acumulativo del refuerzo a nuestras acciones victoriosas terminará creando una estrategia ganadora.

# [3.](#Índice) Sobreentrenamiento y validación

## [3.1](#Índice) ¿Qué es el sobreentrenamiento?

Como mencionamos cuando definimos al Machine Learning, la idea fundamental es encontrar patrones que podamos generalizar para luego poder aplicar esta generalización sobre los casos que todavía no hemos observado y realizar predicciones. Pero también puede ocurrir que durante el entrenamiento solo descubramos casualidades en los datos que se parecen a patrones interesantes, pero que no generalicen. Esto es lo que se conoce con el nombre de **sobreentrenamiento o sobreajuste ó en inglés overfitting** .

El sobreentrenamiento es la tendencia que tienen la mayoría de los algoritmos de Machine Learning a ajustarse a unas características muy específicas de los datos de entrenamiento que no tienen relación causal con la función objetivo que estamos buscando para generalizar. El ejemplo más extremo de un modelo sobreentrenado es un modelo que solo memoriza las respuestas correctas; este modelo al ser utilizado con datos que nunca antes ha visto va a tener un rendimiento azaroso, ya que nunca logró generalizar un patrón para predecir.

    En aprendizaje automático, el sobreajuste (también es frecuente emplear el término en inglés overfitting) es el efecto de sobreentrenar un algoritmo de aprendizaje con unos ciertos datos para los que se conoce el resultado deseado. El algoritmo de aprendizaje debe alcanzar un estado en el que será capaz de predecir el resultado en otros casos a partir de lo aprendido con los datos de entrenamiento, generalizando para poder resolver situaciones distintas a las acaecidas durante el entrenamiento. Sin embargo, cuando un sistema se entrena demasiado (se sobreentrena) o se entrena con datos extraños, el algoritmo de aprendizaje puede quedar ajustado a unas características muy específicas de los datos de entrenamiento que no tienen relación causal con la función objetivo. Durante la fase de sobreajuste el éxito al responder las muestras de entrenamiento sigue incrementándose mientras que su actuación con muestras nuevas va empeorando.

## [3.2](#Índice) Cómo evitar el sobreentrenamiento

Como mencionamos anteriormente, todos los modelos de Machine Learning tienen tendencia al sobreentrenamiento; es por esto que debemos aprender a convivir con el mismo y tratar de tomar medidas preventivas para reducirlo lo más posible. Las dos principales estrategias para lidiar son el sobreentrenamiento son: la retención de datos y la validación cruzada.

En el primer caso, la idea es dividir nuestro conjunto de datos, en uno o varios conjuntos de entrenamiento y otro/s conjuntos de evaluación. Es decir, que no le vamos a pasar todos nuestros datos al algoritmo durante el entrenamiento, sino que vamos a retener una parte de los datos de entrenamiento para realizar una evaluación de la efectividad del modelo. Con esto lo que buscamos es evitar que los mismos datos que usamos para entrenar sean los mismos que utilizamos para evaluar. De esta forma vamos a poder analizar con más precisión como el modelo se va comportando a medida que más lo vamos entrenando y poder detectar el punto crítico en el que el modelo deja de generalizar y comienza a sobreajustarse a los datos de entrenamiento.

**La validación cruzada** es un procedimiento más sofisticado que el anterior. En lugar de solo obtener una simple estimación de la efectividad de la generalización; la idea es realizar un análisis estadístico para obtener otras medidas del rendimiento estimado, como la media y la varianza, y así poder entender cómo se espera que el rendimiento varíe a través de los distintos conjuntos de datos. Esta variación es fundamental para la evaluación de la confianza en la estimación del rendimiento. La validación cruzada también hace un mejor uso de un conjunto de datos limitado; ya que a diferencia de la simple división de los datos en uno el entrenamiento y otro de evaluación; la validación cruzada calcula sus estimaciones sobre todo el conjunto de datos mediante la realización de múltiples divisiones e intercambios sistemáticos entre datos de entrenamiento y datos de evaluación.

La validación cruzada o cross-validation es una técnica utilizada para evaluar los resultados de un análisis estadístico
y garantizar que son independientes de la partición entre datos de entrenamiento y prueba. Consiste en repetir y calcular la media aritmética obtenida de las medidas de evaluación sobre diferentes particiones. Se utiliza en entornos donde el objetivo principal es la predicción y se quiere estimar cómo de preciso es un modelo que se llevará a cabo a la práctica.
Es una técnica muy utilizada en proyectos de inteligencia artificial para validar modelos generados. La siguiente figura muestra un esquema de esta técnica.

## [3.3](#Índice) Tipos de validaciones cruzadas

### [3.3.1](#Índice) Validación cruzada de K iteraciones

En la validación cruzada de K iteraciones o K-fold cross-validation los datos de muestra se dividen en K subconjuntos. Uno de los subconjuntos se utiliza como datos de prueba y el resto (K-1) como datos de entrenamiento. El proceso de validación cruzada es repetido durante k iteraciones, con cada uno de los posibles subconjuntos de datos de prueba. Finalmente se realiza la media aritmética de los resultados de cada iteración para obtener un único resultado. Este método es muy preciso puesto que evaluamos a partir de K combinaciones de datos de entrenamiento y de prueba, pero aun así tiene una desventaja, y es que, a diferencia del método de retención, es lento desde el punto de vista computacional. En la práctica, la elección del número de iteraciones depende de la medida del conjunto de datos. Lo más común es utilizar la validación cruzada de 10 iteraciones (10-fold cross-validation).

### [3.3.2](#Índice) Validación cruzada aleatoria
Este método consiste en dividir aleatoriamente el conjunto de datos de entrenamiento y el conjunto de datos de
prueba. Para cada división la función de aproximación se ajusta a partir de los datos de entrenamiento y calcula los valores de salida para el conjunto de datos de prueba. El resultado final se corresponde a la media aritmética de los valores obtenidos para las diferentes divisiones. La ventaja de este método es que la división de datos entrenamiento-prueba no depende del número de iteraciones. Pero, en cambio, con este método hay algunas muestras que quedan sin evaluar y otras que se evalúan más de una vez, es decir, los subconjuntos de prueba y entrenamiento se pueden solapar.

### [3.3.3](#Índice) Validación cruzada dejando uno fuera
La validación cruzada dejando uno fuera o Leave-one-out cross-validation (LOOCV) implica separar los datos de forma que para cada iteración tengamos una sola muestra para los datos de prueba y todo el resto conformando los datos de entrenamiento. La evaluación viene dada por el error, y en este tipo de validación cruzada el error es muy bajo, pero en cambio, a nivel computacional es muy costoso, puesto que se tienen que realizar un elevado número de iteraciones, tantas como N muestras tengamos y para cada una analizar los datos tanto de entrenamiento como de prueba.

## [3.4](#Índice) Cálculo del error
La evaluación de las diferentes validaciones cruzadas normalmente viene dada por el error obtenido en cada iteración, ahora bien, por cada uno de los métodos puede variar el número de iteraciones, según la elección del diseñador en función del número de datos total.

### [3.4.1](#Índice) Error de la validación cruzada de K iteraciones
En cada una de las k iteraciones de este tipo de validación se realiza un cálculo de error. El resultado final lo obtenemos a partir de realizar la media aritmética de los $K$ valores de errores obtenidos, según la fórmula:

\begin{equation}
   E = \frac{1}{K}\sum_{i=1}^{K}E_i
\end{equation}

Es decir, se realiza el sumatorio de los $K$ valores de error y se divide entre el valor de $K$.

### [3.4.2](#Índice) Error de la validación cruzada aleatoria

En la validación cruzada aleatoria, a diferencia del método anterior, cogemos muestras al azar durante k iteraciones, aunque de igual manera, se realiza un cálculo de error para cada iteración. El resultado final también lo obtenemos a partir de realizar la media aritmética de los $K$ valores de errores obtenidos, según la misma fórmula

### [3.4.3](#Índice) Error de la validación cruzada dejando uno fuera
En la validación cruzada dejando uno fuera se realizan tantas iteraciones como muestras $(N)$ tenga el conjunto de datos. De forma que para cada una de las N iteraciones se realiza un cálculo de error. El resultado final lo obtenemos realizando la media aritmética de los N valores de errores obtenidos, según la fórmula:

\begin{equation}
   E = \frac{1}{N}\sum_{i=1}^{N}E_i
\end{equation}

Donde se realiza el sumatorio de los $N$ valores de error y se divide entre el valor de $N$.

# [4.](#Índice) Pasos para construir un modelo de machine learning

Construir un modelo de Machine Learning, no se reduce solo a utilizar un algoritmo de aprendizaje o utilizar una librería de Machine Learning; sino que es todo un proceso que suele involucrar los siguientes pasos:

* **Recolectar los datos**. Podemos recolectar los datos desde muchas fuentes, podemos, por ejemplo, extraer los datos de un sitio web u obtener los datos utilizando una API o desde una base de datos. Podemos también utilizar otros dispositivos que recolectan los datos por nosotros; o utilizar datos que son de dominio público. El número de opciones que tenemos para recolectar datos ¡no tiene fin!. Este paso parece obvio, pero es uno de los que más complicaciones trae y más tiempo consume.

* **Preprocesar los datos**. Una vez que tenemos los datos, tenemos que asegurarnos que tiene el formato correcto para nutrir nuestro algoritmo de aprendizaje. Es prácticamente inevitable tener que realizar varias tareas de preprocesamiento antes de poder utilizar los datos. Igualmente este punto suele ser mucho más sencillo que el paso anterior.

* **Explorar los datos**. Una vez que ya tenemos los datos y están con el formato correcto, podemos realizar un pre análisis para corregir los casos de valores faltantes o intentar encontrar a simple vista algún patrón en los mismos que nos facilite la construcción del modelo. En esta etapa suelen ser de mucha utilidad las medidas estadísticas y los gráficos en 2 y 3 dimensiones para tener una idea visual de como se comportan nuestros datos. En este punto podemos detectar valores atípicos que debamos descartar; o encontrar las características que más influencia tienen para realizar una predicción.

* **Entrenar el algoritmo**. Aquí es donde comenzamos a utilizar las técnicas de Machine Learning realmente. En esta etapa nutrimos al o los algoritmos de aprendizaje con los datos que venimos procesando en las etapas anteriores. La idea es que los algoritmos puedan extraer información útil de los datos que le pasamos para luego poder hacer predicciones.

* **Evaluar el algoritmo**. En esta etapa ponemos a prueba la información o conocimiento que el algoritmo obtuvo del entrenamiento del paso anterior. Evaluamos que tan preciso es el algoritmo en sus predicciones y si no estamos muy conforme con su rendimiento, podemos volver a la etapa anterior y continuar entrenando el algoritmo cambiando algunos parámetros hasta lograr un rendimiento aceptable.

* **Utilizar el modelo**. En esta ultima etapa, ya ponemos a nuestro modelo a enfrentarse al problema real. Aquí también podemos medir su rendimiento, lo que tal vez nos obligue a revisar todos los pasos anteriores.

# [5.](#Índice) Machine learning en Python

Como siempre me gusta comentar, una de las grandes ventajas que ofrece Python sobre otros lenguajes de programación; es lo grande y prolifera que es la comunidad de desarrolladores que lo rodean; comunidad que ha contribuido con una gran variedad de librerías de primer nivel que extienden la funcionalidades del lenguaje. Para el caso de Machine Learning, las principales librerías que podemos utilizar son:

## [5.1](#Índice) Scikit-Learn

Scikit-learn (http://scikit-learn.org/stable/documentation.html) es la principal librería que existe para trabajar con Machine Learning, incluye la implementación de un gran número de algoritmos de aprendizaje. La podemos utilizar para clasificaciones, extraccion de características, regresiones, agrupaciones, reducción de dimensiones, selección de modelos, o preprocesamiento. Posee una API que es consistente en todos los modelos y se integra muy bien con el resto de los paquetes científicos que ofrece Python. Esta librería también nos facilita las tareas de evaluación, diagnostico y validaciones cruzadas ya que nos proporciona varios métodos de fábrica para poder realizar estas tareas en forma muy simple.

## [5.2](#Índice) Statsmodels

Statsmodels es otra gran librería que hace foco en modelos estadísticos y se utiliza principalmente para análisis predictivos y exploratorios. Al igual que Scikit-learn, también se integra muy bien con el resto de los paquetes cientificos de Python. Si deseamos ajustar modelos lineales, hacer una análisis estadístico, o tal vez un poco de modelado predictivo, entonces Statsmodels es la librería ideal. Las pruebas estadísticas que ofrece son bastante amplias y abarcan tareas de validación para la mayoría de los casos.

## [5.3](#Índice) PyMC

pyMC es un módulo de Python que implementa modelos estadísticos bayesianos, incluyendo la cadena de Markov Monte Carlo(MCMC). pyMC ofrece funcionalidades para hacer el análisis bayesiano lo mas simple posible. Incluye los modelos bayesianos, distribuciones estadísticas y herramientas de diagnostico para la covarianza de los modelos. Si queremos realizar un análisis bayesiano esta es sin duda la librería a utilizar.

## [5.4](#Índice) NTLK

NLTK es la librería líder para el procesamiento del lenguaje natural o NLP por sus siglas en inglés. Proporciona interfaces fáciles de usar a más de 50 cuerpos y recursos léxicos, como WordNet, junto con un conjunto de bibliotecas de procesamiento de texto para la clasificación, tokenización, el etiquetado, el análisis y el razonamiento semántico.

Obviamente, aquí solo estoy listando unas pocas de las muchas librerías que existen en Python para trabajar con problemas de Machine Learning, los invito a realizar su propia investigación sobre el tema.

##  [5.5](#Índice) Algoritmos más utilizados

Los algoritmos que más se suelen utilizar en los problemas de Machine Learning son los siguientes:

 * **Regresión Lineal**
 * **Regresión Logística**
 * **Arboles de Decision**
 * **Random Forest**
 * **SVM o Máquinas de vectores de soporte.**
 * **KNN o K vecinos más cercanos.**
 * **K-means**

## [5.6](#Índice) Lectura recomedada

# [6.](#Índice) Árboles de Decisión

Los Árboles de Decisión son diagramas con construcciones lógicas, muy similares a los sistemas de predicción basados en reglas, que sirven para representar y categorizar una serie de condiciones que ocurren de forma sucesiva para la resolución de un problema, en general, de clasificación. Los Árboles de Decisión están compuestos por nodos raíz, nodos interiores, nodos terminales y ramas que emanan de los nodos interiores. Cada nodo interior en el árbol contiene una prueba de un atributo, y cada rama representa un valor distinto del atributo. Siguiendo las ramas desde el nodo raíz hacia abajo, cada ruta finalmente termina en un nodo terminal creando una segmentación de los datos. La figura siguiente muestra un esquema de un árbol de decisión.

*Este árbol consta de los siguientes componentes:*

1. Las preguntas / condiciones, son Nodos.
2. Las opciones Yes / No, representan las ramas.
3. Las acciones finales son las hojas del árbol.

## [6.1](#Índice) ¿Cómo funciona?

* El aprendizaje de árboles de decisión es sencillo, fácil de implementar y poderoso.
* Un árbol recibe un objeto o situación descrita por un conjunto de atributos y regresa una decisión
* Cada nodo interno corresponde a una prueba en el valor de uno de los atributos y las ramas están etiquetadas con los posibles valores de la prueba.
* Cada hoja especifica el valor de la clase

#### Complejidad

* Para n atributos, hay $2^n$ filas y podemos considerar la salida como una función definida por $2^n$ bits
* Con esto hay $2^{2^n}$ posibles funciones diferentes para $n$ atributos (para 6 atributos, hay $2\times10^{19}$ )
* Por lo que tenemos que usar algún algoritmo ingenioso para encontrar una hipótesis consistente en un espacio de búsqueda tan grande

#### Otras características
+ Un ejemplo es descrito por los valores de los atributos y el valor del predicado meta (clase)
+ Si el predicado es verdadero, entonces el ejemplo es positivo, sino el ejemplo es negativo
+ **En caso de existir más clases, los ejemplos de una sola clase son positivos y el resto de los ejemplos son considerados negativos**

#### Sigamos aprendiendo como funciona
* Idea: Probar primero el atributo más “importante”
* Este particiona los ejemplos creando diversos sub-conjuntos y cada subconjunto es un nuevo problema con menos ejemplos y un atributo menos
* Este proceso recursivo tiene varios posibles resultados:
   1. Si existen ejemplos positivos y negativos, escoge el mejor atributo
   2. Si todos los ejemplos son positivos (o negativos), termina y regresa True (o False)
   3. No quedan ejemplos, regresa un default con base en la clasificación mayoritaria de su nodo padre

 Algoritmo ID3 creado por Ross Quinlan https://en.wikipedia.org/wiki/Ross_Quinlan, http://www.rulequest.com/Personal/

#### ¿Cómo se hace?
* La medida en ESCOGE-ATRIBUTO debe ser máxima cuando el atributo discrimine perfectamente ejemplos positivos y negativos, y mı́nima cuando el atributo no sea relevante

* Una posibilidad es usar una medida basada en cantidad de información (basado en la teorı́a de Shannon y Weaver ’49)

* La cantidad de información mide la (im)pureza en una colección arbitraria de ejemplos
* La cantidad de información (medida en bits) recibida respecto a la ocurrencia de un evento es inversamente proporcional a la probabilidad de ocurrencia de dicho evento

## [6.2](#Índice) Ejemplo: Salir a Jugar Golf

### Entropía:

Supongamos que tenemos 2 clases p y n.

* En el caso de los árboles que queremos estimar las probabilidades de las respuestas (p ó n), lo hacemos con la proporción de ejemplos p y n

* Si se tiene p ejemplos y n ejemplos donde p + n = total de ejemplos, entonces la información requerida es:

\begin{equation}
  I \left(\frac{p}{p+n},\frac{n}{p+n}\right) = -\left(\frac{p}{p+n}\right)\log_2 \left(\frac{p}{p+n}\right) -  \left(\frac{n}{p+n}\right)\log_2 \left(\frac{n}{p+n}\right)
\end{equation}

en su forma general:

\begin{equation}
I(P(v_1),...,P(v_n)) = \sum_{i=1}^{N_{clases}} P(v_i)\log_2(P(v_i))
\end{equation}

* Cada atributo A, divide a los ejemplos en subconjuntos $E_1,E_2, . . . ,E_v$ de acuerdo a los $v$ valores del atributo

* Cada subconjunto $E_i$ tiene $p_i$ ejemplos y $n_i$ ejemplos, por lo que para cada rama  necesitamos: $I\left( \frac{p_i}{p_i + n_i}, \frac{n_i}{p_i + n_i} \right)$ para responder a una pregunta.

* Por lo que en promedio, después de probar el atributo A, necesitamos:

\begin{equation}
  E(A) = \sum_{i=1}^{v} \left( \frac{p_i+n_i}{p+n} \right) I\left( \frac{p_i}{p_i + n_i}, \frac{n_i}{p_i + n_i} \right)
\end{equation}

### Luego

* La cantidad de información que ganamos al seleccionar un atributo esta dada por:

\begin{equation}
G(A) = I\left( \frac{p}{p + n}, \frac{n}{p + n} \right) - E(A)
\end{equation}

+ La ganancia $G(A)$ de A me dice el número de bits que ahorramos para responder a la pregunta de la clase de un ejemplo, dado que conocemos el valor del atributo A.
* Mide que tan bien un atributo separa a los ejemplos de entrenamiento de acuerdo a la clase
* La funcion de evaluación escoge el atributo de mayor ganancia

### Saquemos las cuentas para el ejemplo de la tabla de Golf:

Primero calculemos la información requerida para la clasificación de las clases. Miramos la categoría clase donde tenemos P = 9 y N = 5 con un total de 14 ejemplos.

\begin{equation}
I\left( \frac{p}{p + n}, \frac{n}{p + n} \right) = I \left( \frac{9}{14},\frac{5}{14} \right) = - \frac{9}{14}\log_2 \left( \frac{9}{12} \right) - \frac{5}{14}\log_2 \left(\frac{5}{12} \right) = 0.941\ bits
\end{equation}

#### Para el atributo Ambiente:
Considerando el atributo **Ambiente**, con sus tres valores (v=3):

***Soleado:*** $p_1 = 2$, $n_1 = 3$; $I(p_1,n_1) = 0.971\ \ bits$

\begin{equation}
I\left( \frac{p_i}{p_i + n_i}, \frac{n_i}{p_i + n_i} \right) = I \left( \frac{2}{5},\frac{3}{5} \right) = - \frac{2}{5}\log_2 \left( \frac{2}{5} \right) - \frac{3}{5}\log_2 \left(\frac{3}{5} \right) = 0.971\ bits
\end{equation}

***Nublado:*** $p_2 = 4$, $n_2 = 0$; $I(p_2,n_2) = 0\ \ bits$

***Lluvioso:*** $p_3 = 3$, $n_3 = 2$; $I(p_3,n_3) = 0.971\ \ bits$

**Entropía para el atributo Ambiente es:**

$E(Ambiente) = \left( \frac{5}{14} \right) I(p_1,n_1) + \left( \frac{4}{14} \right) I(p_2,n_2) + \left( \frac{5}{14} \right) I(p_3,n_3) = 0.694\ bits$

#### Para Humedad (v=2):
***Alta:*** $p_1 = 3$, $n_1 = 4$; $I(p_1,n_1) = 0.985\ \ bits$

***Normal:*** $p_2 = 6$, $n_2 = 1$; $I(p_2,n_2) = 0.592\ \ bits$

$E(Humedad) = 0.798\ bits$

#### Para Viento (v=2):
***No:*** $p_1 = 6$, $n_1 = 2$; $I(p_1,n_1) = 0.811\ \ bits$

***Si:*** $p_2 = 3$, $n_2 = 3$; $I(p_2,n_2) = 1.0\ \ bits$

$E(Viento) = 0.892\ bits$

#### Para Temperatura:
$E(Temperatura) = 0.911\ bits$

#### Ganancias G(A) para cada atributo

Las ganancias son entonces:

$G(Ambiente) = 0.246\ bits $ (MAX)

$G(Humedad) = 0.151\ bits$

$G(Viento) = 0.048\ bits$

$G(Temperatura) = 0.029\ bits$

Por lo que ID3 escoge el atributo Ambiente como nodo raíz y procede a realizar el mismo proceso con los ejemplos de cada rama

#### Entonces:
Para Ambiente tenemos tres subconjuntos: **soleado** (2P, 3N), **nublado** (4P, 0N), **lluvioso** (3P, 2N).

Para nublado, no tenemos que hacer nada, mas que asignarle la clase P (mirar la Tabla)

Por ejemplo, para soleado haríamos el mismo proceso:

$G(Humedad) = 0.97 - [(3/5)0 + (2/5)0] = 0.97\ bits$ (MAX)

$G(Temperatura) = 0.97 - [(2/5)0 + (2/5)1 + (1/5)0] = 0.570\ bits$

$G(Viento) = 0.97 - [(2/5)1 + (3/5)0.918] = 0.019\ bits$

Con el arbol construido, podemos preguntar si está bién
jugar el sabado en la mañana con ambiente soleado,
temperatura alta, humedad alta y con viento, a lo cual el
árbol me responde que no

# [7.](#Índice) Árboles de Decisión en Python: Iris DataSet

Importamos las librerias necesarias para preparar los datos y crear nuestro modelo

```python
import warnings

# Arboles de Decision

# importando pandas, numpy y matplotlib
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

```

Cargamos los datos

```python
iris_df = pd.read_csv('../../data/04_01_iris.csv')
iris_df
```

## [7.1](#Índice) Labores de preprocesamiento

Sin embargo, scikit-learn no acepta cadenas como parámetros de las funciones, todo deben de ser números. Para ello, nos podemos valer del objeto sklearn.preprocessing.LabelEncoder, que nos transforma automáticamente las cadenas a números. La forma en que se utiliza es la siguiente:

```python
#? pd.tools.plotting.scatter_matrix()
#? preprocessing.LabelEncoder()

from sklearn import preprocessing
le = preprocessing.LabelEncoder()
le.fit(iris_df['species'])
iris_df['species_cod'] = le.transform(iris_df['species'])

iris_df
#le.inverse_transform(iris_df.species_cod)
```

Como podéis observar, primero se crea el LabelEncoder y luego se "entrena" mediante el método fit. Para un LabelEncoder, "entrenar" el modelo es decidir el mapeo que vimos anteriormente, en este caso:

   * Iris-setosa -> 0
   * Iris-versicolor -> 1
   * Iris-virginica -> 2

Una vez entrenado, utilizando el método transform del LabelEncoder, podremos transformar cualquier ndarray que queramos (hubiéramos tenido un error si alguna de las etiquetas de test no estuviera en train). Esta estructura (método fit más método transform o predict) se repite en muchos de los objetos de scikit-learn.

Hay muchas más tareas de preprocesamiento que se pueden hacer en scikit-learn. **Consulta el paquete sklearn.preprocessing**.

```python
warnings.filterwarnings('ignore')

import seaborn as sns;
sns.set(style="ticks", color_codes=True)
iris = sns.load_dataset("iris")
sns.pairplot(iris, hue="species")
plt.show()
# Nota--> ayuda sobre alguna función...
#? sns.pairplot
```

### Separamos los datos

Separamos los datos,

```python
iris_data = iris_df.drop(['species','species_cod'],axis=1)
iris_data.head()
```

```python
iris_target = iris_df.species_cod
iris_target.head()
```

### Separamos los Datos.... Entrenamiento y test

```python
# Separamos los Datos.... Entrenamiento y test
#?  train_test_split()

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(iris_data, iris_target,
                                                    test_size=0.33,
                                                    random_state=192,
                                                    shuffle =True)

print('Set de datos para Entrenamiento =',len(X_train))
print('Set de datos para Test',len(X_test))
print('Total',len(X_test)+len(X_train))
print('Data Shape=',X_test.shape)
print('Target Shape =',y_test.shape)

X_train.head()
```

## [7.2](#Índice) Árboles de Decisión: DecisionTreeClassifier

```python
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score

arbol = DecisionTreeClassifier(max_depth=None, criterion='entropy', random_state=454)

#? DecisionTreeClassifier
```

```python
warnings.filterwarnings('ignore')

# ? cross_val_score()
cross_val_score(arbol, iris_data, iris_target)
arbol.fit(X_train,y_train)

print ("Score with data Tes",arbol.score(X_test,y_test))
print ("Score with data Train",arbol.score(X_train,y_train))
```

### [7.2.1](#Índice) Visualizando el árbol

```python
from sklearn.tree import export_graphviz
import graphviz

iris_target_names = ['setosa', 'versicolor', 'virginica']
iris_feature_names =['sepal length (cm)',
 'sepal width (cm)',
 'petal length (cm)',
 'petal width (cm)']

export_graphviz(arbol,out_file='arbol.dot',class_names=iris_target_names,
                feature_names=iris_feature_names,
                impurity=False,filled=True
               )

with open('arbol.dot') as f:
    dot_graph = f.read()
graphviz.Source(dot_graph)
```

### [7.2.2](#Índice) Visualizando la importancia de las características

```python
cara = iris_data.shape[1]
plt.figure(figsize=(25, 10))
plt.barh(range(cara),arbol.feature_importances_) ##--> metodo que categoriza el orden de importancia
plt.yticks(np.arange(cara),iris_feature_names,fontsize=32)
plt.xlabel('Importancia de las Caracteristicas',fontsize=42)
plt.ylabel('Caracteristicas',fontsize=45)
plt.xticks(fontsize=32)
plt.show()
```

### [7.2.3](#Índice) Classification Report

```python
from sklearn.metrics import classification_report, accuracy_score, confusion_matrix

p = arbol.predict(X_test)

print ('Accuracy:', accuracy_score(y_test, p))
print ('\nConfusion Matrix:\n', confusion_matrix(y_test, p))
print ('\nClassification Report:\n', classification_report(y_test, p))
```

###  [7.2.4](#Índice) Haciendo Predicciones

```python
warnings.filterwarnings('ignore')
# Alguna Predicción....
prediccion = arbol.predict([[4.3,2.6,3.4,1.3]])
#prediccion = arbol.predict(X_test)
print(prediccion, le.inverse_transform(prediccion))
```

```python
warnings.filterwarnings('ignore')

ind = 78
print(iris_data.iloc[ind])
print('specie',iris_target.iloc[ind], le.inverse_transform(iris_target.iloc[[ind]]))
x_new = iris_data.iloc[ind]

print('\n======== PREDICTION ========')
prediction    = arbol.predict([x_new.values])
prediction_pb = arbol.predict_proba([x_new.values])
print('Specie prediction',prediction, le.inverse_transform(prediction))
print('Probability Specie prediction',prediction_pb)
```

## [7.3](#Índice) Árboles de Decisión: RandomForestClassifier

```python
# ---------------- Random Forests
from sklearn.ensemble import RandomForestClassifier
warnings.filterwarnings('ignore')

#?RandomForestClassifier()

forest = RandomForestClassifier(n_estimators=100, criterion='entropy', random_state=896, verbose = 2, n_jobs=3)
forest.fit(X_train,y_train)

#you can tune parameter such as:
# - n_job (how many cores)(n_job=-1 => all cores)
# - max_depth
# - max_feature

print('acc for training data: {:.2f}'.format(forest.score(X_train,y_train)))
print('acc for test data: {:.2f}'.format(forest.score(X_test,y_test)))
```

###  [7.3.1](#Índice) Haciendo predicciones

```python
import warnings
warnings.filterwarnings('ignore')

ind = 78
print(iris_data.iloc[ind])
print('specie',iris_target.iloc[ind], le.inverse_transform(iris_target.iloc[[ind]]))
x_new = iris_data.iloc[ind]

print('\n======== PREDICTION ========')
prediction = forest.predict([x_new.values])
prediction_pb = forest.predict_proba([x_new.values])
print('Specie prediction',prediction, le.inverse_transform(prediction))
print('Probability Specie prediction',prediction_pb)
```

###   [7.3.2](#Índice) Classification Report

```python
from sklearn.metrics import classification_report, accuracy_score, confusion_matrix

p = forest.predict(X_test)

print ('Accuracy:', accuracy_score(y_test, p))
print ('\nConfusion Matrix:\n', confusion_matrix(y_test, p))
print ('\nClassification Report:\n', classification_report(y_test, p))

```

#   [8.](#Índice) Ejercicios

**Replicar estas técnicas de Clasificación con los siguientes DataSet:**

## Breast Cancer Wisconsin (Diagnostic) Data Set

File: ./data/06_breast-cancer-wisconsin-data.csv

Source: http://archive.ics.uci.edu/ml/datasets/breast%2Bcancer%2Bwisconsin%2B%28diagnostic%29

Abstract: Diagnostic Wisconsin Breast Cancer Database Source:

Creators:

    Dr. William H. Wolberg, General Surgery Dept. University of Wisconsin, Clinical Sciences Center Madison, WI 53792 wolberg '@' eagle.surgery.wisc.edu

    W. Nick Street, Computer Sciences Dept. University of Wisconsin, 1210 West Dayton St., Madison, WI 53706 street '@' cs.wisc.edu 608-262-6619

    Olvi L. Mangasarian, Computer Sciences Dept. University of Wisconsin, 1210 West Dayton St., Madison, WI 53706 olvi '@' cs.wisc.edu

Información del conjunto de datos:

Las características se calculan a partir de una imagen digitalizada de aspiración con aguja fina (PAAF) de una masas mamarias. Describen las características de los núcleos celulares presentes en la imagen (más detalles http://pages.cs.wisc.edu/~street/images/)

Attribute Information:

    ID number
    Diagnosis (M = malignant, B = benign)

**Ten real-valued features are computed for each cell nucleus:**

a) radius (mean of distances from center to points on the perimeter)

b) texture (standard deviation of gray-scale values)

c) perimeter

d) area

e) smoothness (local variation in radius lengths)

f) compactness (perimeter^2 / area - 1.0)

g) concavity (severity of concave portions of the contour)

h) concave points (number of concave portions of the contour)

i) symmetry

j) fractal dimension ("coastline approximation" - 1)

## Diabetes Data Set

File: ./data/06_diabetes.csv

Source : https://archive.ics.uci.edu/ml/datasets/diabetes

Michael Kahn, MD, PhD, Universidad de Washington, St. Louis, MO Información del conjunto de datos:

Los registros de pacientes con diabetes se obtuvieron de dos fuentes: a) un dispositivo de registro electrónico automático y b) registros en papel.

El dispositivo automático tenía un reloj interno para los eventos de marca de tiempo, mientras que los registros en papel solo proporcionaban espacios de "tiempo lógico" (desayuno, almuerzo, cena, hora de acostarse).

Para los registros en papel, se asignaron horarios fijos para el desayuno (08:00), el almuerzo (12:00), la cena (18:00) y la hora de acostarse (22:00). Por lo tanto, los registros en papel tienen tiempos de grabación uniformes ficticios, mientras que los registros electrónicos tienen marcas de tiempo más realistas.

Los archivos de diabetes constan de cuatro campos por registro. Cada campo está separado por una pestaña y cada registro está separado por una nueva línea.

Nombres de archivos y formato

(1) Fecha en formato MM-DD-YYYY (2) Tiempo en formato XX: YY (3) Código (4) Valor

El campo Código se descifra de la siguiente manera:

33 = Dosis de insulina regular

34 = dosis de insulina NPH

35 = dosis de insulina UltraLente

48 = medición de glucosa en sangre no especificada

57 = medición de glucosa en sangre no especificada

58 = medición de glucosa en sangre antes del desayuno

59 = medición de glucosa en sangre después del desayuno

60 = medición de glucosa en sangre antes del almuerzo

61 = medición de glucosa en sangre después del almuerzo

62 = medición de glucosa en sangre antes de la cena

63 = medición de glucosa en sangre después de la cena

64 = Medición de glucosa en sangre antes del aperitivo

65 = Síntomas de hipoglucemia

66 = Ingestión típica de comida

67 = Ingestión de comida más de lo habitual

68 = ingesta de comida menos de lo usual

69 = actividad típica de ejercicio

70 = Actividad de ejercicio más de lo habitual

71 = actividad de ejercicio menos de lo habitual

72 = evento especial no especificado

https://www.geeksforgeeks.org/confusion-matrix-machine-learning/