Está en la página 1de 44

TEMA 1.

Introducción: Preprocesado de datos


Máxima Formación

Contents
Introducción al Curso 2

1. Machine Learning 2

2. Tipos de Problemas: Regresión & Clasificación. 3

3. Proceso de Machine Learning. 3

4. Manipulación de datos con dplyr. 5


4.1. Función: select() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
4.2. Función: filter() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
4.3. Función: mutate() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
4.4. Función: group_by() y summarise() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
4.5. Función: arrange() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
4.6. Función: inner_join() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

5. Análisis Exploratorio de datos. 13


5.1. Visualización de datos con R. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
5.2. Visualización de la Variable Dependiente. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
5.3. Visualización Variables Cuantitativas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
5.4. Visualización Variables Cualitativas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
5.5. Correlación entre variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
5.6. Introducción a ggplot2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

6. Preprocesado de los datos. 32


6.1. Imputación de valores Ausentes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
6.2. Centrado - Escalado de Variables Continuas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
6.3. Variables Dummys . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
6.4. Variables con Varianza Cero. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
6.5. División del conjunto de datos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

Anexo 1. Variable Dependiente Clasificación: Balanceo de clases. 38

Anexo 2. Variable dependiente Regresión: Simetría. 42

1
Introducción al Curso
Bienvenidos al curso de árboles de decisión con R. Este curso pretende enseñar los conocimientos de cómo
construir modelos de árboles de decisión, desde los más sencillos (árboles individuales), hasta modelos más
complicados, como el ensemble de modelos de árboles de decisión. El curso se ha construido de forma que el
nivel de dificultad aumenta según avanza en el temario, pero cualquier persona con interés puede realizarlo.
Es totalmente práctico con la finalidad de comprender la teoría por medio de ejemplos.
Empezaremos en el primer tema con los conocimientos esenciales sobre el Machine Learning y el preprocesado
y visualización de datos. En el tema dos veremos cómo se construye un árbol de decisión individual y como
optimizar sus parámetros. El tema tres se dedica a los métodos de ensemble de Bootstrap Aggregation. En el
veremos los modelos de Bagging y Random Forest. El tema cuatro se verán los modelos de Boosting. Además,
aprenderemos como realizar validación cruzada y a buscar los hiperparámetros de los distintos modelos.
La resolución de los ejercicios prácticos que veremos a lo largo del curso, no busca conseguir la mejor precisión,
ya que no es el objetivo del curso. El objetivo del curso es enseñar los mejores métodos para lograr la
mejor precisión. Obtener un mejor resultado en la resolución de problemas dependerá de muchos factores:
conocimientos técnicos sobre el problema a resolver, base de datos, tiempo, capacidad de computación. . .
Además, cuando nos enfrentamos con un problema de machine learning debemos asumir que no siempre
obtendremos los mismos resultados realizando los mismos métodos y que tendremos que realizar pruebas-error
para lograr un resultado óptimo.

1. Machine Learning
El Aprendizaje Automático o Machine Learning lo podemos definir como un método que permite a los
ordenadores aprender de los datos patrones y relaciones que existen en ellos y que nos ayudan en la toma de
decisiones. El Machine Learning lo podemos incluir dentro de la inteligencia artificial en una sub-rama de las
ciencias de la computación.
La gran cantidad de datos que en la actualidad se producen y la capacidad para procesar grandes volúmenes
de datos de forma rápida y precisa por medio de las tecnologías de Big Data, han hecho que el aprendizaje
automático tome una gran importancia, ya que aporta un valor añadido a nuestros datos y negocio.
El aprendizaje automático se apoya en las tecnologías de Big Data que surgen para resolver tres tipos de
problemas como son:
• Variedad de los datos: Han surgido nuevos tipos de datos que se quieren almacenar como son los datos
no estructurados.
• Escalabilidad: Se busca la rapidez en el rendimiento y procesamiento de los datos, por lo que se escala
en horizontal.
• Velocidad: La velocidad de generación de datos, necesita velocidad de procesamiento.
Se han creado nuevas herramientas y sistemas para resolver estos tres problemas o mejorar la solución de
procesamiento y análisis de datos. Las organizaciones se han dado cuenta del valor de los datos y los beneficios
que pueden obtener del tratamiento de estos, buscando patrones y relaciones que sirvan a la organización en
la toma de decisiones.
Nos podemos encontrar con diferentes tipos de problemas dependiendo del tipo de datos que disponemos:
• Aprendizaje supervisado: Son aquellos problemas de aprendizaje automático en los que el algoritmo
aprende de unos datos previos los cuales existe una variable etiquetada que es la variable a predecir. Su
objetivo es aprender de estos datos etiquetados para identificar los patrones y reglas que le permita
predecir la etiqueta al enfrentarse a nuevos casos. Dentro del aprendizaje supervisado nos podemos
encontrar con problemas de regresión y problemas de clasificación.
• Aprendizaje no supervisado: Son aquellos problemas donde no existen etiquetas con las que el algoritmo
aprende, es decir, no requiere de ningún etiquetado previo de los datos. Este tipo de aprendizaje busca
obtener relaciones, diferencias o asociaciones entre las distintas observaciones.

2
• Aprendizaje Semiestructurado: Entre el aprendizaje supervisado y el no supervisado, existe el aprendizaje
semi-supervisado que es una combinación de los dos anteriores. El algoritmo aprende tanto de datos
etiquetados como no etiquetados.
Nuestro curso se basa en el aprendizaje supervisado, es decir, siempre tendremos un conjunto de variables
predictoras y una variable a predecir (variable dependiente o etiquetada) en el conjunto de datos. Cuando el
algoritmo se enfrente a nuevos datos buscará etiquetar las nuevas observaciones en función del aprendizaje
del conjunto de entrenamiento.

2. Tipos de Problemas: Regresión & Clasificación.


Si estamos ante un problema de Machine Learning supervisado, nos podemos encontrar con dos tipos de
problemas en función de la variable a predecir o dependiente, pudiendo ser problemas de regresión o problemas
de clasificación.
Nos enfrentamos a un problema de regresión cuando la variable dependiente es una variable continua, es
decir, cuando puede tomar infinitos valores numéricos. Ejemplos de problemas de regresión son: estimar la
demanda de un producto, predecir el precio de una vivienda o predecir el valor de una acción.
Por el contrario, nos enfrentamos a un problema de clasificación cuando la variable dependiente es una
variable que puede tomar valores finitos, es decir, es una variable categórica.
Si la variable dependiente solo puede tomar dos valores nos enfrentamos a un problema de clasificación binaria.
Ejemplos de problemas de clasificación binaria son: conceder o no un préstamo, detección de una enfermedad
o no, predecir si una máquina se va a romper o no, clasificar un correo como no deseado o no, etc.
Si la variable dependiente puede tomar más de dos valores nos enfrentamos a un problema de clasificación
multiclase. Ejemplos de problemas de clasificación multiclase son: una clasificación de sentimiento de los
comentarios de Twitter (bueno, malo o neutro), predicción en imágenes de distintos objetos, etc.
A lo largo del curso veremos problemas tanto de regresión como de clasificación (binario y multiclase),
resueltos con árboles de decisión.

3. Proceso de Machine Learning.


Cuando nos enfrentamos con un problema de Machine Learning se debe de seguir un proceso para resolverlo.
Como punto inicial, que guiará todo el proceso, tendremos que definir el objetivo que queremos resolver con
los datos que podemos obtener en la empresa e identificar si estos datos son los correctos para el problema que
planteamos. En muchos casos, plantearemos un objetivo que, con los datos que tenemos no podemos resolver
o no son significativos, por lo que el objetivo tiene que estar en consonancia con los datos que tenemos.
1. Obtención y comprensión de los datos: Encontrar los datos que necesitamos es una de las etapas más
difíciles de completar y donde se dedica más tiempo junto con la preparación de estos, incluso existiendo
dentro de la organización. Uno de los problemas más habituales es que cada empleado guarda sus
propios archivos en local, con distintos formatos, distintos programas sin unificar con el resto de los
empleados. . . por lo que serán difíciles de obtener. Una vez que los tenemos, debemos familiarizarnos
con ellos, obteniendo una descripción de los datos y realizando una exploración de los mismos, que nos
pueden dar idea de que variables pueden ser importantes en el proceso de modelado del algoritmo.
2. Preparación de los datos: Cuando ya tenemos los datos y tenemos una descripción de ellos, tenemos
que realizar una limpieza de estos datos. Debemos buscar datos ausentes, influyentes o fuera de rango,
creación de nuevas variables o eliminación de aquellas que no consideramos influyentes o que están
altamente correlacionadas con otras. Además, podemos transformar los datos para que se mejore la
precisión del algoritmo, por ejemplo, reduciendo su dimensionalidad cuando tenemos muchas variables
correlacionadas (PCA). En esta fase buscamos preparar los datos para entrenar el modelo.

3
3. Entrenamiento del Modelo: En esta fase debemos elegir el modelo más adecuado en función del problema
al que nos enfrentamos, ya que utilizaremos distintos modelos para un problema de regresión que para
un problema de clasificación. Además, debemos de buscar los mejores parámetros (hiperparámetros)
del modelo que se ajusten a nuestros datos sin caer en el sobreajuste. Debemos de especificar la métrica
con la que vamos a evaluar nuestro modelo.
4. Resultado del modelo: Una vez que hemos entrenado nuestro modelo debemos probarlo sobre datos con
los que no haya sido entrenado (conjunto de validación) para evaluar como ajusta a nuevos datos y
medir la precisión de la predicción. Se busca identificar si el resultado del modelo está en consonancia
con los objetivos marcados. En esta fase muchas veces tenemos que retroceder a las dos fases anteriores
al no alcanzar los objetivos marcados incluso puede pasar que tengamos que redefinir el objetivo, ya
que con los datos que tenemos no podemos alcanzarlo.
5. Implantación: Una vez que tenemos el modelo que se ajusta a nuestros objetivos debemos instaurarlo
en la organización. Además, debemos de realizar un seguimiento y mantenimiento del mismo por si
surgen diferencias con los nuevos datos reales o por si con el paso del tiempo hay variaciones.
Hay que recordar que la mayoría de las veces que nos enfrentamos a un problema de Machine Learning,
tendremos que probar distintos métodos, distintos procesos de preparación de datos, distintos modelos y
parámetros. . . y que no existe un método fijo que podamos aplicar a todos los problemas, sino que solo
podemos probar y modificar.

4
4. Manipulación de datos con dplyr.
Uno de los paquetes que se han vuelto más populares en los últimos años es dplyr, que nos permite manipular
conjuntos de datos en R con el operador %>% o pipe. Este operador, junto con las funciones, que podemos
concatenar, nos sirve para ahorrarnos código y tiempo. Vamos a ver las funciones más importantes.
Lo primero que vamos a hacer es instalar el paquete y cargar la librería en nuestro espacio de trabajo.
#install.packages("dplyr")
library(dplyr)

