Introducción

La Inteligencia Artificial (IA) ha transformado profundamente múltiples sectores, consolidándose como una herramienta esencial para enfrentar desafíos complejos y mejorar la eficiencia en diversas industrias. En el corazón de la IA están los algoritmos que capacitan a las máquinas para aprender de grandes cantidades de datos y tomar decisiones informadas. Cuando estos algoritmos se entrenan correctamente, pueden clasificar información, hacer predicciones precisas y descubrir patrones en escenarios donde los métodos tradicionales resultan ineficaces. No obstante, la efectividad de los modelos de IA depende en gran medida de la selección del algoritmo adecuado, la plataforma de desarrollo empleada y el tipo de problema que se busca resolver.

En este analisis estadistico descriptivo se estudiara una base de datos que contiene el desempeño de varios algoritmos de inteligencia artificial sobre un problema especifico, que contiene el desempeño, la precisión, tiempo de entrenamiento y otras caracteristicas correspondientes al proceso de cada algoritmo, caracteristicas que se compararan con el objetivo de resolver la pregunta problema planteada.

Pregunta Problema

En una base de datos la cual contiene el desempeño de distintos algoritmos de IA es inevitable comparar los distintos algoritmos con el fin de encontrar los mejores en ciertos aspectos, como por ejemplo trabajando con un tipo de problema especifico, ya que esta información podria facilitar la resolución de dicho problema, con este contexto el EDA se enfoca en resolver una pregunta propuesta por el equipo, sin tener en cuenta el frameWork, ¿cuál es el algoritmo que logra el mayor puntaje F1 con menor tiempo de entrenamiento en los problemas de regresión?

## 
## Adjuntando el paquete: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
## Cargando paquete requerido: Rcpp
## ## 
## ## Amelia II: Multiple Imputation
## ## (Version 1.8.2, built: 2024-04-10)
## ## Copyright (C) 2005-2024 James Honaker, Gary King and Matthew Blackwell
## ## Refer to http://gking.harvard.edu/amelia/ for more information
## ##
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ forcats   1.0.0     ✔ purrr     1.0.2
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
## 
## Adjuntando el paquete: 'gridExtra'
## 
## 
## The following object is masked from 'package:dplyr':
## 
##     combine
## 
## 
## 
## Adjuntando el paquete: 'mice'
## 
## 
## The following object is masked from 'package:stats':
## 
##     filter
## 
## 
## The following objects are masked from 'package:base':
## 
##     cbind, rbind
## 
## 
## Cargando paquete requerido: Matrix
## 
## 
## Adjuntando el paquete: 'Matrix'
## 
## 
## The following objects are masked from 'package:tidyr':
## 
##     expand, pack, unpack
## 
## 
## Cargando paquete requerido: stats4
## 
## mi (Version 1.1, packaged: 2022-06-05 05:31:15 UTC; ben)
## 
## mi  Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Trustees of Columbia University
## 
## This program comes with ABSOLUTELY NO WARRANTY.
## 
## This is free software, and you are welcome to redistribute it
## 
## under the General Public License version 2 or later.
## 
## Execute RShowDoc('COPYING') for details.
## 
## 
## Adjuntando el paquete: 'mi'
## 
## 
## The following objects are masked from 'package:mice':
## 
##     complete, pool
## 
## 
## The following object is masked from 'package:tidyr':
## 
##     complete

Analisis de variables

Para comenzar el analisis, es necesario identificar las caracteristicas basicas de la base de datos con la que estamos trabajando, empezando por la dimensión de la base de datos:

dim(datosReadr)
## [1] 560  10
head(datosReadr)
## # A tibble: 6 × 10
##   Algorithm      Framework   Problem_Type Dataset_Type Accuracy Precision Recall
##   <chr>          <chr>       <chr>        <chr>           <dbl>     <dbl>  <dbl>
## 1 SVM            Scikit-lea… Regression   Time Series     0.662     0.693 NA    
## 2 K-Means        Keras       Clustering   Time Series     0.744     0.490  0.877
## 3 Neural Network Keras       Clustering   Image           0.885     0.595  0.969
## 4 SVM            Keras       Clustering   Text            0.842     0.842  0.875
## 5 SVM            Scikit-lea… Regression   Tabular         0.723     0.686  0.301
## 6 K-Means        PyTorch     Regression   Image           0.637     0.626  7.45 
## # ℹ 3 more variables: F1_Score <dbl>, Training_Time <dbl>, Date <dttm>

