Documentos de Académico
Documentos de Profesional
Documentos de Cultura
TEMA1-Preprocesado de Datos
TEMA1-Preprocesado de Datos
Contents
Introducción al Curso 2
1. Machine Learning 2
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.
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
## 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)
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)
7
De igual modo podemos utilizar ends_with() y contains() para elegir columnas que terminen o que contengan
un cierto patrón.
## 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)
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
## # 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
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.
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)
14
#Dimensiones del conjunto de datos.
dim(Cars93)
## [1] 93 27
#Estructura de los datos
str(Cars93)
##
## 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
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.
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)))
}
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
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.
21
Distribución AirBags ~ Origin
40
USA
non−USA
30
20
10
0
AirBags
22
Distribución DriveTrain ~ Origin
USA
60
non−USA
50
40
30
20
10
0
DriveTrain
23
Distribución Cylinders ~ Origin
USA
non−USA
40
30
20
10
0
3 4 5 6 8 rotary
Cylinders
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))
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")
27
Disribución Variable Origin
50
40
30
Origin
count
USA
20 non−USA
10
USA non−USA
Origin
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
29
Distibución de la variable AirBags en función de Origin
40
30
Origin
count
USA
20
non−USA
10
30
12.5
10.0
7.5
count
5.0
2.5
0.0
20 40 60
Price
## $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..
## [1] 748 9
#Estructura de los datos
str(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)
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)
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)
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.
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,]
##
## 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)
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)
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)
43
Histograma de Price − Cars93
15
Frequency
10
5
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)
44