Utilizaremos un dataset sobre empresas con una dimensión de 161 empresas y 41 variables referidos a datos
sobre actividad, facturación, deuda, si se le concede un préstamo, cantidad a conceder, etc.
#Cargamos los datos
empresas <- read.csv("DATASETS/Empresas.csv", header = T, sep = ";")
#Dimensiones
dim(empresas)

## [1] 161 41
#Cinco primeras observaciones
head(empresas,5)

## ID COD_CNAE ACTIVIDAD_CNAE
## 1 AA999 1013 Elaboracion de productos carnicos
## 2 AA1000 1011 Procesado y conservacion de carne
## 3 AA1001 4321 Instalaciones electricas
## 4 AA1002 4511 Venta de automoviles y vehiculos de motor ligeros
## 5 AA1003 5610 Restaurantes y puestos de comidas
## CONSTITUCION EMPLEADOS MUJERES HOMBRES CP SECTOR EBITDA_1
## 1 1985 80 18,52 81,48 45517 Fabricantes 1198
## 2 1972 61 1,79 98,21 28914 Fabricantes 657
## 3 1990 1 6,32 93,68 28970 Construccion 3550
## 4 2006 27 16,67 83,33 28905 No disponible 174
## 5 1986 10 0 0 28970 Servicios Generales 118
## EBITDA_2 EBITDA_3 INMOVILIZADO_1 INMOVILIZADO_2 INMOVILIZADO_3 TOTAL_ACTIVO_1
## 1 932 964 3489 3568 3449 23704
## 2 590 1028 5207 4956 3813 19299
## 3 3355 3247 4605 4355 4377 58811
## 4 219 125 1248 1260 152 4555
## 5 117 118 869 940 1012 1141
## TOTAL_ACTIVO_2 TOTAL_ACTIVO_3 FONDOS_PROPIOS_1 FONDOS_PROPIOS_2
## 1 17895 17431 8666 8349
## 2 18762 17861 11320 11083
## 3 47717 43705 14430 13229
## 4 4033 3143 809 786
## 5 1178 1149 551 545
## FONDOS_PROPIOS_3 DEUDA_LARGO_1 DEUDA_LARGO_2 DEUDA_LARGO_3 DEUDA_CORTO_1
## 1 8148 3010 1490 1432 6622
## 2 10162 983 986 1196 667
## 3 12926 1114 1401 1681 2970
## 4 115 0 0 0 3145
## 5 536 244 386 320 296
## DEUDA_CORTO_2 DEUDA_CORTO_3 IMPORTE_NETO_1 IMPORTE_NETO_2 IMPORTE_NETO_3
## 1 3271 3573 23381 21076 21257
## 2 775 1094 40339 38858 40784

5
## 3 2172 5777 104922 74855 70839
## 4 2673 2638 13240 11890 9755
## 5 212 253 528 500 540
## EFECTIVO_1 EFECTIVO_2 EFECTIVO_3 RESULTADO_1 RESULTADO_2 RESULTADO_3 CONCEDER
## 1 303 236 203 316 200 214 0
## 2 539 574 914 237 211 592 1
## 3 64 462 230 2419 2324 1689 1
## 4 275 91 130 23 26 -26 0
## 5 0 8 0 5 8 3 1
## CANTIDAD RECOMENDADO ENDEUDAMIENTO EBITDA
## 1 0 0 9632 1198
## 2 2292 2292 1650 657
## 3 3000 17216 4084 3550
## 4 0 0 3145 174
## 5 168 168 540 118

4.1. Función: select()


La primera función que vamos a ver es la función select() que podemos utilizarla para seleccionar un
subconjunto de columnas del dataset original. En nuestro ejemplo vamos a seleccionar en nuestro dataset de
empresas en tres columnas que son: ID, CONCEDER (concesión de un préstamo o no) y CANTIDAD (límite
a conceder). Además, nos permite ordenar las columnas en el orden seleccionado.
empresas_sub1 <- dplyr::select(empresas,ID,CONCEDER,CANTIDAD)
head(empresas_sub1)

## ID CONCEDER CANTIDAD
## 1 AA999 0 0
## 2 AA1000 1 2292
## 3 AA1001 1 3000
## 4 AA1002 0 0
## 5 AA1003 1 168
## 6 AA1004 1 3000
Con la misma función podemos realizar la operación contraria. Del dataset original podemos no seleccionar
las columnas que indiquemos con un guion delante del nombre. Quitamos las columnas: COD_CNAE,
ACTIVIDAD_CNAE, CONSTITUCION y EMPLEADOS.
empresas_sub2 <- dplyr:: select(empresas,-COD_CNAE,-ACTIVIDAD_CNAE,-CONSTITUCION,-EMPLEADOS)
head(empresas_sub2)

## ID MUJERES HOMBRES CP SECTOR EBITDA_1 EBITDA_2 EBITDA_3


## 1 AA999 18,52 81,48 45517 Fabricantes 1198 932 964
## 2 AA1000 1,79 98,21 28914 Fabricantes 657 590 1028
## 3 AA1001 6,32 93,68 28970 Construccion 3550 3355 3247
## 4 AA1002 16,67 83,33 28905 No disponible 174 219 125
## 5 AA1003 0 0 28970 Servicios Generales 118 117 118
## 6 AA1004 14,54 85,46 4850 Fabricantes 65718 53985 53153
## INMOVILIZADO_1 INMOVILIZADO_2 INMOVILIZADO_3 TOTAL_ACTIVO_1 TOTAL_ACTIVO_2
## 1 3489 3568 3449 23704 17895
## 2 5207 4956 3813 19299 18762
## 3 4605 4355 4377 58811 47717
## 4 1248 1260 152 4555 4033
## 5 869 940 1012 1141 1178
## 6 154671 160956 171730 710174 739895
## TOTAL_ACTIVO_3 FONDOS_PROPIOS_1 FONDOS_PROPIOS_2 FONDOS_PROPIOS_3

6
## 1 17431 8666 8349 8148
## 2 17861 11320 11083 10162
## 3 43705 14430 13229 12926
## 4 3143 809 786 115
## 5 1149 551 545 536
## 6 767894 325579 316169 291116
## DEUDA_LARGO_1 DEUDA_LARGO_2 DEUDA_LARGO_3 DEUDA_CORTO_1 DEUDA_CORTO_2
## 1 3010 1490 1432 6622 3271
## 2 983 986 1196 667 775
## 3 1114 1401 1681 2970 2172
## 4 0 0 0 3145 2673
## 5 244 386 320 296 212
## 6 177591 190601 237652 40318 80720
## DEUDA_CORTO_3 IMPORTE_NETO_1 IMPORTE_NETO_2 IMPORTE_NETO_3 EFECTIVO_1
## 1 3573 23381 21076 21257 303
## 2 1094 40339 38858 40784 539
## 3 5777 104922 74855 70839 64
## 4 2638 13240 11890 9755 275
## 5 253 528 500 540 0
## 6 110110 365428 291655 264131 72586
## EFECTIVO_2 EFECTIVO_3 RESULTADO_1 RESULTADO_2 RESULTADO_3 CONCEDER CANTIDAD
## 1 236 203 316 200 214 0 0
## 2 574 914 237 211 592 1 2292
## 3 462 230 2419 2324 1689 1 3000
## 4 91 130 23 26 -26 0 0
## 5 8 0 5 8 3 1 168
## 6 43894 15022 16909 32053 26876 1 3000
## RECOMENDADO ENDEUDAMIENTO EBITDA
## 1 0 9632 1198
## 2 2292 1650 657
## 3 17216 4084 3550
## 4 0 3145 174
## 5 168 540 118
## 6 176399 217909 65718
También nos sirve para seleccionar columnas con un patrón determinado. Vamos elegir columnas que empiecen
por la letra “e” y la columna ID
empresas_sub3 <- dplyr:: select(empresas, ID, starts_with("e"))
head(empresas_sub3)

## ID EMPLEADOS EBITDA_1 EBITDA_2 EBITDA_3 EFECTIVO_1 EFECTIVO_2 EFECTIVO_3


## 1 AA999 80 1198 932 964 303 236 203
## 2 AA1000 61 657 590 1028 539 574 914
## 3 AA1001 1 3550 3355 3247 64 462 230
## 4 AA1002 27 174 219 125 275 91 130
## 5 AA1003 10 118 117 118 0 8 0
## 6 AA1004 1 65718 53985 53153 72586 43894 15022
## ENDEUDAMIENTO EBITDA
## 1 9632 1198
## 2 1650 657
## 3 4084 3550
## 4 3145 174
## 5 540 118
## 6 217909 65718

7
De igual modo podemos utilizar ends_with() y contains() para elegir columnas que terminen o que contengan
un cierto patrón.

4.2. Función: filter()


Si la función select() nos permitía seleccionar columnas, con la función filter() nos permite seleccionar
observaciones. Filtramos observaciones cuyo año de constitución sea igual a 1990.
empresas_sub4 <- filter(empresas, CONSTITUCION == 1990)
head(empresas_sub4)

## ID COD_CNAE ACTIVIDAD_CNAE
## 1 AA1001 4321 Instalaciones electricas
## 2 AA1066 4638 Comercio al por mayor de pescados, mariscos y otros
## 3 AA1074 4638 Comercio al por mayor de pescados, mariscos y otros
## 4 AA1079 1013 Elaboracion de productos carnicos
## 5 AA1123 1071 Fabricacion de pan y de productos frescos de
## CONSTITUCION EMPLEADOS MUJERES HOMBRES CP SECTOR EBITDA_1
## 1 1990 1 6,32 93,68 28970 Construccion 3550
## 2 1990 103 33,33 66,67 23260 Comercio por mayor 1607
## 3 1990 480 47,88 52,12 50197 Comercio por mayor 9573
## 4 1990 9 0 0 37770 Fabricantes 73
## 5 1990 44 31,82 68,18 28914 Fabricantes 374
## EBITDA_2 EBITDA_3 INMOVILIZADO_1 INMOVILIZADO_2 INMOVILIZADO_3 TOTAL_ACTIVO_1
## 1 3355 3247 4605 4355 4377 58811
## 2 767 1804 9222 9442 9691 25977
## 3 6834 8593 29860 34060 39886 58412
## 4 68 107 273 283 305 2297
## 5 387 407 1007 2028 2247 2760
## TOTAL_ACTIVO_2 TOTAL_ACTIVO_3 FONDOS_PROPIOS_1 FONDOS_PROPIOS_2
## 1 47717 43705 14430 13229
## 2 28313 28428 8023 7819
## 3 55779 56680 36521 33628
## 4 2419 2399 1602 1567
## 5 4420 4419 966 951
## FONDOS_PROPIOS_3 DEUDA_LARGO_1 DEUDA_LARGO_2 DEUDA_LARGO_3 DEUDA_CORTO_1
## 1 12926 1114 1401 1681 2970
## 2 8486 3394 4008 4206 9465
## 3 32887 291 373 471 139
## 4 1540 0 50 73 14
## 5 938 269 347 401 383
## DEUDA_CORTO_2 DEUDA_CORTO_3 IMPORTE_NETO_1 IMPORTE_NETO_2 IMPORTE_NETO_3
## 1 2172 5777 104922 74855 70839
## 2 11790 10808 35235 34096 33181
## 3 143 446 167462 150423 136424
## 4 123 0 4507 4660 4099
## 5 373 532 9455 9600 5437
## EFECTIVO_1 EFECTIVO_2 EFECTIVO_3 RESULTADO_1 RESULTADO_2 RESULTADO_3 CONCEDER
## 1 64 462 230 2419 2324 1689 1
## 2 396 65 80 204 -594 95 0
## 3 446 340 290 2694 741 518 1
## 4 39 13 77 34 26 50 1
## 5 177 79 15 15 12 11 1
## CANTIDAD RECOMENDADO ENDEUDAMIENTO EBITDA
## 1 3000 17216 4084 3550