Esta base esta compuesta por 560 filas y 10 columnas, usando la función head() también se puede observar una muestra inicial del contenido, la base de datos se compone de 560 entradas, cada una indicando un modelo diferente, y 10 variables.

Los nombres de las variables son las siguientes:

names(datosReadr)
##  [1] "Algorithm"     "Framework"     "Problem_Type"  "Dataset_Type" 
##  [5] "Accuracy"      "Precision"     "Recall"        "F1_Score"     
##  [9] "Training_Time" "Date"

El tipo de variable y ejemplos de los datos que estas reciben aparecen a continuacion:

glimpse(datosReadr)
## Rows: 560
## Columns: 10
## $ Algorithm     <chr> "SVM", "K-Means", "Neural Network", "SVM", "SVM", "K-Mea…
## $ Framework     <chr> "Scikit-learn", "Keras", "Keras", "Keras", "Scikit-learn…
## $ Problem_Type  <chr> "Regression", "Clustering", "Clustering", "Clustering", …
## $ Dataset_Type  <chr> "Time Series", "Time Series", "Image", "Text", "Tabular"…
## $ Accuracy      <dbl> 0.6618051, 0.7443216, 0.8852037, 0.8416477, 0.7229514, 0…
## $ Precision     <dbl> 0.6929447, 0.4900292, 0.5948056, 0.8424142, 0.6856109, 0…
## $ Recall        <dbl> NA, 0.8766533, 0.9685424, 0.8748388, 0.3010956, 7.454809…
## $ F1_Score      <dbl> 0.4426950, 0.4414046, 0.9644707, 0.7041523, 0.6456472, 0…
## $ Training_Time <dbl> 4.9785924, NA, 3.2825938, 4.0416289, 3.6039908, 3.006475…
## $ Date          <dttm> 2023-03-08 11:26:21, 2023-03-09 11:26:21, 2023-03-10 11…

Para falicitar el manejo de los datos categoricos, convertimos estos datos en factores:

datosReadr %>% mutate_if(is.character, as.factor);datosReadr %>% glimpse
## # A tibble: 560 × 10
##    Algorithm      Framework  Problem_Type Dataset_Type Accuracy Precision Recall
##    <fct>          <fct>      <fct>        <fct>           <dbl>     <dbl>  <dbl>
##  1 SVM            Scikit-le… Regression   Time Series     0.662     0.693 NA    
##  2 K-Means        Keras      Clustering   Time Series     0.744     0.490  0.877
##  3 Neural Network Keras      Clustering   Image           0.885     0.595  0.969
##  4 SVM            Keras      Clustering   Text            0.842     0.842  0.875
##  5 SVM            Scikit-le… Regression   Tabular         0.723     0.686  0.301
##  6 K-Means        PyTorch    Regression   Image           0.637     0.626  7.45 
##  7 Neural Network PyTorch    Regression   Text            0.999     0.637  0.336
##  8 Neural Network Scikit-le… Regression   Image           0.713     0.676  0.480
##  9 SVM            Keras      Regression   Time Series    NA         0.871  0.342
## 10 Random Forest  Keras      Regression   Text            0.582     0.935 NA    
## # ℹ 550 more rows
## # ℹ 3 more variables: F1_Score <dbl>, Training_Time <dbl>, Date <dttm>
## Rows: 560
## Columns: 10
## $ Algorithm     <chr> "SVM", "K-Means", "Neural Network", "SVM", "SVM", "K-Mea…
## $ Framework     <chr> "Scikit-learn", "Keras", "Keras", "Keras", "Scikit-learn…
## $ Problem_Type  <chr> "Regression", "Clustering", "Clustering", "Clustering", …
## $ Dataset_Type  <chr> "Time Series", "Time Series", "Image", "Text", "Tabular"…
## $ Accuracy      <dbl> 0.6618051, 0.7443216, 0.8852037, 0.8416477, 0.7229514, 0…
## $ Precision     <dbl> 0.6929447, 0.4900292, 0.5948056, 0.8424142, 0.6856109, 0…
## $ Recall        <dbl> NA, 0.8766533, 0.9685424, 0.8748388, 0.3010956, 7.454809…
## $ F1_Score      <dbl> 0.4426950, 0.4414046, 0.9644707, 0.7041523, 0.6456472, 0…
## $ Training_Time <dbl> 4.9785924, NA, 3.2825938, 4.0416289, 3.6039908, 3.006475…
## $ Date          <dttm> 2023-03-08 11:26:21, 2023-03-09 11:26:21, 2023-03-10 11…