8
## 2 0 0 12859 1607
## 3 3000 57008 430 9573
## 4 424 424 14 73
## 5 1592 1592 652 374
Estas operaciones las podríamos haber realizado con el operador pipe. Vamos a filtrar por año de constitución
1990 y conceder = 1.
empresas %>%
filter(CONSTITUCION == 1990, CONCEDER == 1) %>%
head()

## ID COD_CNAE ACTIVIDAD_CNAE
## 1 AA1001 4321 Instalaciones electricas
## 2 AA1074 4638 Comercio al por mayor de pescados, mariscos y otros
## 3 AA1079 1013 Elaboracion de productos carnicos
## 4 AA1123 1071 Fabricacion de pan y de productos frescos de
## CONSTITUCION EMPLEADOS MUJERES HOMBRES CP SECTOR EBITDA_1
## 1 1990 1 6,32 93,68 28970 Construccion 3550
## 2 1990 480 47,88 52,12 50197 Comercio por mayor 9573
## 3 1990 9 0 0 37770 Fabricantes 73
## 4 1990 44 31,82 68,18 28914 Fabricantes 374
## EBITDA_2 EBITDA_3 INMOVILIZADO_1 INMOVILIZADO_2 INMOVILIZADO_3 TOTAL_ACTIVO_1
## 1 3355 3247 4605 4355 4377 58811
## 2 6834 8593 29860 34060 39886 58412
## 3 68 107 273 283 305 2297
## 4 387 407 1007 2028 2247 2760
## TOTAL_ACTIVO_2 TOTAL_ACTIVO_3 FONDOS_PROPIOS_1 FONDOS_PROPIOS_2
## 1 47717 43705 14430 13229
## 2 55779 56680 36521 33628
## 3 2419 2399 1602 1567
## 4 4420 4419 966 951
## FONDOS_PROPIOS_3 DEUDA_LARGO_1 DEUDA_LARGO_2 DEUDA_LARGO_3 DEUDA_CORTO_1
## 1 12926 1114 1401 1681 2970
## 2 32887 291 373 471 139
## 3 1540 0 50 73 14
## 4 938 269 347 401 383
## DEUDA_CORTO_2 DEUDA_CORTO_3 IMPORTE_NETO_1 IMPORTE_NETO_2 IMPORTE_NETO_3
## 1 2172 5777 104922 74855 70839
## 2 143 446 167462 150423 136424
## 3 123 0 4507 4660 4099
## 4 373 532 9455 9600 5437
## EFECTIVO_1 EFECTIVO_2 EFECTIVO_3 RESULTADO_1 RESULTADO_2 RESULTADO_3 CONCEDER
## 1 64 462 230 2419 2324 1689 1
## 2 446 340 290 2694 741 518 1
## 3 39 13 77 34 26 50 1
## 4 177 79 15 15 12 11 1
## CANTIDAD RECOMENDADO ENDEUDAMIENTO EBITDA
## 1 3000 17216 4084 3550
## 2 3000 57008 430 9573
## 3 424 424 14 73
## 4 1592 1592 652 374

9
4.3. Función: mutate()
La función mutate() nos sirve para crear variables. Creamos una función que sea el endeudamiento de la
empresa: deuda a corto plazo más deuda a largo plazo, dividido entre el ebitda de la sociedad. Lo creamos
para el año 1. Filtramos para que nos aparezcan solo las empresas de 1990.
empresas_sub5 <- mutate(empresas,endeudamiento = (DEUDA_CORTO_1 + DEUDA_LARGO_1)/EBITDA)
empresas_sub5 <- filter(empresas_sub5, CONSTITUCION == 1990)
empresas_sub5 <- dplyr::select(empresas_sub5, SECTOR, CONSTITUCION, endeudamiento)
head(empresas_sub5)

## SECTOR CONSTITUCION endeudamiento


## 1 Construccion 1990 1.1504225
## 2 Comercio por mayor 1990 8.0018668
## 3 Comercio por mayor 1990 0.0449180
## 4 Fabricantes 1990 0.1917808
## 5 Fabricantes 1990 1.7433155
Como veis hemos utilizado 3 pasos para obtener nuestro resultado. Ahora vamos a concatenar las operaciones
con el operador %>%.
empresas_sub6 <- empresas %>%
mutate(endeudamiento = (DEUDA_CORTO_1 + DEUDA_LARGO_1)/EBITDA) %>%
filter(CONSTITUCION ==1990) %>%
dplyr::select(SECTOR, CONSTITUCION, endeudamiento)
head(empresas_sub6)

## SECTOR CONSTITUCION endeudamiento


## 1 Construccion 1990 1.1504225
## 2 Comercio por mayor 1990 8.0018668
## 3 Comercio por mayor 1990 0.0449180
## 4 Fabricantes 1990 0.1917808
## 5 Fabricantes 1990 1.7433155
Hemos obtenido el mismo resultado, pero lo hemos reducido a una única función, ahorrándonos tiempo de
computación y realizando el proceso más fluido. El operador %>% o pipe nos sirve para concatenar las
funciones. Su función es coger la salida de una sentencia y le aplica la siguiente función.
También con el verbo mutate() podemos crear variables categóricas con el parámetro case_when(). Vamos a
realizar una nueva variable en función de la facturación de la sociedad en el último año.
empresas_mutate <- empresas %>%
mutate(TAMANO = case_when(IMPORTE_NETO_1 <= 10000 ~ "Pequeña",
IMPORTE_NETO_1 > 10000 & IMPORTE_NETO_1 <= 50000 ~ "Mediana",
IMPORTE_NETO_1 > 50000 ~ "Grande"))
empresas_mutate$TAMANO <- as.factor(empresas_mutate$TAMANO)
summary(empresas_mutate$TAMANO)

## Grande Mediana Pequeña NA's


## 26 40 94 1

4.4. Función: group_by() y summarise()


Estas dos funciones normalmente se usan conjuntamente. La función group_by() nos sirve para agrupar por
columnas y la función summarise() para realizar alguna operación con las columnas.
Vamos a agrupar por año de constitución y calculamos los empleados medios por año de constitución.

10
empresas_sub7 <- empresas %>%
group_by(CONSTITUCION) %>%
summarise(media_empl = mean(EMPLEADOS))
head(empresas_sub7,10)

## # A tibble: 10 x 2
## CONSTITUCION media_empl
## <int> <dbl>
## 1 1902 795
## 2 1918 300
## 3 1921 1
## 4 1923 153
## 5 1947 283
## 6 1950 1
## 7 1955 22
## 8 1956 60
## 9 1962 366
## 10 1966 15
Ahora agrupamos las columnas por año de constitución y sector, y obtenemos la media, el mínimo y el
máximo de empleados de las empresas por año (en este caso la media, mínimo y máximo coinciden al tener
una observación por cada año).
empresas_sub8 <- empresas %>%
group_by(ID,CONSTITUCION, SECTOR) %>%
summarise(media_empl = mean(EMPLEADOS),min_empl = min(EMPLEADOS), max_empl = max(EMPLEADOS))
head(empresas_sub8)

## # A tibble: 6 x 6
## # Groups: ID, CONSTITUCION [6]
## ID CONSTITUCION SECTOR media_empl min_empl max_empl
## <chr> <int> <chr> <dbl> <int> <int>
## 1 AA1000 1972 Fabricantes 61 61 61
## 2 AA1001 1990 Construccion 1 1 1
## 3 AA1002 2006 No disponible 27 27 27
## 4 AA1003 1986 Servicios Generales 10 10 10
## 5 AA1004 1989 Fabricantes 1 1 1
## 6 AA1005 1988 Construccion 19 19 19

4.5. Función: arrange()


Con la función arrange() podemos ordenar nuestro dataset por columnas de forma descendiente con el atributo
desc() o ascendente por defecto si solo le indicamos la columna. Ordenamos el dataset empresas_sub7 de
forma ascendente, por la variable media_empl.
empresas %>%
group_by(CONSTITUCION) %>%
summarise(media_empl = mean(EMPLEADOS)) %>%
arrange(media_empl)

## # A tibble: 53 x 2
## CONSTITUCION media_empl
## <int> <dbl>
## 1 1921 1
## 2 1950 1
## 3 2014 4

11
## 4 1974 9
## 5 1993 9
## 6 2011 12.8
## 7 1991 14
## 8 1966 15
## 9 2010 16
## 10 2009 16.3
## # ... with 43 more rows
Y ahora ordenamos el dataset de forma descendente por la variable media_empl.
empresas %>%
group_by(CONSTITUCION) %>%
summarise(media_empl = mean(EMPLEADOS)) %>%
arrange(desc(media_empl))

## # A tibble: 53 x 2
## CONSTITUCION media_empl
## <int> <dbl>
## 1 1902 795
## 2 1983 420
## 3 1970 400
## 4 1962 366
## 5 1918 300
## 6 1947 283
## 7 1981 222
## 8 1986 211.
## 9 1977 190
## 10 1973 162.
## # ... with 43 more rows

4.6. Función: inner_join()


Con el paquete dplyr también podemos unir conjuntos de datos con una serie de llaves. Vamos a unir el
dataset denominado empresas_sub1 con el dataset empresas_sub8 que incluye las nuevas variables que hemos
creado media_empl, min_empl y max_empl. Le tenemos que indicar con el parámetro by aquella o aquellas
columnas que se repiten en los dos datasets.
empresas_sub11 <- inner_join(empresas_sub1, empresas_sub8, by = "ID")
head(empresas_sub11,5)

## ID CONCEDER CANTIDAD CONSTITUCION SECTOR media_empl min_empl


## 1 AA999 0 0 1985 Fabricantes 80 80
## 2 AA1000 1 2292 1972 Fabricantes 61 61
## 3 AA1001 1 3000 1990 Construccion 1 1
## 4 AA1002 0 0 2006 No disponible 27 27
## 5 AA1003 1 168 1986 Servicios Generales 10 10
## max_empl
## 1 80
## 2 61
## 3 1
## 4 27
## 5 10

12
5. Análisis Exploratorio de datos.
Al enfrentarnos a un problema de Machine Learning, lo primero que debemos realizar es una exploración
descriptiva de los datos con los que aprenderá nuestro modelo. Este análisis es fundamental ya que nos
familiarizará con nuestros datos y nos dará una idea sobre cómo se distribuyen los datos, las variables más
importantes, nuevas variables que podemos crear, etc
Nos podemos encontrar con los siguientes tipos de variables:
• Variables cuantitativas que son variables numéricas que pueden dividirse en continuas (número reales)
o discretas (número enteros).
• Variables cualitativas: Son variables categóricas que pueden dividirse en ordinales (presentan un orden)
o nominales (no presentan un orden establecido).
En función del tipo de variables realizaremos distintos análisis visuales.

5.1. Visualización de datos con R.


Aunque este curso es sobre árboles de decisión, vamos a ver como se crean gráficos básicos con R. Si queremos
gráficos mucho más avanzados, con mayor calidad y opciones, podemos utilizar el paquete ggplot2, en este
apartado vamos a utilizar las funciones básicas del paquete graphics() que viene precargado con R y que son
de fácil uso, y más adelante haremos una introducción a ggplot2 para realizar estos mismos gráficos.
Vamos a utilizar un dataset de la librería MASS llamado “Cars93”, el cual contiene 93 coches en venta en
EEUU en 1993 y 27 variables como origen del coche, precio, mínimo y máximo precio, número de pasajeros,
capacidad del depósito, etc. Hay tanto variables cualitativas como cuantitativas.
Cargamos la librería, obtenemos las dimensiones del dataset y la estructura básica de los datos.
#Cargamos los datos.
library(MASS)
data("Cars93")
#Estadisticos basicos
summary(Cars93)

## Manufacturer Model Type Min.Price Price


## Chevrolet: 8 100 : 1 Compact:16 Min. : 6.70 Min. : 7.40
## Ford : 8 190E : 1 Large :11 1st Qu.:10.80 1st Qu.:12.20
## Dodge : 6 240 : 1 Midsize:22 Median :14.70 Median :17.70
## Mazda : 5 300E : 1 Small :21 Mean :17.13 Mean :19.51
## Pontiac : 5 323 : 1 Sporty :14 3rd Qu.:20.30 3rd Qu.:23.30
## Buick : 4 535i : 1 Van : 9 Max. :45.40 Max. :61.90
## (Other) :57 (Other):87
## Max.Price MPG.city MPG.highway AirBags
## Min. : 7.9 Min. :15.00 Min. :20.00 Driver & Passenger:16
## 1st Qu.:14.7 1st Qu.:18.00 1st Qu.:26.00 Driver only :43
## Median :19.6 Median :21.00 Median :28.00 None :34
## Mean :21.9 Mean :22.37 Mean :29.09
## 3rd Qu.:25.3 3rd Qu.:25.00 3rd Qu.:31.00
## Max. :80.0 Max. :46.00 Max. :50.00
##
## DriveTrain Cylinders EngineSize Horsepower RPM
## 4WD :10 3 : 3 Min. :1.000 Min. : 55.0 Min. :3800
## Front:67 4 :49 1st Qu.:1.800 1st Qu.:103.0 1st Qu.:4800
## Rear :16 5 : 2 Median :2.400 Median :140.0 Median :5200
## 6 :31 Mean :2.668 Mean :143.8 Mean :5281
## 8 : 7 3rd Qu.:3.300 3rd Qu.:170.0 3rd Qu.:5750
## rotary: 1 Max. :5.700 Max. :300.0 Max. :6500

13
##
## Rev.per.mile Man.trans.avail Fuel.tank.capacity Passengers
## Min. :1320 No :32 Min. : 9.20 Min. :2.000
## 1st Qu.:1985 Yes:61 1st Qu.:14.50 1st Qu.:4.000
## Median :2340 Median :16.40 Median :5.000
## Mean :2332 Mean :16.66 Mean :5.086
## 3rd Qu.:2565 3rd Qu.:18.80 3rd Qu.:6.000
## Max. :3755 Max. :27.00 Max. :8.000
##
## Length Wheelbase Width Turn.circle
## Min. :141.0 Min. : 90.0 Min. :60.00 Min. :32.00
## 1st Qu.:174.0 1st Qu.: 98.0 1st Qu.:67.00 1st Qu.:37.00
## Median :183.0 Median :103.0 Median :69.00 Median :39.00
## Mean :183.2 Mean :103.9 Mean :69.38 Mean :38.96
## 3rd Qu.:192.0 3rd Qu.:110.0 3rd Qu.:72.00 3rd Qu.:41.00
## Max. :219.0 Max. :119.0 Max. :78.00 Max. :45.00
##
## Rear.seat.room Luggage.room Weight Origin Make
## Min. :19.00 Min. : 6.00 Min. :1695 USA :48 Acura Integra: 1
## 1st Qu.:26.00 1st Qu.:12.00 1st Qu.:2620 non-USA:45 Acura Legend : 1
## Median :27.50 Median :14.00 Median :3040 Audi 100 : 1
## Mean :27.83 Mean :13.89 Mean :3073 Audi 90 : 1
## 3rd Qu.:30.00 3rd Qu.:15.00 3rd Qu.:3525 BMW 535i : 1
## Max. :36.00 Max. :22.00 Max. :4105 Buick Century: 1
## NA's :2 NA's :11 (Other) :87
#Cinco primeros datos.
head(Cars93,5)

## Manufacturer Model Type Min.Price Price Max.Price MPG.city MPG.highway


## 1 Acura Integra Small 12.9 15.9 18.8 25 31
## 2 Acura Legend Midsize 29.2 33.9 38.7 18 25
## 3 Audi 90 Compact 25.9 29.1 32.3 20 26
## 4 Audi 100 Midsize 30.8 37.7 44.6 19 26
## 5 BMW 535i Midsize 23.7 30.0 36.2 22 30
## AirBags DriveTrain Cylinders EngineSize Horsepower RPM
## 1 None Front 4 1.8 140 6300
## 2 Driver & Passenger Front 6 3.2 200 5500
## 3 Driver only Front 6 2.8 172 5500
## 4 Driver & Passenger Front 6 2.8 172 5500
## 5 Driver only Rear 4 3.5 208 5700
## Rev.per.mile Man.trans.avail Fuel.tank.capacity Passengers Length Wheelbase
## 1 2890 Yes 13.2 5 177 102
## 2 2335 Yes 18.0 5 195 115
## 3 2280 Yes 16.9 5 180 102
## 4 2535 Yes 21.1 6 193 106
## 5 2545 Yes 21.1 4 186 109
## Width Turn.circle Rear.seat.room Luggage.room Weight Origin Make
## 1 68 37 26.5 11 2705 non-USA Acura Integra
## 2 71 38 30.0 15 3560 non-USA Acura Legend
## 3 67 37 28.0 14 3375 non-USA Audi 90
## 4 70 37 31.0 17 3405 non-USA Audi 100
## 5 69 39 27.0 13 3640 non-USA BMW 535i

14
#Dimensiones del conjunto de datos.
dim(Cars93)

## [1] 93 27
#Estructura de los datos
str(Cars93)

## 'data.frame': 93 obs. of 27 variables:


## $ Manufacturer : Factor w/ 32 levels "Acura","Audi",..: 1 1 2 2 3 4 4 4 4 5 ...
## $ Model : Factor w/ 93 levels "100","190E","240",..: 49 56 9 1 6 24 54 74 73 35 ...
## $ Type : Factor w/ 6 levels "Compact","Large",..: 4 3 1 3 3 3 2 2 3 2 ...
## $ Min.Price : num 12.9 29.2 25.9 30.8 23.7 14.2 19.9 22.6 26.3 33 ...
## $ Price : num 15.9 33.9 29.1 37.7 30 15.7 20.8 23.7 26.3 34.7 ...
## $ Max.Price : num 18.8 38.7 32.3 44.6 36.2 17.3 21.7 24.9 26.3 36.3 ...
## $ MPG.city : int 25 18 20 19 22 22 19 16 19 16 ...
## $ MPG.highway : int 31 25 26 26 30 31 28 25 27 25 ...
## $ AirBags : Factor w/ 3 levels "Driver & Passenger",..: 3 1 2 1 2 2 2 2 2 2 ...
## $ DriveTrain : Factor w/ 3 levels "4WD","Front",..: 2 2 2 2 3 2 2 3 2 2 ...
## $ Cylinders : Factor w/ 6 levels "3","4","5","6",..: 2 4 4 4 2 2 4 4 4 5 ...
## $ EngineSize : num 1.8 3.2 2.8 2.8 3.5 2.2 3.8 5.7 3.8 4.9 ...
## $ Horsepower : int 140 200 172 172 208 110 170 180 170 200 ...
## $ RPM : int 6300 5500 5500 5500 5700 5200 4800 4000 4800 4100 ...
## $ Rev.per.mile : int 2890 2335 2280 2535 2545 2565 1570 1320 1690 1510 ...
## $ Man.trans.avail : Factor w/ 2 levels "No","Yes": 2 2 2 2 2 1 1 1 1 1 ...
## $ Fuel.tank.capacity: num 13.2 18 16.9 21.1 21.1 16.4 18 23 18.8 18 ...
## $ Passengers : int 5 5 5 6 4 6 6 6 5 6 ...
## $ Length : int 177 195 180 193 186 189 200 216 198 206 ...
## $ Wheelbase : int 102 115 102 106 109 105 111 116 108 114 ...
## $ Width : int 68 71 67 70 69 69 74 78 73 73 ...
## $ Turn.circle : int 37 38 37 37 39 41 42 45 41 43 ...
## $ Rear.seat.room : num 26.5 30 28 31 27 28 30.5 30.5 26.5 35 ...
## $ Luggage.room : int 11 15 14 17 13 16 17 21 14 18 ...
## $ Weight : int 2705 3560 3375 3405 3640 2880 3470 4105 3495 3620 ...
## $ Origin : Factor w/ 2 levels "USA","non-USA": 2 2 2 2 2 1 1 1 1 1 ...
## $ Make : Factor w/ 93 levels "Acura Integra",..: 1 2 4 3 5 6 7 9 8 10 ...