La base de datos utiliza 10 variables, 5 siendo numericas (dbl) y 5 siendo categoricas (chr):

frecuencias <- table(datosReadr$Algorithm)
print(frecuencias)
## 
##        K-Means Neural Network  Random Forest            SVM 
##            163            135            126            136
tabla_frecuencia <- as.data.frame(frecuencias)
colnames(tabla_frecuencia) <- c("Algorithm", "Frecuencia")

tabla_frecuencia <- tabla_frecuencia %>%
  mutate(Frecuencia_Porcentual = Frecuencia / sum(Frecuencia) * 100)

tabla_frecuencia <- tabla_frecuencia %>%
  mutate(Frecuencia_Acumulada = cumsum(Frecuencia))

moda <- tabla_frecuencia$Categoria[which.max(tabla_frecuencia$Frecuencia)]

tabla_frecuencia <- tabla_frecuencia %>%
  mutate(Moda = moda)

print(tabla_frecuencia)
##        Algorithm Frecuencia Frecuencia_Porcentual Frecuencia_Acumulada
## 1        K-Means        163              29.10714                  163
## 2 Neural Network        135              24.10714                  298
## 3  Random Forest        126              22.50000                  424
## 4            SVM        136              24.28571                  560
frecuencias <- table(datosReadr$Framework)
print(frecuencias)
## 
##        Keras      PyTorch Scikit-learn   TensorFlow 
##          124          135          134          167
tabla_frecuencia <- as.data.frame(frecuencias)
colnames(tabla_frecuencia) <- c("Framework", "Frecuencia")

tabla_frecuencia <- tabla_frecuencia %>%
  mutate(Frecuencia_Porcentual = Frecuencia / sum(Frecuencia) * 100)

tabla_frecuencia <- tabla_frecuencia %>%
  mutate(Frecuencia_Acumulada = cumsum(Frecuencia))

moda <- tabla_frecuencia$Categoria[which.max(tabla_frecuencia$Frecuencia)]

tabla_frecuencia <- tabla_frecuencia %>%
  mutate(Moda = moda)

print(tabla_frecuencia)
##      Framework Frecuencia Frecuencia_Porcentual Frecuencia_Acumulada
## 1        Keras        124              22.14286                  124
## 2      PyTorch        135              24.10714                  259
## 3 Scikit-learn        134              23.92857                  393
## 4   TensorFlow        167              29.82143                  560
frecuencias <- table(datosReadr$Problem_Type)
print(frecuencias)
## 
## Classification     Clustering     Regression 
##            175            196            189
tabla_frecuencia <- as.data.frame(frecuencias)
colnames(tabla_frecuencia) <- c("Problem_Type", "Frecuencia")

tabla_frecuencia <- tabla_frecuencia %>%
  mutate(Frecuencia_Porcentual = Frecuencia / sum(Frecuencia) * 100)

tabla_frecuencia <- tabla_frecuencia %>%
  mutate(Frecuencia_Acumulada = cumsum(Frecuencia))

moda <- tabla_frecuencia$Categoria[which.max(tabla_frecuencia$Frecuencia)]

tabla_frecuencia <- tabla_frecuencia %>%
  mutate(Moda = moda)

print(tabla_frecuencia)
##     Problem_Type Frecuencia Frecuencia_Porcentual Frecuencia_Acumulada
## 1 Classification        175                 31.25                  175
## 2     Clustering        196                 35.00                  371
## 3     Regression        189                 33.75                  560
frecuencias <- table(datosReadr$Dataset_Type)
print(frecuencias)
## 
##       Image     Tabular        Text Time Series 
##         157         136         143         124
tabla_frecuencia <- as.data.frame(frecuencias)
colnames(tabla_frecuencia) <- c("Dataset_Type", "Frecuencia")

tabla_frecuencia <- tabla_frecuencia %>%
  mutate(Frecuencia_Porcentual = Frecuencia / sum(Frecuencia) * 100)

tabla_frecuencia <- tabla_frecuencia %>%
  mutate(Frecuencia_Acumulada = cumsum(Frecuencia))

moda <- tabla_frecuencia$Categoria[which.max(tabla_frecuencia$Frecuencia)]

tabla_frecuencia <- tabla_frecuencia %>%
  mutate(Moda = moda)

print(tabla_frecuencia)
##   Dataset_Type Frecuencia Frecuencia_Porcentual Frecuencia_Acumulada
## 1        Image        157              28.03571                  157
## 2      Tabular        136              24.28571                  293
## 3         Text        143              25.53571                  436
## 4  Time Series        124              22.14286                  560
summary(datosReadr$"Accuracy")
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##  0.5038  0.6236  0.7578  0.8779  0.8824  9.7181      39
summary(datosReadr$"Precision")
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##  0.4019  0.5632  0.7195  0.8129  0.8596  9.7320      19
summary(datosReadr$"Recall")
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##  0.3001  0.4819  0.6493  0.7486  0.8404  9.3662      20
summary(datosReadr$"F1_Score")
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##  0.4000  0.5515  0.7086  0.8122  0.8438  9.3740      20
summary(datosReadr$"Training_Time")
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##  0.1032  1.2441  2.4347  2.9910  3.8131 46.9856      20
summary(datosReadr$"Date")
##                     Min.                  1st Qu.                   Median 
## "2023-03-08 11:26:21.07" "2023-07-26 05:26:21.07" "2023-12-12 23:26:21.07" 
##                     Mean                  3rd Qu.                     Max. 
## "2023-12-12 23:26:21.07" "2024-04-30 17:26:21.07" "2024-09-17 11:26:21.07"

Por ultimo, un resumen general de toda la base de datos

summary(datosReadr)
##   Algorithm          Framework         Problem_Type       Dataset_Type      
##  Length:560         Length:560         Length:560         Length:560        
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##     Accuracy        Precision          Recall          F1_Score     
##  Min.   :0.5038   Min.   :0.4019   Min.   :0.3001   Min.   :0.4000  
##  1st Qu.:0.6236   1st Qu.:0.5632   1st Qu.:0.4819   1st Qu.:0.5515  
##  Median :0.7578   Median :0.7195   Median :0.6493   Median :0.7086  
##  Mean   :0.8779   Mean   :0.8129   Mean   :0.7486   Mean   :0.8122  
##  3rd Qu.:0.8824   3rd Qu.:0.8596   3rd Qu.:0.8404   3rd Qu.:0.8438  
##  Max.   :9.7181   Max.   :9.7320   Max.   :9.3662   Max.   :9.3740  
##  NA's   :39       NA's   :19       NA's   :20       NA's   :20      
##  Training_Time          Date                       
##  Min.   : 0.1032   Min.   :2023-03-08 11:26:21.07  
##  1st Qu.: 1.2441   1st Qu.:2023-07-26 05:26:21.07  
##  Median : 2.4347   Median :2023-12-12 23:26:21.07  
##  Mean   : 2.9910   Mean   :2023-12-12 23:26:21.07  
##  3rd Qu.: 3.8131   3rd Qu.:2024-04-30 17:26:21.07  
##  Max.   :46.9856   Max.   :2024-09-17 11:26:21.07  
##  NA's   :20

Valores Faltantes o NA

Vistas las variables, es necesario determinar si existen valores NA que puedan complicar la aplicacion de funciones o interpretacion de la base de datos, un rápido diagnostico que nos permite saber los valores NA y donde pueden ser encontrados, puede realizarse a través del ‘Missingness Map’ o Mapa de valores faltantes:

suppressWarnings(missmap(datosReadr))

Se encontraron datos faltantes en las variables numericas “Precision”, “Recall”, “F1_Score”, “Training_Time” y “Accuracy”, la cantidad de valores NA por variable se muestra a continuacion:

na_colum <- colSums(is.na(datosReadr))
print(na_colum)
##     Algorithm     Framework  Problem_Type  Dataset_Type      Accuracy 
##             0             0             0             0            39 
##     Precision        Recall      F1_Score Training_Time          Date 
##            19            20            20            20             0