5.2. Visualización de la Variable Dependiente.


Suponemos que queremos conocer si un coche está fabricado por empresas cuyos orígenes son de USA o no,
por lo que nuestra variable dependiente sería Origin que puede tomar dos valores “USA” o “non-USA”, por
lo que estaríamos ante una variable cualitativa nominal, ya que no tiene un orden.
Estudiamos como se distribuye nuestra variable dependiente:
table(Cars93$Origin)

##
## USA non-USA
## 48 45
Vemos que nuestros datos están compuestos por 48 vehículos fabricados por empresas de USA y 45 vehículos
no fabricados por empresas de USA. Vamos a obtener los porcentajes de cada uno de los niveles.
round(prop.table(table(Cars93$Origin))*100,2)

##

15
## USA non-USA
## 51.61 48.39
El 51.61% de los vehículos de empresas de USA y el 48.39% no lo son. Visualmente podríamos realizarlo con
la función barplot().
barplot(table(Cars93$Origin),
xlab = "Origen de la fabricación del vehículo",
ylab = "Número de Casos",
main ="Disribución de la variable",
col = c("red","green"))

Disribución de la variable
40
Número de Casos

30
20
10
0

USA non−USA

Origen de la fabricación del vehículo

Del análisis de la variable dependiente podemos deducir que el dataset está balanceado con un número muy
similar de casos con origen USA como de Origen fuera de USA y por lo tanto el algoritmo a utilizar no tendrá
problemas para aprender de los dos tipos de clases que tenemos. Si tuviéramos una variable dependiente,
donde una de las clases tuviera menos casos que otra, una buena técnica sería balancear el dataset para que
exista el mismo número de casos en cada clase, como veremos al final de este tema.

5.3. Visualización Variables Cuantitativas.


Debemos estudiar la relación de las variables explicativas numéricas en función de la variable dependiente
o respuesta que, en este caso, es la variable Origin con el objetivo de ver que variables pueden ser más
importantes para el modelo. Evaluamos la relación entre una variable explicativa numérica y las categorías
(2) de la variable. No vamos a estudiar todas ya que nos ocuparía mucho tiempo, pero vamos a ver como se
haría. Para ver esta relación con un gráfico, utilizamos la librería sm que instalamos y cargamos primero.
#Instalamos la librería y la cargamos.
#install.packages("sm")

16
library(sm)
#Creamos los gráficos en función de la variable dependiente.
var_con <- c("Price","MPG.city","Fuel.tank.capacity","Passengers","Weight")
for (i in var_con) {
sm.density.compare(Cars93[,i],Cars93$Origin,xlab =i)
title(main = paste("Distribución",i, "~ Origin") )
legend("topright",levels(Cars93$Origin),
fill = 2 +(0:nlevels(Cars93$Origin)))
}

Distribución Price ~ Origin


0.06

USA
non−USA
0.04
Density

0.02
0.00

0 20 40 60

Price

17
Distribución MPG.city ~ Origin
0.10

USA
non−USA
0.08
0.06
Density

0.04
0.02
0.00

10 20 30 40 50

MPG.city

18
Distribución Fuel.tank.capacity ~ Origin

USA
0.12

non−USA
0.08
Density

0.04
0.00

5 10 15 20 25 30

Fuel.tank.capacity

19
Distribución Passengers ~ Origin

USA
0.5

non−USA
0.4
Density

0.3
0.2
0.1
0.0

2 4 6 8

Passengers

20
Distribución Weight ~ Origin
0.0006

USA
non−USA
0.0004
Density

0.0002
0.0000

1000 2000 3000 4000

Weight

Vemos que en todas las variables hay diferencias sustanciales entre los coches fabricados en USA y los que no.
• Price-Origin: Los precios de los vehículos que no son fabricados por empresas con origen USA parecen
ser más caros.
• MPG.city-Origin: Los vehículos fabricados por empresas con origen fuera de USA parecen que consumen
más en ciudad.
• Fuel.tank.capacity-Origin: Los vehículos fabricados por empresas de USA pueden ser de mayor capacidad
en el tanque de combustible, según el gráfico.
• Passenger-Origin: Los vehículos fabricados por compañías de USA, podemos interpretar que son para
más pasajeros que los de fuera de USA.
• Weight-Origin: Los vehículos de USA son más pesados, según el gráfico.

5.4. Visualización Variables Cualitativas.


Estudiamos la distribución de las variables categóricas en función de la variable dependiente. Como en el
caso de las variables continuas nos puede dar una idea si hay diferencias entre los dos niveles de la variable
dependiente.
var_cat <- c("AirBags","DriveTrain","Cylinders")
for (i in var_cat) {
barplot(table(Cars93$Origin, Cars93[,i]), col = c("red","green"),xlab = i)
legend("topright",levels(Cars93$Origin), fill = c("red","green"))
title(main = paste("Distribución",i, "~ Origin") )
}

21
Distribución AirBags ~ Origin
40

USA
non−USA
30
20
10
0

Driver & Passenger Driver only None

AirBags

22
Distribución DriveTrain ~ Origin

USA
60

non−USA
50
40
30
20
10
0

4WD Front Rear

DriveTrain

23
Distribución Cylinders ~ Origin

USA
non−USA
40
30
20
10
0

3 4 5 6 8 rotary

Cylinders

Podemos decir sobre el estudio de las variables categóricas que:


• AirBags-Origin: No hay gran diferencia entre los vehículos fabricados por compañías de USA y los que
no. En principio no sería una variable que nos pudiera influir en el modelo.
• Distribucion DriveTrain-Origin: No hay diferencia entre los distintos tipos de tracción de las ruedas.
• Cylinders-Origin: Se observa diferencias. Los vehículos fabricados por empresas de origen fuera de USA
tiene menos cilindros que los que están fabricados por empresas de USA.

5.5. Correlación entre variables


Cuando nos enfrentamos con un conjunto de datos tenemos que analizar las correlaciones entre variables
numéricas. Buscamos que nuestras variables predictoras tengan una alta correlación positiva (+1) o negativa
(-1) con la variable a predecir, pero no queremos que exista alta correlación entre ellas mismas, ya que
aportarían información redundante. Cuando dos variables predictoras están altamente correlacionadas
debemos eliminar una de ellas.
Utilizamos la librería corrplot que nos permite realizar un gráfico con la correlación entre variables. Utilizamos
el conjunto de datos de Cars93.
#install.packages("corrplot")
library(corrplot)

Seleccionamos las variables continuas de nuestro conjunto de datos y calculamos la correlación por medio de
la función cor().
cars93_vc<- Cars93[,c("Min.Price","Price","Max.Price","MPG.city",
"MPG.highway","EngineSize","Horsepower","RPM")]

24
(cars93_corr <- cor(cars93_vc))

## Min.Price Price Max.Price MPG.city MPG.highway


## Min.Price 1.00000000 0.970601402 0.90675608 -0.6228754 -0.5799658
## Price 0.97060140 1.000000000 0.98158027 -0.5945622 -0.5606804
## Max.Price 0.90675608 0.981580272 1.00000000 -0.5478109 -0.5225607
## MPG.city -0.62287544 -0.594562163 -0.54781090 1.0000000 0.9439358
## MPG.highway -0.57996581 -0.560680362 -0.52256074 0.9439358 1.0000000
## EngineSize 0.64548767 0.597425392 0.53501197 -0.7100032 -0.6267946
## Horsepower 0.80244412 0.788217578 0.74444475 -0.6726362 -0.6190437
## RPM -0.04259816 -0.004954931 0.02501478 0.3630451 0.3134687
## EngineSize Horsepower RPM
## Min.Price 0.6454877 0.80244412 -0.042598158
## Price 0.5974254 0.78821758 -0.004954931
## Max.Price 0.5350120 0.74444475 0.025014782
## MPG.city -0.7100032 -0.67263615 0.363045129
## MPG.highway -0.6267946 -0.61904368 0.313468728
## EngineSize 1.0000000 0.73211973 -0.547897805
## Horsepower 0.7321197 1.00000000 0.036688212
## RPM -0.5478978 0.03668821 1.000000000
Obtenemos el gráfico de las correlaciones donde los círculos azules indican correlación positiva, los círculos
rojos correlación negativa. Según la intensidad del color la correlación es más fuerte o más débil. En la
diagonal se sitúa la correlación de la variable consigo misma.
corrplot(cars93_corr) MPG.highway

Horsepower
EngineSize
Max.Price
Min.Price

MPG.city
Price

RPM
1
Min.Price
0.8

Price 0.6

Max.Price 0.4

0.2
MPG.city
0
MPG.highway
−0.2

EngineSize −0.4

Horsepower −0.6

−0.8
RPM
−1

25
Si nuestra variable a predecir es Origin, del estudio de la correlación de las variables seleccionadas continuas
podemos decir que hay una alta correlación entre Price-Min.Price, Price-Max.Price del 0.97 y 0.98 por lo que
podemos eliminar alguna de ellas. En este caso eliminaríamos Min.Price y Max.Price, ya que la información
que aportan para predecir la variable Origin ya está recogida en la variable Price.

26
5.6. Introducción a ggplot2.
En R podemos hacer gráficos con múltiples librerías, pero las más utilizadas son los gráficos con las librerías
precargadas de R y con la librería ggplot2 perteneciente a tidyverse. Anteriormente, hemos visto como
realizar un Análisis Exploratorio de Datos con la librería base de R. En este apartado vamos a ver cómo
realizar esos mismos gráficos, pero con la librería ggplot.
La interpretación de los gráficos es la misma, ya que realizamos los mismos gráficos, pero de una forma y con
una librería diferente, pero que nos realizándolos de una forma más elegante.
La función básica para crear gráficos en ggplot(), donde tenemos que indicar el conjunto de datos y las
diferentes variables a representar.
La gramática de ggplot2 se compone de una serie de capas, que se añaden a la función básica, cuyos elementos
visuales se llaman geoms donde podemos añadir apariencias estéticas. Así podemos realizar los siguientes
gráficos:
• geom_bar (): Crea un gráfico de barras.
• geom_point (): crea un diagrama de dispersión.
• geom_line (): crea un gráfico de línea.
• geom_histogram (): crea un histograma.
• geom_boxplot (): crea un gráfico de cajas.
La principal estructura de los gráficos en ggplot es la siguiente:
Grafico <- ggplot(datos, aes(X,Y))+geom()
Con esta estructura se realizan la mayoría de los gráficos.
Utilizamos el mismo dataset de la librería MASS llamado “Cars93”, el cual contiene 93 coches en venta en
EEUU en 1993 y 27 variables como origen del coche, precio, mínimo y máximo precio, número de pasajeros,
capacidad del depósito, etc. Hay tanto variables cualitativas como cuantitativas.
Cargamos las librerías, obtenemos las dimensiones del dataset y la estructura básica de los datos.
#Cargamos las librerías
library(ggplot2)
library(dplyr)
library(MASS)
#Cargamos los datos.
data("Cars93")

Visualización de la Variable Dependiente:


Suponemos que queremos conocer si un coche está fabricado por empresas cuyos orígenes son de USA o no,
por lo que nuestra variable dependiente sería Origin que puede tomar dos valores “USA” o “non-USA”, por
lo que estaríamos ante una variable cualitativa nominal, ya que no tiene un orden.
Estudiamos como se distribuye nuestra variable dependiente por medio de la librería ggplot2.
ggplot(data = Cars93, aes(x = Origin, fill = Origin)) + #Función Básica.
geom_bar() + #Tipo de gráfico.
labs(title = "Disribución Variable Origin") + #Título del Gráfico.
theme_classic() #Aspecto visual.

27
Disribución Variable Origin
50

40

30
Origin
count

USA

20 non−USA

10

USA non−USA
Origin

Visualización Variables Cuantitativas:


También podemos realizar los gráficos de las variables cuantitativas en función de la variable dependiente
con la capa geom_density. Realizamos un ejemplo con la variable continua Price en función de la variable
dependiente Origin.
ggplot(data= Cars93, aes(x= Price, fill = Origin)) +
geom_density() +
labs(title = "Distribución de la variable Precio en función de Origen")

28
Distribución de la variable Precio en función de Origen

0.06

0.04 Origin
density

USA
non−USA

0.02

0.00

20 40 60
Price

Visualización Variables Cualitativas:


Por último, vamos a ver como representar una variable categórica en función de una variable dependiente
cualitativa. Realizamos un ejemplo con la variable AirBags en función de la variable Origin. En este caso
tenemos que utilizar la capa geom_bar().
ggplot(data = Cars93, aes(x = AirBags, fill = Origin)) +
geom_bar() +
labs(title = "Distibución de la variable AirBags en función de Origin")

29
Distibución de la variable AirBags en función de Origin

40

30

Origin
count

USA
20
non−USA

10

Driver & Passenger Driver only None


AirBags

Variable dependiente Regresión: Simetría.


Si queremos realizar el estudio de la asimetría de una variable continua, ya sea dependiente o no, podemos
realizarlo gráficamente con ggplot con la capa geom_density(). Realizamos un ejemplo con la variable precio.
ggplot(data = Cars93, aes(x = Price)) +
geom_histogram(fill = "steelblue", colour = "black")

30
12.5

10.0

7.5
count

5.0

2.5

0.0

20 40 60
Price

labs(title = "Distribución de la variable Precio en función de Origen")

## $title
## [1] "Distribución de la variable Precio en función de Origen"
##
## attr(,"class")
## [1] "labels"

31
6. Preprocesado de los datos.
Antes de empezar a modelar debemos realizar una serie de transformaciones en los datos para que nuestros
algoritmos se puedan utilizar para aprender o para que con este conjunto de modificaciones se mejore la
precisión del modelo. Los principales procesos que debemos realizar a nuestros datos, antes de modelar, y
que veremos con ejemplos son:
• Imputación de valores ausentes.
• Centrado - Escalado de variables numéricas.
• Variables Dummys.
• Variables con varianza cero.
• División del conjunto de datos..

6.1. Imputación de valores Ausentes.


Uno de los problemas que nos encontramos al enfrentarnos con un conjunto de datos es el tratamiento
de valores ausentes, es decir, observaciones que no tienen un valor en todas o en algunas de las variables
independientes. El tratamiento de los valores ausentes se puede realizar de diversas formas como son:
• Si hay muchas observaciones podemos decidir eliminar aquellas observaciones que tienen valores perdidos.
• Si hay una variable predictora que es la que acumula más valores ausentes podemos decidir eliminarla.
El número de valores ausentes para eliminar dicha variable es muy relativo, pero se suele eliminar
cuando el 50% de los valores están ausentes.
• Se pueden completar los datos.
Utilizamos un dataset incluido en la librería mice que se llama boys y que tiene 748 observaciones y 9 variables
sobre el peso, altura, años, índice de masa corporal, etc. de los niños alemanes.
Instalamos la librería y la cargamos, obtenemos sus dimensiones y la estructura de los datos.
#Instalamos la librería y la cargamos.
#install.packages("mice")
library(mice)
#Cargamos los datos.
data("boys")
#Dimensiones del conjunto de datos.
dim(boys)

## [1] 748 9
#Estructura de los datos
str(boys)

## 'data.frame': 748 obs. of 9 variables:


## $ age: num 0.035 0.038 0.057 0.06 0.062 0.068 0.068 0.071 0.071 0.073 ...
## $ hgt: num 50.1 53.5 50 54.5 57.5 55.5 52.5 53 55.1 54.5 ...
## $ wgt: num 3.65 3.37 3.14 4.27 5.03 ...
## $ bmi: num 14.5 11.8 12.6 14.4 15.2 ...
## $ hc : num 33.7 35 35.2 36.7 37.3 37 34.9 35.8 36.8 38 ...
## $ gen: Ord.factor w/ 5 levels "G1"<"G2"<"G3"<..: NA NA NA NA NA NA NA NA NA NA ...
## $ phb: Ord.factor w/ 6 levels "P1"<"P2"<"P3"<..: NA NA NA NA NA NA NA NA NA NA ...
## $ tv : int NA NA NA NA NA NA NA NA NA NA ...
## $ reg: Factor w/ 5 levels "north","east",..: 4 4 4 4 4 4 4 3 3 2 ...
Con la función summary() nos indica, junto con otras medidas estadísticas, el número de valores perdidos
que tiene cada una de las variables.
summary(boys)

32
## age hgt wgt bmi
## Min. : 0.035 Min. : 50.00 Min. : 3.14 Min. :11.77
## 1st Qu.: 1.581 1st Qu.: 84.88 1st Qu.: 11.70 1st Qu.:15.90
## Median :10.505 Median :147.30 Median : 34.65 Median :17.45
## Mean : 9.159 Mean :132.15 Mean : 37.15 Mean :18.07
## 3rd Qu.:15.267 3rd Qu.:175.22 3rd Qu.: 59.58 3rd Qu.:19.53
## Max. :21.177 Max. :198.00 Max. :117.40 Max. :31.74
## NA's :20 NA's :4 NA's :21
## hc gen phb tv reg
## Min. :33.70 G1 : 56 P1 : 63 Min. : 1.00 north: 81
## 1st Qu.:48.12 G2 : 50 P2 : 40 1st Qu.: 4.00 east :161
## Median :53.00 G3 : 22 P3 : 19 Median :12.00 west :239
## Mean :51.51 G4 : 42 P4 : 32 Mean :11.89 south:191
## 3rd Qu.:56.00 G5 : 75 P5 : 50 3rd Qu.:20.00 city : 73
## Max. :65.00 NA's:503 P6 : 41 Max. :25.00 NA's : 3
## NA's :46 NA's:503 NA's :522
Como vemos todas las variables tienen valores perdidos, excepto la variable age. Lo primero que vamos a hacer
es eliminar las variables phb, gen y tv, ya que poseen valores faltantes en 503 y 522 valores respectivamente
(tienen más del 50% de valores perdidos).
boys$gen <- NULL
boys$phb <- NULL
boys$tv <- NULL

Nos quedamos con 6 variables. Las variables wgt y reg tienen pocos valores perdidos. Con la variable wgt
utilizamos la media y con la variable reg con el valor más frecuente.
#Imputamos con la media.
boys$wgt[is.na(boys$wgt)] <- mean(boys$wgt, na.rm = T)
#Imputamos con el valor más frecuente: west.
boys$reg[is.na(boys$reg)] <- "west"
#Comprobamos que no nos quedan datos faltantes en las dos variables.
anyNA(boys$wgt)

## [1] FALSE
anyNA(boys$reg)

## [1] FALSE
Comprobamos como se encuentra ahora mismo nuestro conjunto de datos.
summary(boys)

## age hgt wgt bmi


## Min. : 0.035 Min. : 50.00 Min. : 3.14 Min. :11.77
## 1st Qu.: 1.581 1st Qu.: 84.88 1st Qu.: 11.85 1st Qu.:15.90
## Median :10.505 Median :147.30 Median : 34.75 Median :17.45
## Mean : 9.159 Mean :132.15 Mean : 37.15 Mean :18.07
## 3rd Qu.:15.267 3rd Qu.:175.22 3rd Qu.: 59.35 3rd Qu.:19.53
## Max. :21.177 Max. :198.00 Max. :117.40 Max. :31.74
## NA's :20 NA's :21
## hc reg
## Min. :33.70 north: 81
## 1st Qu.:48.12 east :161
## Median :53.00 west :242
## Mean :51.51 south:191

33
## 3rd Qu.:56.00 city : 73
## Max. :65.00
## NA's :46
Después de las diferentes técnicas todavía nos quedan tres variables con valores perdidos que son: hgt, bmi y
hc. Con estas tres variables vamos a utilizar la librería caret para imputar los valores perdidos de una forma
diferente con la función preprocess().
Imputamos los valores perdidos con el parámetro method que puede tomar dos valores knnImpute y bagImpute.
El primer método utiliza k-vecinos más cercanos, centrado y escalado los datos. El segundo método con
árboles de decisión y en este caso no transforma las variables.
#Cargamos la librería.
library(caret)
#Imputamos los datos ausentes restantes.
pre_im = preProcess(boys, method = "bagImpute")
boys <- predict(pre_im, boys)
summary(boys)

## age hgt wgt bmi


## Min. : 0.035 Min. : 50.0 Min. : 3.14 Min. :11.77
## 1st Qu.: 1.581 1st Qu.: 85.5 1st Qu.: 11.85 1st Qu.:15.93
## Median :10.505 Median :145.8 Median : 34.75 Median :17.37
## Mean : 9.159 Mean :131.3 Mean : 37.15 Mean :18.03
## 3rd Qu.:15.267 3rd Qu.:174.8 3rd Qu.: 59.35 3rd Qu.:19.44
## Max. :21.177 Max. :198.0 Max. :117.40 Max. :31.74
## hc reg
## Min. :33.70 north: 81
## 1st Qu.:48.27 east :161
## Median :53.35 west :242
## Mean :51.64 south:191
## 3rd Qu.:56.00 city : 73
## Max. :65.00
Nuestro conjunto de datos ahora no tiene valores ausentes. Hemos visto diferentes formas de imputar los
valores ausentes para ver cómo se haría. Cuando nos enfrentamos con datos reales debemos probar los
distintos métodos y quedarnos con aquel que mejore los resultados.