El tratamiento de los datos faltantes se realizara através imputación de mediana para asi evitar sesgos a la hora de comparar las variables:

datosReadr_imp = datosReadr

tratamiento_atp <- function(x) {
  x[is.na(x)] <- median(x, na.rm = TRUE)
  return(x)
}

datosReadr_imp$Accuracy <- tratamiento_atp(datosReadr$Accuracy)
datosReadr_imp$Precision <- tratamiento_atp(datosReadr$Precision)
datosReadr_imp$Recall <- tratamiento_atp(datosReadr$Recall)
datosReadr_imp$F1_Score <- tratamiento_atp(datosReadr$F1_Score)
datosReadr_imp$Training_Time <- tratamiento_atp(datosReadr$Training_Time)

Finalmente comprobamos que tras la imputación se mantenga la normalidad de las variables:

  • Accuracy
ggp1 <- ggplot(data.frame(value=datosReadr$Accuracy), aes(x=value)) +
  geom_histogram(fill="orange", color="red", alpha=0.9) +
  ggtitle("Original data") +
  xlab("Accuracy") + ylab("Frequency") +
  theme_ipsum() +
  theme(plot.title = element_text(size=15))

ggp2 <- ggplot(data.frame(value=datosReadr_imp$Accuracy), aes(x=value)) +
  geom_histogram(fill="green", color="blue", alpha=0.9) +
  ggtitle("Imput data") +
  xlab("Accuracy") + ylab("Frequency") +
  theme_ipsum() +
  theme(plot.title = element_text(size=15))

grid.arrange(ggp1, ggp2, ncol = 2)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 39 rows containing non-finite outside the scale range
## (`stat_bin()`).
## Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)): font family
## not found in Windows font database
## Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)): font family
## not found in Windows font database
## Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)): font family
## not found in Windows font database
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database

  • Precision
ggp1 <- ggplot(data.frame(value=datosReadr$Precision), aes(x=value)) +
  geom_histogram(fill="orange", color="red", alpha=0.9) +
  ggtitle("Original data") +
  xlab("Precision") + ylab("Frequency") +
  theme_ipsum() +
  theme(plot.title = element_text(size=15))

ggp2 <- ggplot(data.frame(value=datosReadr_imp$Precision), aes(x=value)) +
  geom_histogram(fill="green", color="blue", alpha=0.9) +
  ggtitle("Imput data") +
  xlab("Precision") + ylab("Frequency") +
  theme_ipsum() +
  theme(plot.title = element_text(size=15))

grid.arrange(ggp1, ggp2, ncol = 2)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 19 rows containing non-finite outside the scale range
## (`stat_bin()`).
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database

  • Recall
ggp1 <- ggplot(data.frame(value=datosReadr$Recall), aes(x=value)) +
  geom_histogram(fill="orange", color="red", alpha=0.9) +
  ggtitle("Original data") +
  xlab("Recall") + ylab("Frequency") +
  theme_ipsum() +
  theme(plot.title = element_text(size=15))

ggp2 <- ggplot(data.frame(value=datosReadr_imp$Recall), aes(x=value)) +
  geom_histogram(fill="green", color="blue", alpha=0.9) +
  ggtitle("Imput data") +
  xlab("Recall") + ylab("Frequency") +
  theme_ipsum() +
  theme(plot.title = element_text(size=15))

grid.arrange(ggp1, ggp2, ncol = 2)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 20 rows containing non-finite outside the scale range
## (`stat_bin()`).
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database

  • F1 Score
ggp1 <- ggplot(data.frame(value=datosReadr$F1_Score), aes(x=value)) +
  geom_histogram(fill="orange", color="red", alpha=0.9) +
  ggtitle("Original data") +
  xlab("F1 Score") + ylab("Frequency") +
  theme_ipsum() +
  theme(plot.title = element_text(size=15))

ggp2 <- ggplot(data.frame(value=datosReadr_imp$F1_Score), aes(x=value)) +
  geom_histogram(fill="green", color="blue", alpha=0.9) +
  ggtitle("Imput data") +
  xlab("F1 Score") + ylab("Frequency") +
  theme_ipsum() +
  theme(plot.title = element_text(size=15))