6.2. Centrado - Escalado de Variables Continuas.


Las variables continuas suelen tener distintas magnitudes, unidades y rango, para algunos algoritmos esto
supone un problema ya que las variables con magnitudes altas pesarán mucho más en los cálculos que aquellas
variables con magnitudes bajas. Debemos llevar al mismo nivel todas las variables. Utilizamos el paquete
caret para centrar y estandarizar las variables.
summary(boys)

## age hgt wgt bmi


## Min. : 0.035 Min. : 50.0 Min. : 3.14 Min. :11.77
## 1st Qu.: 1.581 1st Qu.: 85.5 1st Qu.: 11.85 1st Qu.:15.93
## Median :10.505 Median :145.8 Median : 34.75 Median :17.37
## Mean : 9.159 Mean :131.3 Mean : 37.15 Mean :18.03
## 3rd Qu.:15.267 3rd Qu.:174.8 3rd Qu.: 59.35 3rd Qu.:19.44
## Max. :21.177 Max. :198.0 Max. :117.40 Max. :31.74
## hc reg
## Min. :33.70 north: 81
## 1st Qu.:48.27 east :161

34
## Median :53.35 west :242
## Mean :51.64 south:191
## 3rd Qu.:56.00 city : 73
## Max. :65.00
Utilizamos la función preprocess() de la librería caret para centrar y escalar, en el conjunto de datos de
entrenamiento y validación. La función identifica las variables factor no realizando ninguna transformación
en ellas.
#Preprocesado centrado y escalado.
pre_cs <- preProcess(boys,method = c("center","scale"))
boys <- predict(pre_cs, newdata = boys)
summary(boys)

## age hgt wgt bmi


## Min. :-1.3234 Min. :-1.7561 Min. :-1.31004 Min. :-2.0706
## 1st Qu.:-1.0992 1st Qu.:-0.9892 1st Qu.:-0.97457 1st Qu.:-0.6947
## Median : 0.1952 Median : 0.3144 Median :-0.09256 Median :-0.2174
## Mean : 0.0000 Mean : 0.0000 Mean : 0.00000 Mean : 0.0000
## 3rd Qu.: 0.8859 3rd Qu.: 0.9408 3rd Qu.: 0.85492 3rd Qu.: 0.4677
## Max. : 1.7433 Max. : 1.4409 Max. : 3.09076 Max. : 4.5382
## hc reg
## Min. :-3.0528 north: 81
## 1st Qu.:-0.5731 east :161
## Median : 0.2903 west :242
## Mean : 0.0000 south:191
## 3rd Qu.: 0.7412 city : 73
## Max. : 2.2724
Los árboles de decisión no son sensibles a las distintas escalas y pueden manejar variables de diferentes rangos,
pero para utilizar gradientes (GBM) el escalado es fundamental.

6.3. Variables Dummys


El tratamiento de las variables categóricas puede ayudarnos a que nuestro algoritmo (no todos) aprenda
de una forma más efectiva. Una de las formas que tenemos de tratarlas es por medio de la binarización de
estas variables. La creación de variables dummys consiste en crear tantas variables como niveles tenga la
variable categórica, asignando ceros a todas las observaciones excepto a las observaciones que tienen el nivel
que representa la nueva variable creada que se le asigna 1. Para ayudarnos a crear estas variables ficticias
vamos a utilizar también la librería caret.
Seguimos Utilizando nuestro ejemplo de boys creando variables dummys para todas las variables categóricas
del conjunto de datos con la función dummyVars(), que transformara todas las variables en factor en binarias.
Si queremos excluir una variable que no queremos transformar, pondremos su nombre en la parte de la
formula ~ . con un menos delante.
#Variables Dummys.
dummy <- dummyVars(~ ., data = boys)
#Predecimos sobre el conjunto de datos.
boys <-as.data.frame(predict(dummy, boys))
summary(boys)

## age hgt wgt bmi


## Min. :-1.3234 Min. :-1.7561 Min. :-1.31004 Min. :-2.0706
## 1st Qu.:-1.0992 1st Qu.:-0.9892 1st Qu.:-0.97457 1st Qu.:-0.6947
## Median : 0.1952 Median : 0.3144 Median :-0.09256 Median :-0.2174
## Mean : 0.0000 Mean : 0.0000 Mean : 0.00000 Mean : 0.0000

35
## 3rd Qu.: 0.8859 3rd Qu.: 0.9408 3rd Qu.: 0.85492 3rd Qu.: 0.4677
## Max. : 1.7433 Max. : 1.4409 Max. : 3.09076 Max. : 4.5382
## hc reg.north reg.east reg.west
## Min. :-3.0528 Min. :0.0000 Min. :0.0000 Min. :0.0000
## 1st Qu.:-0.5731 1st Qu.:0.0000 1st Qu.:0.0000 1st Qu.:0.0000
## Median : 0.2903 Median :0.0000 Median :0.0000 Median :0.0000
## Mean : 0.0000 Mean :0.1083 Mean :0.2152 Mean :0.3235
## 3rd Qu.: 0.7412 3rd Qu.:0.0000 3rd Qu.:0.0000 3rd Qu.:1.0000
## Max. : 2.2724 Max. :1.0000 Max. :1.0000 Max. :1.0000
## reg.south reg.city
## Min. :0.0000 Min. :0.00000
## 1st Qu.:0.0000 1st Qu.:0.00000
## Median :0.0000 Median :0.00000
## Mean :0.2553 Mean :0.09759
## 3rd Qu.:1.0000 3rd Qu.:0.00000
## Max. :1.0000 Max. :1.00000
Como teníamos 5 niveles en la variable reg nos ha creado 5 variables, una por cada nivel, cuyo nombre es la
variable original concatenada con un “.” con el nombre del nivel. En las observaciones se les ha asignado un
cero si no coincide con el nivel y un 1 si coincide. Hemos transformado el conjunto de datos resultante a
dataframe con la función as.data.frame().
Nuestro conjunto de datos ahora tiene 10 variables, las variables numéricas están centradas y escaladas y las
variables factor se han transformado en variables binarias.

6.4. Variables con Varianza Cero.


Cuando nos enfrentamos con un conjunto de datos con el que vamos a entrenar un modelo es necesario
eliminar aquellas variables con varianza cero o próximas a cero, es decir, debemos eliminar aquellas variables
que sus valores en la mayoría de los casos es el mismo o presentan baja variabilidad, valores muy similares,
aunque no iguales, ya que no aportan información. Tampoco se deben incluir variables que tiene valores con
muy poca frecuencia y que pueden dar problemas al realizar validación cruzada. Para ello podemos utilizar el
paquete caret que por medio de la función nearZeroVar() nos indica que variables pueden ser problemáticas.
Utilizamos el conjunto de datos de boys sobre los niños alemanes, para comprobar si alguna de las variables
puede ser problemática.
#Variables problemáticas
nearZeroVar(boys,saveMetrics = T)

## freqRatio percentUnique zeroVar nzv


## age 1.000000 91.3101604 FALSE FALSE
## hgt 2.166667 65.1069519 FALSE FALSE
## wgt 1.000000 70.0534759 FALSE FALSE
## bmi 2.800000 67.9144385 FALSE FALSE
## hc 1.062500 30.4812834 FALSE FALSE
## reg.north 8.234568 0.2673797 FALSE FALSE
## reg.east 3.645963 0.2673797 FALSE FALSE
## reg.west 2.090909 0.2673797 FALSE FALSE
## reg.south 2.916230 0.2673797 FALSE FALSE
## reg.city 9.246575 0.2673797 FALSE FALSE
Nos indica el percentUnique que son el número de datos únicos del total de datos, zeroVar nos indica si solo
tiene un valor distinto y nzv que nos indica si el predictor tiene varianza cercana a cero. Nuestros datos no
tienen ninguna variable cercana o con varianza cero que debamos eliminar.

36
6.5. División del conjunto de datos.
Cuando nos enfrentamos con un conjunto de datos que queremos modelizar, uno de los pasos que tenemos
que realizar es la división de los datos, en un conjunto de entrenamiento con el que se entrena el modelo, y
un conjunto de validación que nos sirve para ver cómo se comporta el modelo que hemos entrenado ante
nuevos datos. El paquete caret también nos facilita esta división con la función createDataPartition() que
nos asegura una división manteniendo la proporción de la variable dependiente aunque no se garantiza que
las variables independientes se distribuyan de una forma similar en los dos conjuntos de datos.
Utilizamos el conjunto de datos boys, utilizando como variable dependiente o a predecir age (podemos tomar
cualquier variable que queramos predecir).
dim(boys)

## [1] 748 10
Nuestro conjunto de datos tiene 748 observaciones y 10 variables. Creamos dos conjuntos de datos donde el
70% (p = 0.70) de las observaciones vayan al conjunto de entrenamiento y el 30% restante al conjunto de
validación.
#Dividimos el conjunto de datos
index <- createDataPartition(boys$age, p = 0.70,list = FALSE)
train <- boys[index,]
test <- boys[-index,]
#Dimensiones
dim(train)

## [1] 524 10
dim(test)

## [1] 224 10
Ahora tenemos dos conjuntos de datos, un conjunto de entrenamiento denominado train que tiene 524
observaciones y 10 variables, y un conjunto de validación con 224 observaciones y 10 observaciones.

37
Anexo 1. Variable Dependiente Clasificación: Balanceo de clases.
Cuando nos enfrentamos un problema de clasificación la mayoría de las veces la variable a predecir no está
balanceada, es decir, uno o varias de las clases son minoritarias frente al resto de las clases, lo que puede
hacer que nuestro algoritmo no obtenga la información necesaria sobre esa clase o clases minoritarias.
Existen métodos que corrigen este desequilibrio entre las distintas clases, modificando el tamaño original del
conjunto de datos y proporcionando un equilibrio entre clases.
Entre los más importantes se encuentran:
• Submuestreo: Se basa en reducir la clase mayoritaria, reduciendo las observaciones del conjunto de
datos para que sea equilibrado. Esta eliminación de clases puede hacerse de manera aleatoria o con un
criterio de selección preestablecido (EasyEnsemble y BalanceCascade).
• Sobremuestreo: Se basa en aumentar la clase minoritaria, replicando las observaciones de dicha clase
y aumentando los datos hasta que el conjunto de datos este balanceado. Puede utilizar un criterio
aleatorio o preestablecido.
• Sintético: Crea datos artificiales utilizando bootstrapping y k-vecinos más cercanos. Se puede considerar
un método de sobremuestreo.
• Matriz de costos: Se basa en el costo asociado con las observaciones de clasificación equivocada o
errónea. No crea una distribución de datos balanceada.
Para realizar estos métodos de balanceo en R, utilizamos el paquete ROSE que nos ayuda a equilibrar los
datos desequilibrados.
#install.packages("ROSE")
library(ROSE)

Desarrollamos nuestro ejemplo con un dataset sobre la detección del cáncer de mama, donde tenemos la
variable diagnosis que puede tomar dos valores, diagnosis = M que indica que tiene cáncer de mama o
diagnosis = B que indica que el tumor es benigno. Transformamos la variable en factor, compuesta con
ceros y unos. Los algoritmos con los que modelamos tienen menos problemas con variables numéricas en su
tratamiento, por lo que ayudamos en su aprendizaje con su modificación.
#Cargamos los datos.
cancer <- read.csv("DATASETS/data.csv")[,1:8]
#Obtenemos las dimensiones
dim(cancer)

## [1] 569 8
#Transformamos la variable objetivo en factor
cancer$diagnosis <- as.factor(cancer$diagnosis)
levels(cancer$diagnosis) <- c(0,1)

Tenemos un dataset compuesto por 569 observaciones y 8 columnas, con variables tomadas a partir de
imágenes digitalizadas. Si queremos más información sobre el conjunto de datos podemos ir a kaggle
Realizamos gráfico de la variable dependiente para ver cómo se distribuye.
barplot(table(cancer$diagnosis),
xlab = "Clases de diagnosis",
ylab ="Frequencia",
main="Frecuencia de la Variable Diagnosis",
col = c("red","blue"))

38
Frecuencia de la Variable Diagnosis
350
250
Frequencia

150
50
0

0 1

Clases de diagnosis

Como vemos en el gráfico la variable no está equilibrada. La clase 0 o benigno tiene más casos que la clase 1
o maligno. Si quisiéramos saber cuántos casos tenemos de cada una de las clases lo hacemos con la función
table() que nos indica que tenemos 357 casos benignos y 212 casos malignos.
table(cancer$diagnosis)

##
## 0 1
## 357 212
Aunque nuestro dataset es pequeño vamos a dividir el conjunto de datos en entrenamiento (80%) y validación
(20%).
index <- sample(1:nrow(cancer), size = 0.8 * nrow(cancer))
train_can <- cancer[index,]
test_can <- cancer[-index,]

Calculamos el número de casos de cada clase en train.


table(train_can$diagnosis)

##
## 0 1
## 294 161
Realizamos un árbol de decisión sobre nuestro conjunto de datos de entrenamiento y predecimos sobre el
conjunto de datos de validación. No nos preocupamos por como se crea el modelo porque lo veremos en
el tema 2 del curso, sino en los resultados y como le afectan los distintos datos obtenidos con los distintos
métodos.

39
#Creamos el modelo.
library(rpart)
model_can1 <- rpart(diagnosis~.,data = train_can)
#Predecimos con el modelo sobre el conjunto de validación.
predict_can1 <- predict(model_can1, test_can, type ="class")
#Obtenemos matriz de confusión
table(predict_can1, test_can$diagnosis)

##
## predict_can1 0 1
## 0 61 5
## 1 2 46
Nuestra tasa de acierto es de 92.98%. Una tasa de error no muy elevada con 8 casos erróneos.
Vamos a equilibrar nuestros datos con las distintos métodos que nos ofrece el paquete Rose por medio de la
función ovun.sample(). Para que siempre obtengamos los mismos resultados y sea reproducible tomamos la
semilla seed = 1979.
1.Submuestreo: Se realiza disminuyendo los casos de la clase mayoritaria. Utilizamos el parámetro N, donde
se indica el número de la clase minoritaria por 2.
cancer_balanc_sub <- ovun.sample(diagnosis~., #Formula
data = train_can, #Datos
method = "under", #Método a utilizar
seed = 1979, #Semilla
N = 322)$data #Número de la clase minoritaria x2
table(cancer_balanc_sub$diagnosis)

##
## 0 1
## 161 161
2.Sobremuestreo: Se realiza aumentando los casos de la clase minoritaria.
cancer_balanc_over <- ovun.sample(diagnosis~.,
data = train_can,
seed = 1979,
method = "over")$data
table(cancer_balanc_over$diagnosis)

##
## 0 1
## 294 281
3.Submuestreo y Sobremuestreo: Se realizan sobremuestreo sobre la clase minoritaria y la clase mayoritaria
se realiza submuestreo.
cancer_balanc_ambos <- ovun.sample(diagnosis~.,
data = train_can,
method = "both",
seed = 1979,
p = 0.5)$data #Probabilidad de la clase positiva en la
#nueva muestra.
table(cancer_balanc_ambos$diagnosis)

##
## 0 1

40
## 235 220
4.Sintéticos: Crea datos artificiales proporcionando una mejor estimación de los datos originales. El conjunto
de datos resultante tiene el mismo número de observaciones que el dataset original.
cancer_balanc_rose <- ROSE(diagnosis~.,
data = train_can,
seed = 1979)$data

table(cancer_balanc_ambos$diagnosis)

##
## 0 1
## 235 220
Creamos los modelos predecimos con cada uno de los conjuntos de datos que hemos creado.
#Submuestreo.
model_sub <- rpart(diagnosis ~., data = cancer_balanc_sub)
predict_sub <- predict(model_sub, newdata = test_can,type = "class")
#Sobremuestreo
model_over <- rpart(diagnosis ~., data = cancer_balanc_over)
predict_over <- predict(model_over, newdata = test_can, type = "class")
#Submuestreo y Sobremuestreo
model_ambos <- rpart(diagnosis ~., data = cancer_balanc_ambos)
predict_ambos <- predict(model_ambos, newdata = test_can,type = "class")
#Sitético
model_rose <- rpart(diagnosis ~., data = cancer_balanc_rose)
predict_rose <- predict(model_rose, newdata = test_can,type = "class")

Obtenemos la precisión de nuestros modelos con el estadístico AUC, que en el tema 2 explicaremos, pero que
nos quedamos con la idea, que varía entre 0 y 1, siendo mejor cuanto más cercano a 1.
#AUC Original.
roc.curve(test_can$diagnosis, predict_can1,plotit = F)

## Area under the curve (AUC): 0.935


#AUC Submuestreo.
roc.curve(test_can$diagnosis, predict_sub,plotit = F)

## Area under the curve (AUC): 0.933


#AUC Sobremuestreo.
roc.curve(test_can$diagnosis, predict_over,plotit = F)

## Area under the curve (AUC): 0.929


#AUC Submuestreo y Sobremuestreo.
roc.curve(test_can$diagnosis, predict_ambos,plotit = F)

## Area under the curve (AUC): 0.905


#AUC sitéticos
roc.curve(test_can$diagnosis, predict_rose,plotit = F)

## Area under the curve (AUC): 0.913


En esta partición el modelo que obtiene un mayor AUC es el que se realiza con los datos originales. Se debería
realizar múltiples simulaciones con distintas semillas para elegir el tipo de balanceo que obtiene el mayor
AUC.

41
Anexo 2. Variable dependiente Regresión: Simetría.
Si nos enfrentamos a un problema de regresión, debemos analizar la variable dependiente para ver si sigue
una distribución normal o no, es decir, debemos estudiar la asimetría. El estudio de la simetría nos permite
saber cómo se distribuyen los datos con respecto a un valor central (media), pudiendo ser simétrica entorno a
la media, asimétrica a la izquierda con cola a hacia la izquierda o asimétrica hacia la derecha con cola hacia
la derecha. Realizar predicciones sobre una variable que se distribuye normalmente será más preciso que si
realizamos predicciones sobre una variable asimétrica.
Volvemos a utilizar el dataset de la librería MASS llamado “Cars93”, el cual contiene 93 coches en venta en
EEUU en 1993 y 27 variables. Si nuestra variable dependiente es Price, vamos a ver como se distribuye por
medio de un histograma.
#Estadísticos Básicos
summary(Cars93$Price)

## Min. 1st Qu. Median Mean 3rd Qu. Max.


## 7.40 12.20 17.70 19.51 23.30 61.90
#Histograma
hist(Cars93$Price,
main = "Histograma de Price - Cars93",
xlab = "Price",
col = "Blue")
abline(v = mean(Cars93$Price), #Media de la variable price.
col = "Red", #Color
lwd = 5, #Grosor de la línea.
lty = 3) #Tipo de línea.

Histograma de Price − Cars93


30
25
20
Frequency

15
10
5
0

10 20 30 40 50 60

Price

42
Si observamos el gráfico vemos que no se distribuye en este caso de una forma normal y que existe asimetría.
Vamos a utilizar el paquete moments para calcular la asimetría de Fisher por medio de la función skewness.
El estadístico nos puede dar distintos valores:
• Mayor de 0: Asimetría positiva.
• Menor de 0: Asimetría negativa.
• 0: La variable se distribuye de forma simétrica.
#install.packages("moments")
library(moments)
skewness(Cars93$Price)

## [1] 1.508243
El resultado es mayor de 0 o positivo por lo tanto tenemos asimetría positiva, es decir, tenemos una cola
larga hacia la derecha como habíamos visto en el gráfico. Cuando tenemos asimetría, tanto a la izquierda
como a la derecha, debemos normalizar dicha distribución para que nuestras predicciones sean más fiables,
para ello podemos tomar el logaritmo de los datos.
#Logaritmo de Price
Cars93$Price <- log(Cars93$Price)
#Estadísticos básicos de Price
summary(Cars93$Price)

## Min. 1st Qu. Median Mean 3rd Qu. Max.


## 2.001 2.501 2.874 2.865 3.148 4.126
#Histograma
hist(Cars93$Price,
main = "Histograma de Price - Cars93",
xlab = "Price",
col = "Blue")
abline(v = mean(Cars93$Price), #Media de la variable price.
col = "Red", #Color
lwd = 5, #Grosor de la línea.
lty = 3) #Tipo de línea.

43
Histograma de Price − Cars93
15
Frequency

10
5
0

2.0 2.5 3.0 3.5 4.0

Price

La nueva variable ahora se distribuye de una forma normal con la media centrada sin grandes colas a los
lados. Utilizamos la variable transformada para entrenar nuestro modelo y por tanto sus predicciones se
distribuirán según la variable transformada, por lo que si queremos que al final se cuantifiquen como la
distribución original tenemos que tomar la exponencial exp() de la predicción.
Cars93$Price <- exp(Cars93$Price)
summary(Cars93$Price)

## Min. 1st Qu. Median Mean 3rd Qu. Max.


## 7.40 12.20 17.70 19.51 23.30 61.90
En este tema, hemos visto cómo utilizar la librería dplyr, un análisis exploratorio de nuestros datos y cómo
realizar un pre-procesado de datos para tenerlos listos para el modelo. En próximos temas veremos cómo
modelar con los distintos algoritmos basados en árboles de decisión.

44

También podría gustarte