grid.arrange(ggp1, ggp2, ncol = 2)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 20 rows containing non-finite outside the scale range
## (`stat_bin()`).
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database

  • Training Time
ggp1 <- ggplot(data.frame(value=datosReadr$Training_Time), aes(x=value)) +
  geom_histogram(fill="orange", color="red", alpha=0.9) +
  ggtitle("Original data") +
  xlab("Training Time") + ylab("Frequency") +
  theme_ipsum() +
  theme(plot.title = element_text(size=15))

ggp2 <- ggplot(data.frame(value=datosReadr_imp$Training_Time), aes(x=value)) +
  geom_histogram(fill="green", color="blue", alpha=0.9) +
  ggtitle("Imput data") +
  xlab("Training Time") + ylab("Frequency") +
  theme_ipsum() +
  theme(plot.title = element_text(size=15))

grid.arrange(ggp1, ggp2, ncol = 2)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 20 rows containing non-finite outside the scale range
## (`stat_bin()`).
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
## family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## font family not found in Windows font database

A través de las gráficas se puede observar que la diferencia no es significativa y se mantiene la distribución, por lo que se puede continuar con el estudio.

Analisis de normalidad

Para analizar la posibilidad de normalidad en el conjunto de datos númericos de la base de datos, se hará de dos formas, una analítica y otra gráfica.

Para la primera usaremos el test de Shapiro-Wilk, para el cual existen dos hipótesis:

1- Hipótesis nula (H0): Los datos provienen de una distribución normal.

2- Hipótesis alternativa (H1): Los datos no provienen de una distribución normal.

Al ejecutar el test se consigue un valor p (p-value) para decidir si rechzar o no la hipótesis nula, si este p-value es menor a 0.05, la hipótesis nula se rechaza, si es maor, significa que podría haber normalidad, observemos cual es el caso:

numeric_vars <- datosReadr[sapply(datosReadr, is.numeric)]

normality_tests <- lapply(numeric_vars, shapiro.test)

print(normality_tests)
## $Accuracy
## 
##  Shapiro-Wilk normality test
## 
## data:  X[[i]]
## W = 0.22771, p-value < 2.2e-16
## 
## 
## $Precision
## 
##  Shapiro-Wilk normality test
## 
## data:  X[[i]]
## W = 0.25643, p-value < 2.2e-16
## 
## 
## $Recall
## 
##  Shapiro-Wilk normality test
## 
## data:  X[[i]]
## W = 0.31806, p-value < 2.2e-16
## 
## 
## $F1_Score
## 
##  Shapiro-Wilk normality test
## 
## data:  X[[i]]
## W = 0.2547, p-value < 2.2e-16
## 
## 
## $Training_Time
## 
##  Shapiro-Wilk normality test
## 
## data:  X[[i]]
## W = 0.34923, p-value < 2.2e-16

Como se puede ver, todos los p.value son muchos menores a 0.05, pero debemos recordar que este test funciona mejor para muestras pequeñas (de menos de 50 observaciones) y puede ser influenciado por valores extremos, ahora con los gráficso QQ-plot podemos ver que tan cierto es que el test fue influenciado.

Para analizar un gráfico QQ-plot hay que entender sus partes:

1- Eje X (Cuantiles Teóricos): Representa los cuantiles de la distribución teórica con la que estás comparando tus datos (como la distribución normal).

2- Eje Y (Cuantiles Muestrales): Representa los cuantiles de los datos observados.

3- Línea Diagonal: Representa el caso ideal en el que los datos coinciden exactamente con la distribución teórica (por ejemplo, normalidad perfecta). Cuanto más cerca estén los puntos de esta línea, más parecida será la distribución de los datos a la distribución teórica.

como se puede ver, se hace una comparación cuartil-cuartil a eso debe su nombre (en inglés quantile-quantile) el eje X representa la forma que deberían tener los cuartiles si las variables siguen una distribución nomral, y se comparan con los cuartiles reales, los del eje Y.

Si los puntos en la gráfica se acercan mucho a la línea diagonal, se puede decir que es muy probable que la variable siga una distribución normal. Veamos si es el caso:

names_numeric_vars <- names(numeric_vars)
for (var in names_numeric_vars){
  qqnorm(datosReadr[[var]], main = paste("QQ Plot -", var))
  qqline(datosReadr[[var]], col = "red")
}

Datos atipicos

Ahora utilizaremos graficas de cajas y bigotes con cada variable para describir su comportamiento e identificar los datos atipicos:

variables <- c("Accuracy", "Precision", "F1_Score", "Training_Time", "Recall")
for (var in variables) {
  boxplot(datosReadr[[var]], main = var, ylab = var)
  
  atipicos <- boxplot.stats(datosReadr[[var]])$out
  
  if (length(atipicos) > 0) {
    text(x = rep(1, length(atipicos)), y = atipicos, labels = round(atipicos, 2), pos = 4, col = "purple")
  }
}

Se puede observar la cantidad de valores atipicos en cada variable, para evitar que estos puedan afectar el estudio de la base de datos se les dará su respectivo tratamiento, se extraeran de la base de datos, se reemplazaran por la media en cada variable y se almacenaran los datos atipicos de cada variable en un dataset aparte para su posterior estudio:

conversion_atp <- function(x) {
  iqr <- IQR(x, na.rm = TRUE)
  cuartil1 <- quantile(x, 0.25, na.rm = TRUE)
  cuartil3 <- quantile(x, 0.75, na.rm = TRUE)
  lim_inferior <- cuartil1 - 1.5 * iqr
  lim_superior <- cuartil3 + 1.5 * iqr
  x <- ifelse(x < lim_inferior | x > lim_superior, NA, x)
  return(x)
}
datosReadr$Accuracy <- conversion_atp(datosReadr$Accuracy)
datosReadr$Precision <- conversion_atp(datosReadr$Precision)
datosReadr$Recall <- conversion_atp(datosReadr$Recall)
datosReadr$F1_Score <- conversion_atp(datosReadr$F1_Score)
datosReadr$Training_Time <- conversion_atp(datosReadr$Training_Time)

tratamiento_atp <- function(x) {
  x[is.na(x)] <- median(x, na.rm = TRUE)
  return(x)
}

datosReadr$Accuracy <- tratamiento_atp(datosReadr$Accuracy)
datosReadr$Precision <- tratamiento_atp(datosReadr$Precision)
datosReadr$Recall <- tratamiento_atp(datosReadr$Recall)
datosReadr$F1_Score <- tratamiento_atp(datosReadr$F1_Score)
datosReadr$Training_Time <- tratamiento_atp(datosReadr$Training_Time)

Hecho el tratamiento, se procede a observar nuevamente cada variable en el boxplot:

variables <- c("Accuracy", "Precision", "F1_Score", "Training_Time", "Recall")
for (var in variables) {
  boxplot(datosReadr[[var]], main = var, ylab = var)
  
  atipicos <- boxplot.stats(datosReadr[[var]])$out
  
  if (length(atipicos) > 0) {
    text(x = rep(1, length(atipicos)), y = atipicos, labels = round(atipicos, 2), pos = 4, col = "purple")
  }
}

Se puede apreciar el cambio en la curva de normalidad después del tratamiento de los valores atipicos:

names_numeric_vars <- names(numeric_vars)
for (var in names_numeric_vars){
  qqnorm(datosReadr[[var]], main = paste("QQ Plot -", var))
  qqline(datosReadr[[var]], col = "red")
}

Análisis de pregunta

Hecho el tratamiento a la base de datos y explicada cada una de las variables, se puede proceder a analizar la pregunta y encontrar solución, se está buscando el algoritmo que tenga mayor puntaje F1 con el menor tiempo de entrenamiento en problemas de regresión.

tabla_uso <- datosReadr %>%
  group_by(Algorithm, Problem_Type) %>%
  summarise(Count = n(), .groups = "drop")

# Crear el gráfico con etiquetas
ggplot(tabla_uso, aes(x = Algorithm, y = Count, fill = Problem_Type)) +
  geom_bar(stat = "identity", position = "dodge") +
  geom_text(aes(label = Count), 
            position = position_dodge(width = 0.9), 
            vjust = -0.5, # Ajustar la posición vertical de la etiqueta
            size = 3) +  # Tamaño de la fuente
  labs(title = "Uso de Algoritmos por Tipo de Problema",
       x = "Algoritmo",
       y = "Cantidad de Usos",
       fill = "Tipo de Problema") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

En el gráfico anterior se muestra el uso de cada uno de estos algoritmos en cada tipo de problemas, y se puede observar que los dos más usados para problemas de regresión son el SVM y K-Means, además se puede apreciar que cada uno de los algoritmos tiene un enfoque a cierto tipo de problema, para continuar con el estudio de la pregunta, vamos a analizar como es el desempeño de estos algoritmos en problemas de regresión:

datos_regresion <- datosReadr %>%
  filter(Algorithm %in% c("SVM", "K-Means") & Problem_Type == "Regression")

promedios <- datos_regresion %>%
  group_by(Algorithm) %>%
  summarise(promedio_f1 = mean(F1_Score, na.rm = TRUE))

conteo_por_encima <- datos_regresion %>%
  left_join(promedios, by = "Algorithm") %>%
  mutate(encima_promedio = F1_Score > promedio_f1) %>%
  group_by(Algorithm) %>%
  summarise(conteo_encima = sum(encima_promedio))

resultados <- promedios %>%
  left_join(conteo_por_encima, by = "Algorithm")

ggplot(datos_regresion, aes(x = Algorithm, y = F1_Score, fill = Algorithm)) +
  geom_boxplot() +  # Boxplot para mostrar la distribución del puntaje F1
  geom_jitter(alpha = 0.5, width = 0.2) +  # Añadir puntos individuales
  geom_text(data = resultados, aes(x = Algorithm, y = max(datos_regresion$F1_Score) + 0.05, 
                                     label = paste("Encima del promedio:", conteo_encima)),
            color = "black", size = 5, vjust = 0) +  # Etiquetas con conteo
  labs(title = "Comparación del Puntaje F1 en Problemas de Regresión",
       x = "Algoritmo",
       y = "Puntaje F1") +
  theme_minimal()

# 2. Gráfico para comparar el puntaje F1 con el tiempo de entrenamiento
ggplot(datos_regresion, aes(x = Training_Time, y = F1_Score, color = Algorithm)) +
  geom_point(size = 3, alpha = 0.6) +  # Puntos
  geom_smooth(method = "lm", se = FALSE) +  # Línea de tendencia
  labs(title = "Puntaje F1 vs. Tiempo de Entrenamiento en Problemas de Regresión",
       x = "Tiempo de Entrenamiento",
       y = "Puntaje F1") +
  theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'

En la primera comparativa,se puede observar que a primera vista la diferencia de los puntajes F1 de ambos algoritmos no es tan significativa, el algoritmo k-means presenta un rango intercuartil mayor entre sus puntajes F1 y un promedio mayor, sin embargo el algoritmo SVM tiene más puntajes por encima de su promedio comparado a K-Means.

En la segunda comparativa en la cual se puede apreciar el Tiempo de entrenamiento Contra El puntaje F1, lo primero a evidenciar es que los puntajes se encuentran demasiado dispersos respecto al tiempo, es decir, no existe una correlación entre el tiempo de entrenamiento y el puntaje F1, aparte de eso se puede ver que el algoritmo K-Means obtiene un mejor puntaje con un menor tiempo de entrenamiento comparado con SVM, que unicamente supera el puntaje de K-Means poco antes de las 4 horas de entrenamiento.

Por ultimo también es importante analizar los promedios de cada algoritmo respecto al tiempo y respecto su puntaje F1:

## [1] "El promedio del puntaje F1 de K-Means en problemas de regresión es: 0.711391011081969"
## [1] "El promedio del tiempo de entrenamiento de K-Means en problemas de regresión es: 2.57018451039694"
## [1] "El promedio del puntaje F1 de SVM en problemas de regresión es: 0.695920122821653"
## [1] "El promedio del tiempo de entrenamiento de SVM en problemas de regresión es: 2.33723401069937"

En Conclusión, aunque la diferencia no es Realmente Significativa, K-Means es el algoritmo más efectivo a la hora de trabajar con problemas de Regresión, ya que con menor tiempo de entrenamiento logra un puntaje F1 promedio mayor al de SVM, aunque también cabe resaltar que el tiempo de entrenamiento promedio para SVM en los problemas de regresión es inferior al del algoritmo K-Means.