Algoritmos de inteligencia artificial utilizando frameworks de python

Desarrollado por Tiffany Mendoza y Alejandra Menenses

Comprender el contexto y formulación de la Pregunta Problema

Google define la inteligencia artificial como “un conjunto de tecnologías que permiten que las computadoras realicen una variedad de funciones avanzadas, incluida la capacidad de ver, comprender y traducir lenguaje hablado y escrito, analizar datos, hacer recomendaciones y mucho más” (¿Qué Es la Inteligencia Artificial o IA? | Google Cloud, s. f.). Desde la popularización de herramientas como el chatGPT, Claude, Copilot, entre otras, se ha vuelto muy común que pequeñas, medianas empresas y usuarios avanzados busquen alternativas para crear sus propios algoritmos y sacar el máximo provecho a estas nuevas tecnologías y aplicarlas a sus necesidades específicas. Es aquí como lenguajes más amistosos como Python y sus frameworks entran en juego. Este lenguaje, según Amazon es muy facil de comprender debido a una sintaxis parecida al inglés, es eficiente, pues los scripts tienden a ser más cortos en comparación con otros lenguajes y cuenta con una comunidad de millones de desarrolladores alrededor del mundo, lo que facilita el soporte. Además explican que gracias librerías como Keras, OpenCV-Python, TensorFlow, Scikit-learn, entre otras ponen a Python como la opción más llamativa para científicos de datos realizar tareas de análisis de datos y machine learning (¿Qué Es Python? - Explicación del Lenguaje Python - AWS, s.f.).

Pregunta problema

Al buscar en internet “frameworks para IA de python” encontramos infinita información de diversas fuentes. Para usuarios no avanzados buscando adentrarse en este mundo es normal que surjan dudas como ¿Cuales frameworks existen? ¿Qué puedo construir con ellas? ¿Cómo se entrenan y cuando tiempo duran en ese proceso?. Entre esas y muchas preguntas que se pueden responder, para el desarrollo del presente trabajo se buscará responder:
¿Qué frameworks producen los modelos más precisos en cada tipo de problema?

install.packages(“readxl”, “tidyr”, “dplyr”, “knitr”, “kableExtra”, “plotly”, “readr”, “ggplot2”, “rworldmap”, “VIM”, “mice”, “naniar”, “Amelia”, “tidyverse”, “hrbrthemes”, “gridExtra”, “missForest”, “outliers”, “lattice”)

Análisis Exploratorio de Datos

Cargue de los datos
# Leer los datos
# archivo <- file.choose()
archivo <- '/Users/pctm/Documents/EDA/Dataset_IA_corte_II.xlsx'
data <- read_excel(archivo, col_names = TRUE)

# Crear la tabla y aplicar estilos
kable(data[1:20,], caption = "Algoritmos de inteligencia artificial utilizando frameworks de python") %>%
  kable_styling(full_width = TRUE) %>%
  scroll_box(width = "900px", height = "500px")
Algoritmos de inteligencia artificial utilizando frameworks de python
Algorithm Framework Problem_Type Dataset_Type Accuracy Precision Recall F1_Score Training_Time Date
SVM Scikit-learn Regression Time Series 0.6618051 0.6929447 NA 0.4426950 4.9785924 2023-03-08 11:26:21
K-Means Keras Clustering Time Series 0.7443216 0.4900292 0.8766533 0.4414046 NA 2023-03-09 11:26:21
Neural Network Keras Clustering Image 0.8852037 0.5948056 0.9685424 0.9644707 3.2825938 2023-03-10 11:26:21
SVM Keras Clustering Text 0.8416477 0.8424142 0.8748388 0.7041523 4.0416289 2023-03-11 11:26:21
SVM Scikit-learn Regression Tabular 0.7229514 0.6856109 0.3010956 0.6456472 3.6039908 2023-03-12 11:26:21
K-Means PyTorch Regression Image 0.6368133 0.6255330 7.4548096 0.8865271 3.0064753 2023-03-13 11:26:21
Neural Network PyTorch Regression Text 0.9985623 0.6366858 0.3357948 0.9014956 NA 2023-03-14 11:26:21
Neural Network Scikit-learn Regression Image 0.7130907 0.6756681 0.4803251 0.5993146 2.3283453 2023-03-15 11:26:21
SVM Keras Regression Time Series NA 0.8710099 0.3416673 0.8161708 3.4064529 2023-03-16 11:26:21
Random Forest Keras Regression Text 0.5818119 0.9352508 NA 0.8626737 3.4199049 2023-03-17 11:26:21
SVM PyTorch Regression Image 0.8974048 9.7320081 0.7806129 0.7927904 1.9283008 2023-03-18 11:26:21
SVM Keras Clustering Image 0.8468411 0.8721420 0.3801413 0.4909570 4.7142907 2023-03-19 11:26:21
SVM TensorFlow Clustering Tabular 0.6103848 0.5892441 0.5686872 0.9255299 0.9200495 2023-03-20 11:26:21
SVM PyTorch Clustering Image 0.5411905 0.8128808 0.6193656 0.7234567 2.5517613 2023-03-21 11:26:21
K-Means Keras Clustering Text 0.8402497 0.6625619 0.5583371 0.5694835 3.4853315 2023-03-22 11:26:21
Neural Network PyTorch Regression Text NA 0.5528024 0.3847175 0.6551369 3.5159654 2023-03-23 11:26:21
K-Means TensorFlow Classification Tabular 0.6366298 0.9045229 0.5932635 0.4225427 3.2783309 2023-03-24 11:26:21
K-Means PyTorch Regression Text 0.9754318 0.4230558 0.8258246 0.4767201 1.4489122 2023-03-25 11:26:21
K-Means PyTorch Classification Time Series 0.5755289 0.9410572 0.3497054 0.8593281 0.8654122 2023-03-26 11:26:21
SVM PyTorch Clustering Text 0.7161674 0.6768865 0.3561260 0.4000070 3.2161076 2023-03-27 11:26:21
Estructura del data set
#Verifico si hay algun problema en la base de datos, sin embargo, todo está funcionando bien
print(problems(data))
## # A tibble: 0 × 4
## # ℹ 4 variables: row <int>, col <int>, expected <chr>, actual <chr>

Esto indica que no se encontraron problemas en los datos, ya que el tibble tiene 0 filas y 4 columnas, es decir el tibble está vacío.

Las variables son:

# Mostrar nombres de las variables

variables <- names(data)
variables
##  [1] "Algorithm"     "Framework"     "Problem_Type"  "Dataset_Type" 
##  [5] "Accuracy"      "Precision"     "Recall"        "F1_Score"     
##  [9] "Training_Time" "Date"
2. ANÁLISIS DE LAS CARACTERÍSTICAS DE LA BASE DE DATOS:

Verificamos los datos viendo el encabezado y la cola de los datos:

head(data)
## # 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>
tail(data)
## # A tibble: 6 × 10
##   Algorithm      Framework  Problem_Type  Dataset_Type Accuracy Precision Recall
##   <chr>          <chr>      <chr>         <chr>           <dbl>     <dbl>  <dbl>
## 1 Neural Network Keras      Classificati… Image           0.744     0.637  0.443
## 2 K-Means        Keras      Regression    Time Series     0.555     0.653  0.714
## 3 K-Means        Keras      Regression    Tabular         0.774     0.647  0.430
## 4 Random Forest  TensorFlow Clustering    Image           0.727    NA      0.532
## 5 K-Means        TensorFlow Regression    Tabular         0.922     0.828  0.899
## 6 Random Forest  PyTorch    Clustering    Image           0.549     0.765  0.445
## # ℹ 3 more variables: F1_Score <dbl>, Training_Time <dbl>, Date <dttm>
dim(data)
## [1] 560  10

Filas: 560 Columnas: 10

Analizamos ahora la estructura de la base de datos:

#Realizo el cambio porque esta variable es numérica.
data$Date <- as.numeric(data$Date)
str(data)
## tibble [560 × 10] (S3: tbl_df/tbl/data.frame)
##  $ Algorithm    : chr [1:560] "SVM" "K-Means" "Neural Network" "SVM" ...
##  $ Framework    : chr [1:560] "Scikit-learn" "Keras" "Keras" "Keras" ...
##  $ Problem_Type : chr [1:560] "Regression" "Clustering" "Clustering" "Clustering" ...
##  $ Dataset_Type : chr [1:560] "Time Series" "Time Series" "Image" "Text" ...
##  $ Accuracy     : num [1:560] 0.662 0.744 0.885 0.842 0.723 ...
##  $ Precision    : num [1:560] 0.693 0.49 0.595 0.842 0.686 ...
##  $ Recall       : num [1:560] NA 0.877 0.969 0.875 0.301 ...
##  $ F1_Score     : num [1:560] 0.443 0.441 0.964 0.704 0.646 ...
##  $ Training_Time: num [1:560] 4.98 NA 3.28 4.04 3.6 ...
##  $ Date         : num [1:560] 1.68e+09 1.68e+09 1.68e+09 1.68e+09 1.68e+09 ...
library(tibble)

diccionario_variables <- tibble(
  Nombre = c(
    "Algorithm", 
    "Framework", 
    "Problem_Type", 
    "Dataset_Type", 
    "Accuracy", 
    "Precision", 
    "Recall", 
    "F1_Score", 
    "Training_Time", 
    "Date"
  ),
  Definición = c(
    "Tipo de algoritmo de IA utilizado",
    "Framework o biblioteca utilizada para la implementación del modelo de IA",
    "Tipo de problema abordado por el modelo",
    "Tipo de datos utilizados en el entrenamiento del modelo",
    "Precisión del modelo en el conjunto de prueba",
    "Precisión del modelo en términos de predicciones positivas",
    "Sensibilidad del modelo para identificar correctamente los positivos",
    "Medida armónica entre precisión y recall",
    "Tiempo de entrenamiento del modelo en horas",
    "Fecha en la que se realizó la evaluación del modelo"
  ),
  Naturaleza = c(
    "Categórica",
    "Categórica",
    "Categórica",
    "Categórica",
    "Numérica Continua",
    "Numérica Continua",
    "Numérica Continua",
    "Numérica Continua",
    "Numérica Continua",
    "Numérica Continua"
  ),
  `Nivel de Medición` = c(
    "Nominal",
    "Nominal",
    "Nominal",
    "Nominal",
    "Razón",
    "Razón",
    "Razón",
    "Razón",
    "Razón",
    "Razón"
  ),
  `Criterio de calificación y/o clasificación` = c(
    "'Neural Network', 'Random Forest', 'SVM', 'K-Means'",
    "'TensorFlow', 'PyTorch', 'Keras', 'Scikit-learn'",
    "'Classification', 'Regression', 'Clustering'",
    "'Image', 'Text', 'Tabular', 'Time Series'",
    "Valor entre [0,1]",
    "Valor entre [0,1]",
    "Valor entre [0,1]",
    "Valor entre [0,1]",
    "Tiempo en horas",
    "Fecha en formato YYYY-MM-DD"
  )
)

# Mostrar la tabla
diccionario_variables
## # A tibble: 10 × 5
##    Nombre       Definición Naturaleza `Nivel de Medición` Criterio de califica…¹
##    <chr>        <chr>      <chr>      <chr>               <chr>                 
##  1 Algorithm    Tipo de a… Categórica Nominal             'Neural Network', 'Ra…
##  2 Framework    Framework… Categórica Nominal             'TensorFlow', 'PyTorc…
##  3 Problem_Type Tipo de p… Categórica Nominal             'Classification', 'Re…
##  4 Dataset_Type Tipo de d… Categórica Nominal             'Image', 'Text', 'Tab…
##  5 Accuracy     Precisión… Numérica … Razón               Valor entre [0,1]     
##  6 Precision    Precisión… Numérica … Razón               Valor entre [0,1]     
##  7 Recall       Sensibili… Numérica … Razón               Valor entre [0,1]     
##  8 F1_Score     Medida ar… Numérica … Razón               Valor entre [0,1]     
##  9 Training_Ti… Tiempo de… Numérica … Razón               Tiempo en horas       
## 10 Date         Fecha en … Numérica … Razón               Fecha en formato YYYY…
## # ℹ abbreviated name: ¹​`Criterio de calificación y/o clasificación`
# Crear y estilizar la tabla centrada
tabla_diccionario <- diccionario_variables %>%
  kable(caption = "Operacionalización de Variables", align = 'c') %>%  # 'align = "c"' centra las columnas
  kable_styling(full_width = F, position = "center") %>%  # Centra la tabla
  column_spec(1, bold = TRUE, color = "darkgrey", border_right = TRUE) %>%
  #column_spec(2, background = "lightgrey", border_right = TRUE) %>%
  row_spec(0, bold = TRUE, color = "darkblue")  # Encabezado en negrita y color rojo

# Mostrar la tabla
tabla_diccionario
Operacionalización de Variables
Nombre Definición Naturaleza Nivel de Medición Criterio de calificación y/o clasificación
Algorithm Tipo de algoritmo de IA utilizado Categórica Nominal ‘Neural Network’, ‘Random Forest’, ‘SVM’, ‘K-Means’
Framework Framework o biblioteca utilizada para la implementación del modelo de IA Categórica Nominal ‘TensorFlow’, ‘PyTorch’, ‘Keras’, ‘Scikit-learn’
Problem_Type Tipo de problema abordado por el modelo Categórica Nominal ‘Classification’, ‘Regression’, ‘Clustering’
Dataset_Type Tipo de datos utilizados en el entrenamiento del modelo Categórica Nominal ‘Image’, ‘Text’, ‘Tabular’, ‘Time Series’
Accuracy Precisión del modelo en el conjunto de prueba Numérica Continua Razón Valor entre [0,1]
Precision Precisión del modelo en términos de predicciones positivas Numérica Continua Razón Valor entre [0,1]
Recall Sensibilidad del modelo para identificar correctamente los positivos Numérica Continua Razón Valor entre [0,1]
F1_Score Medida armónica entre precisión y recall Numérica Continua Razón Valor entre [0,1]
Training_Time Tiempo de entrenamiento del modelo en horas Numérica Continua Razón Tiempo en horas
Date Fecha en la que se realizó la evaluación del modelo Numérica Continua Razón Fecha en formato YYYY-MM-DD

Limpieza, tratamiento y preparación

Revisión de los tipos de datos y conversión.

En la lectura inicial que se realizó de la base de datos se concluyó que no tenemos problema con ningún dato en particular por tanto no será necesario hacer una conversión. Para facilitar la lectura se traerá a esta sección una tabla con los tipos de datos de cada una de las variables. Para resumir, tenemos 4 variables de tipo chr, 5 num y una de tipo date.

str(data)
## tibble [560 × 10] (S3: tbl_df/tbl/data.frame)
##  $ Algorithm    : chr [1:560] "SVM" "K-Means" "Neural Network" "SVM" ...
##  $ Framework    : chr [1:560] "Scikit-learn" "Keras" "Keras" "Keras" ...
##  $ Problem_Type : chr [1:560] "Regression" "Clustering" "Clustering" "Clustering" ...
##  $ Dataset_Type : chr [1:560] "Time Series" "Time Series" "Image" "Text" ...
##  $ Accuracy     : num [1:560] 0.662 0.744 0.885 0.842 0.723 ...
##  $ Precision    : num [1:560] 0.693 0.49 0.595 0.842 0.686 ...
##  $ Recall       : num [1:560] NA 0.877 0.969 0.875 0.301 ...
##  $ F1_Score     : num [1:560] 0.443 0.441 0.964 0.704 0.646 ...
##  $ Training_Time: num [1:560] 4.98 NA 3.28 4.04 3.6 ...
##  $ Date         : num [1:560] 1.68e+09 1.68e+09 1.68e+09 1.68e+09 1.68e+09 ...

Revisión de datos faltantes.

aggr(data, numbers = TRUE, col = c("navyblue", "red"), 
     cex.axis = 0.7, gap = 3, ylab = c("Proportion of missingness", "Missingness pattern"))

md.pattern(data, plot = TRUE, rotate.names = TRUE)

##     Algorithm Framework Problem_Type Dataset_Type Date Precision Recall
## 448         1         1            1            1    1         1      1
## 39          1         1            1            1    1         1      1
## 16          1         1            1            1    1         1      1
## 17          1         1            1            1    1         1      1
## 2           1         1            1            1    1         1      1
## 18          1         1            1            1    1         1      0
## 1           1         1            1            1    1         1      0
## 16          1         1            1            1    1         0      1
## 1           1         1            1            1    1         0      1
## 1           1         1            1            1    1         0      1
## 1           1         1            1            1    1         0      0
##             0         0            0            0    0        19     20
##     F1_Score Training_Time Accuracy    
## 448        1             1        1   0
## 39         1             1        0   1
## 16         1             0        1   1
## 17         0             1        1   1
## 2          0             0        1   2
## 18         1             1        1   1
## 1          1             0        1   2
## 16         1             1        1   1
## 1          1             0        1   2
## 1          0             1        1   2
## 1          1             1        1   2
##           20            20       39 118
gg_miss_var(data, show_pct = TRUE)

missmap(data, col = c("yellow", "darkblue"), legend = TRUE)
## Warning: Unknown or uninitialised column: `arguments`.
## Unknown or uninitialised column: `arguments`.
## Warning: Unknown or uninitialised column: `imputations`.

Los gráficos anteriores permiten observar los porcentajes de datos faltantes. Para este dataset el missingness map muestra que se tiene un 98% de datos completos. Los demás representan de distintas formas que se presentan valores faltantes en las variables Precision, F1_Score, Recall, Training_TIme y Accuracy, siendo esta última la que mayor porcentaje tiene (6%).

Resumen estadistico inicial

summary(data)
##   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.   :1.678e+09  
##  1st Qu.: 1.2441   1st Qu.:1.690e+09  
##  Median : 2.4347   Median :1.702e+09  
##  Mean   : 2.9910   Mean   :1.702e+09  
##  3rd Qu.: 3.8131   3rd Qu.:1.714e+09  
##  Max.   :46.9856   Max.   :1.727e+09  
##  NA's   :20

Un primer análisis de este dataset incluye un conjunto de 560 observaciones y 10 variables catégoricas y numéricas. Si nos detenemos a observar las variables numéricas tenemos casos como “Accuracy” varía desde un mínimo de 0.5038 hasta un máximo de 9.7181, indicando una amplia gama de resultados en los modelos. Precision que varía entre 0.4019 y 9.7320. Por otro lado, Recall y F1_Score fluctuan entre 0.3 y 9.3 y 0.4 y 9.3, respectivamente. Los tiempos de entrenamiento se extienden desde 0.10 horas hasta 46.9. Las medias de nuestras variables corresponderían a 0.8779, 0.8129, 0.7486, 0.8122 y 2.9910, en el orden mencionado anteriormente.

Detección de outliers

Accuracy
data_omit <- na.omit(data)
suppressWarnings({
  library(ggplot2)
  library(tidyverse)
  library(hrbrthemes)
  library(gridExtra)
  library(missForest)
})

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

ggp2 <- ggplot(data.frame(value=data_omit$Accuracy), aes(x=value)) +
  geom_histogram(fill="#43B047", color="#049DCB", alpha=0.9) +
  ggtitle("Reemplazado por Mediana") +
  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()`).
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Se ve en la imagen anterior que omitiendo los datos NA tenemos una distribución con algunas diferencias para datos superiores al 7.5. Debido a eso, imputaremos los datos NA por su mediana.

data_imputados <- data %>%
  mutate(
    Accuracy = ifelse(is.na(Accuracy), median(Accuracy, na.rm = TRUE), Accuracy),
    Precision = ifelse(is.na(Precision), median(Precision, na.rm = TRUE), Precision),
    Recall = ifelse(is.na(Recall), median(Recall, na.rm = TRUE), Recall),
    F1_Score = ifelse(is.na(F1_Score), median(F1_Score, na.rm = TRUE), F1_Score),
    Training_Time = ifelse(is.na(Training_Time), median(Training_Time, na.rm = TRUE), Training_Time)
  )

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

ggp2 <- ggplot(data.frame(value=data_imputados$Accuracy), aes(x=value)) +
  geom_histogram(fill="#43B047", color="#049DCB", alpha=0.9) +
  ggtitle("Reemplazado por Mediana") +
  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()`).
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Precision

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

ggp2 <- ggplot(data.frame(value=data_imputados$Precision), aes(x=value)) +
  geom_histogram(fill="#43B047", color="#049DCB", alpha=0.9) +
  ggtitle("Reemplazado por Mediana") +
  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()`).
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Recall

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

ggp2 <- ggplot(data.frame(value=data_imputados$Recall), aes(x=value)) +
  geom_histogram(fill="#43B047", color="#049DCB", alpha=0.9) +
  ggtitle("Reemplazado por Mediana") +
  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()`).
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

F1_Score

ggp1 <- ggplot(data.frame(value=data$F1_Score), aes(x=value)) +
  geom_histogram(fill="#FD0000", color="#E52521", 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=data_imputados$F1_Score), aes(x=value)) +
  geom_histogram(fill="#43B047", color="#049DCB", alpha=0.9) +
  ggtitle("Reemplazado por Mediana") +
  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()`).
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Training_Time

ggp1 <- ggplot(data.frame(value=data$Training_Time), aes(x=value)) +
  geom_histogram(fill="#FD0000", color="#E52521", 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=data_imputados$Training_Time), aes(x=value)) +
  geom_histogram(fill="#43B047", color="#049DCB", alpha=0.9) +
  ggtitle("Mediana") +
  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()`).
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Luego de haber tratado los valores NA. Ahora procederemos a identificar los valores atípicos y a realizar el adecuado tratamiento.

# Crear boxplot para Accuracy
gg_accuracy <- ggplot(data_imputados) +
  aes(x = "", y = Accuracy) +
  geom_boxplot(fill = "#0c4c8a") +
  ggtitle("Accuracy") +
  theme_minimal()

# Crear boxplot para Precision
gg_precision <- ggplot(data_imputados) +
  aes(x = "", y = Precision) +
  geom_boxplot(fill = "#0c4c8a") +
  ggtitle("Precision") +
  theme_minimal()

# Crear boxplot para Recall
gg_recall <- ggplot(data_imputados) +
  aes(x = "", y = Recall) +
  geom_boxplot(fill = "#0c4c8a") +
  ggtitle("Recall") +
  theme_minimal()

# Crear boxplot para F1_Score
gg_f1_score <- ggplot(data_imputados) +
  aes(x = "", y = F1_Score) +
  geom_boxplot(fill = "#0c4c8a") +
  ggtitle("F1 Score") +
  theme_minimal()

# Crear boxplot para Training Time
gg_training_time <- ggplot(data_imputados) +
  aes(x = "", y = Training_Time) +
  geom_boxplot(fill = "#0c4c8a") +
  ggtitle("TrainingT") +
  theme_minimal()
grid.arrange(gg_accuracy, gg_precision, gg_recall, gg_f1_score, gg_training_time, ncol = 5)

Tratamiento de valores atipicos

Primero se procederá a utilizar el método de Hampel para encontrar los valores atípicos. Luego, se aplicará Grubbs para ver si los valores máximos y minimos corresponden a outliers. Acto seguido, los valores encontrados se tratarán como NA y serán reemplazados por la mediana.

Accuracy

###### Accuracy (Hampel)
lower_bound <- median(data_imputados$Accuracy) - 3 * mad(data_imputados$Accuracy, constant = 1)
upper_bound <- median(data_imputados$Accuracy) + 3 * mad(data_imputados$Accuracy, constant = 1)
outlier_ind <- which(data_imputados$Accuracy < lower_bound | data$Accuracy > upper_bound)
###### Accuracy (Grubbs)
test <- grubbs.test(data_imputados$Accuracy)
test2 <- grubbs.test(data_imputados$Accuracy, opposite = TRUE)
test
## 
##  Grubbs test for one outlier
## 
## data:  data_imputados$Accuracy
## G = 9.72222, U = 0.83061, p-value < 2.2e-16
## alternative hypothesis: highest value 9.71807960083799 is an outlier
test2
## 
##  Grubbs test for one outlier
## 
## data:  data_imputados$Accuracy
## G = 0.40183, U = 0.99971, p-value = 1
## alternative hypothesis: lowest value 0.503781437489071 is an outlier
data_imputados$Accuracy[outlier_ind] <- NA
median_accuracy <- median(data_imputados$Accuracy, na.rm = TRUE)
data_imputados$Accuracy[is.na(data_imputados$Accuracy)] <- median_accuracy

gg_accuracy <- ggplot(data_imputados) +
  aes(x = "", y = Accuracy) +
  geom_boxplot(fill = "#0c4c8a") +
  ggtitle("Accuracy") +
  theme_minimal()

print(gg_accuracy)

Al volver a pintar el boxplot, se ve que ya no se tiene presencia de valores atípicos. A continuación,de manera iterativa, se aplicará el mismo método en las demás variables. #### Precision

###### Precision (Hampel)
lower_bound <- median(data_imputados$Precision, na.rm = TRUE) - 3 * mad(data_imputados$Precision, constant = 1, na.rm = TRUE)
upper_bound <- median(data_imputados$Precision, na.rm = TRUE) + 3 * mad(data_imputados$Precision, constant = 1, na.rm = TRUE)
outlier_ind <- which(data_imputados$Precision < lower_bound | data_imputados$Precision > upper_bound)

###### Precision (Grubbs)
test <- grubbs.test(data_imputados$Precision)
test2 <- grubbs.test(data_imputados$Precision, opposite = TRUE)
test
## 
##  Grubbs test for one outlier
## 
## data:  data_imputados$Precision
## G = 10.65938, U = 0.79638, p-value < 2.2e-16
## alternative hypothesis: highest value 9.73200811902357 is an outlier
test2
## 
##  Grubbs test for one outlier
## 
## data:  data_imputados$Precision
## G = 0.48720, U = 0.99957, p-value = 1
## alternative hypothesis: lowest value 0.401930958162567 is an outlier
# Reemplazo de outliers por NA y tratamiento
data_imputados$Precision[outlier_ind] <- NA
median_precision <- median(data_imputados$Precision, na.rm = TRUE)
data_imputados$Precision[is.na(data_imputados$Precision)] <- median_precision

# Visualización
gg_precision <- ggplot(data_imputados) +
  aes(x = "", y = Precision) +
  geom_boxplot(fill = "#0c4c8a") +
  ggtitle("Precision") +
  theme_minimal()

print(gg_precision)

Recall

###### Recall (Hampel)
lower_bound <- median(data_imputados$Recall, na.rm = TRUE) - 3 * mad(data_imputados$Recall, constant = 1, na.rm = TRUE)
upper_bound <- median(data_imputados$Recall, na.rm = TRUE) + 3 * mad(data_imputados$Recall, constant = 1, na.rm = TRUE)
outlier_ind <- which(data_imputados$Recall < lower_bound | data_imputados$Recall > upper_bound)

###### Recall (Grubbs)
test <- grubbs.test(data_imputados$Recall)
test2 <- grubbs.test(data_imputados$Recall, opposite = TRUE)
test
## 
##  Grubbs test for one outlier
## 
## data:  data_imputados$Recall
## G = 11.18902, U = 0.77564, p-value < 2.2e-16
## alternative hypothesis: highest value 9.36618227656069 is an outlier
test2
## 
##  Grubbs test for one outlier
## 
## data:  data_imputados$Recall
## G = 0.57746, U = 0.99940, p-value = 1
## alternative hypothesis: lowest value 0.30009428510314 is an outlier
# Reemplazo de outliers por NA y tratamiento
data_imputados$Recall[outlier_ind] <- NA
median_recall <- median(data_imputados$Recall, na.rm = TRUE)
data_imputados$Recall[is.na(data_imputados$Recall)] <- median_recall

# Visualización
gg_recall <- ggplot(data_imputados) +
  aes(x = "", y = Recall) +
  geom_boxplot(fill = "#0c4c8a") +
  ggtitle("Recall") +
  theme_minimal()

print(gg_recall)

F1_Score

###### F1_Score (Hampel)
lower_bound <- median(data_imputados$F1_Score, na.rm = TRUE) - 3 * mad(data_imputados$F1_Score, constant = 1, na.rm = TRUE)
upper_bound <- median(data_imputados$F1_Score, na.rm = TRUE) + 3 * mad(data_imputados$F1_Score, constant = 1, na.rm = TRUE)
outlier_ind <- which(data_imputados$F1_Score < lower_bound | data_imputados$F1_Score > upper_bound)

###### F1_Score (Grubbs)
test <- grubbs.test(data_imputados$F1_Score)
test2 <- grubbs.test(data_imputados$F1_Score, opposite = TRUE)
test
## 
##  Grubbs test for one outlier
## 
## data:  data_imputados$F1_Score
## G = 9.76785, U = 0.82901, p-value < 2.2e-16
## alternative hypothesis: highest value 9.37404866571267 is an outlier
test2
## 
##  Grubbs test for one outlier
## 
## data:  data_imputados$F1_Score
## G = 0.46580, U = 0.99961, p-value = 1
## alternative hypothesis: lowest value 0.40000698085322 is an outlier
# Reemplazo de outliers por NA y tratamiento
data_imputados$F1_Score[outlier_ind] <- NA
median_f1 <- median(data_imputados$F1_Score, na.rm = TRUE)
data_imputados$F1_Score[is.na(data_imputados$F1_Score)] <- median_f1

# Visualización
gg_f1 <- ggplot(data_imputados) +
  aes(x = "", y = F1_Score) +
  geom_boxplot(fill = "#0c4c8a") +
  ggtitle("F1 Score") +
  theme_minimal()

print(gg_f1)

Training_Time

#### Training_Time (Hampel)
lower_bound <- median(data_imputados$Training_Time, na.rm = TRUE) - 3 * mad(data_imputados$Training_Time, constant = 1, na.rm = TRUE)
upper_bound <- median(data_imputados$Training_Time, na.rm = TRUE) + 3 * mad(data_imputados$Training_Time, constant = 1, na.rm = TRUE)
outlier_ind <- which(data_imputados$Training_Time < lower_bound | data_imputados$Training_Time > upper_bound)

###### Training_Time (Grubbs)
test <- grubbs.test(data_imputados$Training_Time)
test2 <- grubbs.test(data_imputados$Training_Time, opposite = TRUE)
test
## 
##  Grubbs test for one outlier
## 
## data:  data_imputados$Training_Time
## G = 10.12941, U = 0.81612, p-value < 2.2e-16
## alternative hypothesis: highest value 46.9856257524936 is an outlier
test2
## 
##  Grubbs test for one outlier
## 
## data:  data_imputados$Training_Time
## G = 0.66003, U = 0.99922, p-value = 1
## alternative hypothesis: lowest value 0.103201614730368 is an outlier
# Reemplazo de outliers por NA y tratamiento
data_imputados$Training_Time[outlier_ind] <- NA
median_training_time <- median(data_imputados$Training_Time, na.rm = TRUE)
data_imputados$Training_Time[is.na(data_imputados$Training_Time)] <- median_training_time

# Visualización
gg_training_time <- ggplot(data_imputados) +
  aes(x = "", y = Training_Time) +
  geom_boxplot(fill = "#0c4c8a") +
  ggtitle("Training Time") +
  theme_minimal()

print(gg_training_time)

A manera de resumen se mostraran como quedaron los boxplot de las variables luego de tratar los outliers en cada una.

grid.arrange(gg_accuracy, gg_precision, gg_recall, gg_f1, gg_training_time, ncol = 5)

Análisis descriptivo de las variables

Análisis de variables numéricas

A continuación se procederá a analizar cada una de las variables que componen el dataset. Se hará teniendo en cuenta el estado original de la variable y el estado luego de la imputación.

Accuracy
# datos originales 
summary(data$Accuracy)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##  0.5038  0.6236  0.7578  0.8779  0.8824  9.7181      39
# datos tratados
summary(data_imputados$Accuracy)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.5038  0.6368  0.7578  0.7519  0.8583  0.9997

Se puede ver que al mantenerse constante el valor de la mediana y la disminución de la media es muy probable que la eliminacion de los valores atipicos permite que los datos resultantes no esten sesgados por esos valores extremos mejorando la representación de los valores típicos.

# datos originales 
coef_variacion <- (sd(data$Accuracy, na.rm = TRUE) / mean(data$Accuracy, na.rm = TRUE)) * 100
coef_variacion
## [1] 107.4343
# datos tratados
coef_variacion2 <- (sd(data_imputados$Accuracy) / mean(data_imputados$Accuracy)) * 100
coef_variacion2
## [1] 18.35139

En esta ocasión, el resultado menor de el coeficiente de variación para los datos imputados indica mayor homogeneidad en los datos y que son menos dispersos que como estaban originalmente.

Gráficos
gg_1 <- ggplot(data_imputados) +
  aes(x = "", y = Accuracy) +
  geom_boxplot(fill = "#0c4c8a") +
  ggtitle("Accuracy") +
  theme_minimal()

gg_2 <- ggplot(data) +
  aes(x = "", y = Accuracy) +
  geom_boxplot(fill = "#0c4c8a") +
  ggtitle("Accuracy") +
  theme_minimal()

grid.arrange(gg_2, gg_1, ncol = 2)
## Warning: Removed 39 rows containing non-finite outside the scale range
## (`stat_boxplot()`).

densidad<-ggplot(data, aes(x = Accuracy)) +
  geom_density(fill = "#0c4c8a", alpha = 0.7) +
  labs(x = "Accuracy", y = "Densidad", title = "Gráfico de Densidad de Accuracy") +
  theme_minimal()

densidad2<-ggplot(data_imputados, aes(x = Accuracy)) +
  geom_density(fill = "#0c4c8a", alpha = 0.7) +
  labs(x = "Accuracy", y = "Densidad", title = "Gráfico de Densidad de Accuracy") +
  theme_minimal()

grid.arrange(densidad, densidad2, ncol = 2)
## Warning: Removed 39 rows containing non-finite outside the scale range
## (`stat_density()`).

Se pueden ver con estos gráficos que luego de tratar los datos ahora estan mucho más concentrados y la densidad no se extiende a valores atípicos.

Precision
# datos originales 
summary(data$Precision)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##  0.4019  0.5632  0.7195  0.8129  0.8596  9.7320      19
# datos tratados
summary(data_imputados$Precision)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.4019  0.5672  0.7195  0.7064  0.8444  0.9990

Se puede ver que al mantenerse constante el valor de la mediana y la disminución de la media es muy probable que la eliminacion de los valores atipicos permite que los datos resultantes no esten sesgados por esos valores extremos mejorando la representación de los valores típicos.

# datos originales 
coef_variacion <- (sd(data$Precision, na.rm = TRUE) / mean(data$Precision, na.rm = TRUE)) * 100
coef_variacion
## [1] 104.7427
# datos tratados
coef_variacion2 <- (sd(data_imputados$Precision) / mean(data_imputados$Precision)) * 100
coef_variacion2
## [1] 23.4662

En esta ocasión, el resultado menor de el coeficiente de variación para los datos imputados indica mayor homogeneidad en los datos y que son menos dispersos que como estaban originalmente.

Gráficos
gg_1 <- ggplot(data_imputados) +
  aes(x = "", y = Precision) +
  geom_boxplot(fill = "#0c4c8a") +
  ggtitle("Precision") +
  theme_minimal()

gg_2 <- ggplot(data) +
  aes(x = "", y = Precision) +
  geom_boxplot(fill = "#0c4c8a") +
  ggtitle("Precision") +
  theme_minimal()

grid.arrange(gg_2, gg_1, ncol = 2)
## Warning: Removed 19 rows containing non-finite outside the scale range
## (`stat_boxplot()`).

densidad<-ggplot(data, aes(x = Precision)) +
  geom_density(fill = "#0c4c8a", alpha = 0.7) +
  labs(x = "Precision", y = "Densidad", title = "Gráfico de Densidad de Precision") +
  theme_minimal()

densidad2<-ggplot(data_imputados, aes(x = Precision)) +
  geom_density(fill = "#0c4c8a", alpha = 0.7) +
  labs(x = "Precision", y = "Densidad", title = "Gráfico de Densidad de Precision") +
  theme_minimal()

grid.arrange(densidad, densidad2, ncol = 2)
## Warning: Removed 19 rows containing non-finite outside the scale range
## (`stat_density()`).

Se pueden ver con estos gráficos que luego de tratar los datos ahora estan mucho más concentrados y la densidad no se extiende a valores atípicos.

Recall
# datos originales 
summary(data$Recall)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##  0.3001  0.4819  0.6493  0.7486  0.8404  9.3662      20
# datos tratados
summary(data_imputados$Recall)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.3001  0.4926  0.6493  0.6492  0.8262  0.9985

Se puede ver que al mantenerse constante el valor de la mediana y la disminución de la media es muy probable que la eliminacion de los valores atipicos permite que los datos resultantes no esten sesgados por esos valores extremos mejorando la representación de los valores típicos.

# datos originales 
coef_variacion <- (sd(data$Recall, na.rm = TRUE) / mean(data$Recall, na.rm = TRUE)) * 100
coef_variacion
## [1] 104.7911
# datos tratados
coef_variacion2 <- (sd(data_imputados$Recall) / mean(data_imputados$Recall)) * 100
coef_variacion2
## [1] 30.93555

En esta ocasión, el resultado menor de el coeficiente de variación para los datos imputados indica mayor homogeneidad en los datos y que son menos dispersos que como estaban originalmente.

Gráficos
gg_1 <- ggplot(data_imputados) +
  aes(x = "", y = Recall) +
  geom_boxplot(fill = "#0c4c8a") +
  ggtitle("Recall") +
  theme_minimal()

gg_2 <- ggplot(data) +
  aes(x = "", y = Recall) +
  geom_boxplot(fill = "#0c4c8a") +
  ggtitle("Recall") +
  theme_minimal()

grid.arrange(gg_2, gg_1, ncol = 2)
## Warning: Removed 20 rows containing non-finite outside the scale range
## (`stat_boxplot()`).

densidad<-ggplot(data, aes(x = Recall)) +
  geom_density(fill = "#0c4c8a", alpha = 0.7) +
  labs(x = "Recall", y = "Densidad", title = "Gráfico de Densidad de Recall") +
  theme_minimal()

densidad2<-ggplot(data_imputados, aes(x = Recall)) +
  geom_density(fill = "#0c4c8a", alpha = 0.7) +
  labs(x = "Recall", y = "Densidad", title = "Gráfico de Densidad de Recall") +
  theme_minimal()

grid.arrange(densidad, densidad2, ncol = 2)
## Warning: Removed 20 rows containing non-finite outside the scale range
## (`stat_density()`).

Se pueden ver con estos gráficos que luego de tratar los datos ahora estan mucho más concentrados y la densidad no se extiende a valores atípicos.

F1_Score
# datos originales 
summary(data$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
# datos tratados
summary(data_imputados$F1_Score)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.4000  0.5579  0.7086  0.6970  0.8301  0.9993

Se puede ver que al mantenerse constante el valor de la mediana y la disminución de la media es muy probable que la eliminacion de los valores atipicos permite que los datos resultantes no esten sesgados por esos valores extremos mejorando la representación de los valores típicos.

# datos originales 
coef_variacion <- (sd(data$F1_Score, na.rm = TRUE) / mean(data$F1_Score, na.rm = TRUE)) * 100
coef_variacion
## [1] 109.9297
# datos tratados
coef_variacion2 <- (sd(data_imputados$F1_Score) / mean(data_imputados$F1_Score)) * 100
coef_variacion2
## [1] 23.9237

En esta ocasión, el resultado menor de el coeficiente de variación para los datos imputados indica mayor homogeneidad en los datos y que son menos dispersos que como estaban originalmente.

Gráficos
gg_1 <- ggplot(data_imputados) +
  aes(x = "", y = F1_Score) +
  geom_boxplot(fill = "#0c4c8a") +
  ggtitle("F1_Score") +
  theme_minimal()

gg_2 <- ggplot(data) +
  aes(x = "", y = F1_Score) +
  geom_boxplot(fill = "#0c4c8a") +
  ggtitle("F1_Score") +
  theme_minimal()

grid.arrange(gg_2, gg_1, ncol = 2)
## Warning: Removed 20 rows containing non-finite outside the scale range
## (`stat_boxplot()`).

densidad<-ggplot(data, aes(x = F1_Score)) +
  geom_density(fill = "#0c4c8a", alpha = 0.7) +
  labs(x = "F1_Score", y = "Densidad", title = "Gráfico de Densidad de F1_Score") +
  theme_minimal()

densidad2<-ggplot(data_imputados, aes(x = F1_Score)) +
  geom_density(fill = "#0c4c8a", alpha = 0.7) +
  labs(x = "F1_Score", y = "Densidad", title = "Gráfico de Densidad de F1_Score") +
  theme_minimal()

grid.arrange(densidad, densidad2, ncol = 2)
## Warning: Removed 20 rows containing non-finite outside the scale range
## (`stat_density()`).

Se pueden ver con estos gráficos que luego de tratar los datos ahora estan mucho más concentrados y la densidad no se extiende a valores atípicos.

Training_Time
# datos originales 
summary(data$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
# datos tratados
summary(data_imputados$Training_Time)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.1032  1.2982  2.4347  2.4835  3.6862  4.9978

Se puede ver que al mantenerse constante el valor de la mediana y la disminución de la media es muy probable que la eliminacion de los valores atipicos permite que los datos resultantes no esten sesgados por esos valores extremos mejorando la representación de los valores típicos.

# datos originales 
coef_variacion <- (sd(data$Training_Time, na.rm = TRUE) / mean(data$Training_Time, na.rm = TRUE)) * 100
coef_variacion
## [1] 147.9032
# datos tratados
coef_variacion2 <- (sd(data_imputados$Training_Time) / mean(data_imputados$Training_Time)) * 100
coef_variacion2
## [1] 55.49392

En esta ocasión, el resultado menor de el coeficiente de variación para los datos imputados indica mayor homogeneidad en los datos y que son menos dispersos que como estaban originalmente.

Gráficos
gg_1 <- ggplot(data_imputados) +
  aes(x = "", y = Training_Time) +
  geom_boxplot(fill = "#0c4c8a") +
  ggtitle("TrainingTime") +
  theme_minimal()

gg_2 <- ggplot(data) +
  aes(x = "", y = Training_Time) +
  geom_boxplot(fill = "#0c4c8a") +
  ggtitle("TrainingTime") +
  theme_minimal()

grid.arrange(gg_2, gg_1, ncol = 2)
## Warning: Removed 20 rows containing non-finite outside the scale range
## (`stat_boxplot()`).

densidad<-ggplot(data, aes(x = Training_Time)) +
  geom_density(fill = "#0c4c8a", alpha = 0.7) +
  labs(x = "TrainingTime", y = "Densidad", title = "Gráfico de Densidad de TrainingTime") +
  theme_minimal()

densidad2<-ggplot(data_imputados, aes(x = Training_Time)) +
  geom_density(fill = "#0c4c8a", alpha = 0.7) +
  labs(x = "TrainingTime", y = "Densidad", title = "Gráfico de Densidad TrainingTime") +
  theme_minimal()

grid.arrange(densidad, densidad2, ncol = 2)
## Warning: Removed 20 rows containing non-finite outside the scale range
## (`stat_density()`).

Se pueden ver con estos gráficos que luego de tratar los datos ahora estan mucho más concentrados y la densidad no se extiende a valores atípicos.

Análisis de variables categóricas

Algorithm

Primero verificaremos las categorías presentes en esta variable. Luego validaremos si hay valores NA.

table(data$Algorithm)
## 
##        K-Means Neural Network  Random Forest            SVM 
##            163            135            126            136
NAS_TL<-is.na(data$Algorithm) 
data[NAS_TL,]
## # A tibble: 0 × 10
## # ℹ 10 variables: Algorithm <chr>, Framework <chr>, Problem_Type <chr>,
## #   Dataset_Type <chr>, Accuracy <dbl>, Precision <dbl>, Recall <dbl>,
## #   F1_Score <dbl>, Training_Time <dbl>, Date <dbl>

Dado que no encontramos NA, procederemos a graficar.

data_summary <- data %>%
  count(Algorithm) %>%
  mutate(Percentage = n / sum(n) * 100)

data_summary <- data %>%
  count(Algorithm) %>%
  mutate(Percentage = n / sum(n) * 100)

ggplot(data_summary, aes(x = Algorithm, y = n)) +
  geom_bar(stat = "identity", fill = "#0073B2") +
  geom_text(aes(label = paste0(round(Percentage, 1), "%")), 
            vjust = -0.5, color = "black") +  
  ggtitle("Frecuencia de Algorithm") +
  xlab("Algorithm") +
  ylab("Frecuencia") +
  theme_minimal()

Vemos en este gráfico que la categoría con menor representación es Random Forest y la de mayor es K-Means. Neural Network y SVM tienen cantidades parecidas.

Framework

Primero verificaremos las categorías presentes en esta variable. Luego validaremos si hay valores NA.

table(data$Framework)
## 
##        Keras      PyTorch Scikit-learn   TensorFlow 
##          124          135          134          167
NAS_TL<-is.na(data$Framework) 
data[NAS_TL,]
## # A tibble: 0 × 10
## # ℹ 10 variables: Algorithm <chr>, Framework <chr>, Problem_Type <chr>,
## #   Dataset_Type <chr>, Accuracy <dbl>, Precision <dbl>, Recall <dbl>,
## #   F1_Score <dbl>, Training_Time <dbl>, Date <dbl>

Dado que no encontramos NA, procederemos a graficar.

framework_summary <- data %>%
  count(Framework) %>%
  mutate(Percentage = n / sum(n) * 100)

ggplot(framework_summary, aes(x = Framework, y = n)) +
  geom_bar(stat = "identity", fill = "#0073B2") +
  geom_text(aes(label = paste0(round(Percentage, 1), "%")), 
            vjust = -0.5, color = "black") +
  ggtitle("Frecuencia de Framework") +
  xlab("Framework") +
  ylab("Frecuencia") +
  theme_minimal()

Vemos en este gráfico que la categoría con mayor representación es TensoFlow y la de menor es Keras. PyTorch y Scikit-learn tienen cantidades parecidas.

Problem_Type

Primero verificaremos las categorías presentes en esta variable. Luego validaremos si hay valores NA.

table(data$Problem_Type)
## 
## Classification     Clustering     Regression 
##            175            196            189
NAS_TL<-is.na(data$Problem_Type) 
data[NAS_TL,]
## # A tibble: 0 × 10
## # ℹ 10 variables: Algorithm <chr>, Framework <chr>, Problem_Type <chr>,
## #   Dataset_Type <chr>, Accuracy <dbl>, Precision <dbl>, Recall <dbl>,
## #   F1_Score <dbl>, Training_Time <dbl>, Date <dbl>

Dado que no encontramos NA, procederemos a graficar.

problem_type_summary <- data %>%
  count(Problem_Type) %>%
  mutate(Percentage = n / sum(n) * 100)

ggplot(problem_type_summary, aes(x = Problem_Type, y = n)) +
  geom_bar(stat = "identity", fill = "#0073B2") +
  geom_text(aes(label = paste0(round(Percentage, 1), "%")), 
            vjust = -0.5, color = "black") +  
  ggtitle("Frecuencia de Problem_Type") +
  xlab("Problem_Type") +
  ylab("Frecuencia") + 
  theme_minimal()

Vemos en este gráfico que la categoría con mayor representación es Clustering y la de menor es Classification.

Dataset_Type

Primero verificaremos las categorías presentes en esta variable. Luego validaremos si hay valores NA.

table(data$Dataset_Type)
## 
##       Image     Tabular        Text Time Series 
##         157         136         143         124
NAS_TL<-is.na(data$Dataset_Type) 
data[NAS_TL,]
## # A tibble: 0 × 10
## # ℹ 10 variables: Algorithm <chr>, Framework <chr>, Problem_Type <chr>,
## #   Dataset_Type <chr>, Accuracy <dbl>, Precision <dbl>, Recall <dbl>,
## #   F1_Score <dbl>, Training_Time <dbl>, Date <dbl>

Dado que no encontramos NA, procederemos a graficar.

dataset_type_summary <- data %>%
  count(Dataset_Type) %>%
  mutate(Percentage = n / sum(n) * 100)

ggplot(dataset_type_summary, aes(x = Dataset_Type, y = n)) +
  geom_bar(stat = "identity", fill = "#0073B2") +
  geom_text(aes(label = paste0(round(Percentage, 1), "%")), 
            vjust = -0.5, color = "black") +  
  xlab("Dataset_Type") +
  ylab("Frecuencia") +  
  theme_minimal()

Vemos en este gráfico que la categoría con menor representación es time series y la de mayor es Image.

Exploración de interacciones de variables por medio de graficos y pruebas análiticas (Bivariados)

Categórica vs Numérica
Algorithm vs Accuracy
ggplot(data_imputados, aes(x = Algorithm, y = Accuracy)) +
  geom_boxplot(fill = "#0c4c8a") +
  theme_minimal()

Framework vs Accuracy
ggplot(data_imputados, aes(x = Framework, y = Accuracy)) +
  geom_boxplot(fill = "#0c4c8a") +
  theme_minimal()

Problem_Type vs Accuracy
ggplot(data_imputados, aes(x = Problem_Type, y = Accuracy)) +
  geom_boxplot(fill = "#0c4c8a") +
  theme_minimal()

Dataset_Type vs Accuracy
ggplot(data_imputados, aes(x = Dataset_Type, y = Accuracy)) +
  geom_boxplot(fill = "#0c4c8a") +
  theme_minimal()

Para este primer análisis se comparó la precisión frente a variables como el tipo de problema, el tipo de dataset, el framework y el algoritmo. En el caso de los algoritmos, vemos que las medianas son muy similares, por tanto no hay diferencias muy grandes entre el accuracy de cada algoritmo. Lo mismo vemos para el framework, en este caso la caja de tensorflow es más grande por lo que podríamos pensar que los valores acá pueden estar más dispersos. Para el tipo de problema vemos que tambien vemos medianas similares, pero hay sesgos notorios en problemas de clasificación y regresión, mientras que según el dataset hay una ligera diferencia en la mediana del text, apareciendo un poco por encima de las demás.

Categórica vs Categórica
Algorithm vs Framework
ggplot(data_imputados, aes(x = Algorithm, fill = Framework)) +
  geom_bar(position = "fill") +
  theme_minimal()

Algorithm vs Dataset_Type
ggplot(data_imputados, aes(x = Algorithm, fill = Dataset_Type)) +
  geom_bar(position = "fill") +
  theme_minimal()

Algorithm vs Problem_Type
ggplot(data_imputados, aes(x = Algorithm, fill = Problem_Type)) +
  geom_bar(position = "fill") +
  theme_minimal()

En esta ocasión comparamos el comportamiento de la variable algoritmo con el framework, el tipo de dataset, y el tipo de problema. Frente al framework tenemos que Tensorflow está distribuido similarmente entre los diferentes algoritmos, mientras que para PyTorch se usa más para SVM y poco para K-Means. Por otro lado, en Keras tambien vemos una diferencia considerable entre algoritmos SVM y Neural Network o K-Means que casi duplica su barra. Frente al tipo de dataset, tenemos que las time series se distribuyen similarmente, pero para los tipo Tabular, vemos que las K-means o Random Forest se presentan más seguido que las SVM. Finalmente, frente al tipo de problemas vemos que es común encontrar relacionadas las Regressions con Neural Network o SVM, Clustering con K-Means y Classification con Neural Network.

Numérica vs Numérica
Accuracy vs Training_Time
ggplot(data_imputados, aes(x = Accuracy, y = Training_Time)) +
  geom_point() +
  geom_smooth(method = "lm", se = FALSE, color = "blue") +
  theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'

Precision vs Training_Time
ggplot(data_imputados, aes(x = Precision, y = Training_Time)) +
  geom_point() +
  geom_smooth(method = "lm", se = FALSE, color = "blue") +
  theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'

Recall vs Training_Time
ggplot(data_imputados, aes(x = Recall, y = Training_Time)) +
  geom_point() +
  geom_smooth(method = "lm", se = FALSE, color = "blue") +
  theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'

F1_Score vs Training_Time
ggplot(data_imputados, aes(x = F1_Score, y = Training_Time)) +
  geom_point() +
  geom_smooth(method = "lm", se = FALSE, color = "blue") +
  theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'

Para este caso, comparamos la variable Training_Time con las demás variables numéricas. En este caso vemos que para ninguna de las comparaciones veremos una tendencia clara que permita concluir que están estrechamente relacionadas entre si, pues los puntos estan muy dispersos y la linea de regresión no tiene una pendiente muy marcada.

Pruebas analíticas

Para realizar las pruebas analíticas primero determinaremos la normalidad de las variables numéricas.

normality_test <- function(data) {
  shapiro_test <- shapiro.test(data)
  return(shapiro_test$p.value > 0.05)
}

variables <- c("Accuracy", "Precision", "Recall", "F1_Score", "Training_Time")

normality_results <- tibble(
  Variable = variables,
  Normal_Distribution = sapply(variables, function(v) normality_test(data_imputados[[v]]))
)

normality_results
## # A tibble: 5 × 2
##   Variable      Normal_Distribution
##   <chr>         <lgl>              
## 1 Accuracy      FALSE              
## 2 Precision     FALSE              
## 3 Recall        FALSE              
## 4 F1_Score      FALSE              
## 5 Training_Time FALSE

Al confirmar que no tenemos distribuciones normales en las variables numéricas se procederá a aplicar pruebas como Kruskal-Wallis, para las categóricas vs numéricas, Chi-cuadrado para categóricas vs categóricas y Correlación de Spearman para numérica vs numérica. ##### Kruskal-Wallis ##### Algorithm vs Accuracy

kruskal.test(Accuracy ~ Algorithm, data = data_imputados)
## 
##  Kruskal-Wallis rank sum test
## 
## data:  Accuracy by Algorithm
## Kruskal-Wallis chi-squared = 1.4043, df = 3, p-value = 0.7045
Framework vs Accuracy
kruskal.test(Accuracy ~ Framework, data = data_imputados)
## 
##  Kruskal-Wallis rank sum test
## 
## data:  Accuracy by Framework
## Kruskal-Wallis chi-squared = 1.5348, df = 3, p-value = 0.6743
Problem_Type vs Accuracy
kruskal.test(Accuracy ~ Problem_Type, data = data_imputados)
## 
##  Kruskal-Wallis rank sum test
## 
## data:  Accuracy by Problem_Type
## Kruskal-Wallis chi-squared = 5.6331, df = 2, p-value = 0.05981
Dataset_Type vs Accuracy
kruskal.test(Accuracy ~ Dataset_Type, data = data_imputados)
## 
##  Kruskal-Wallis rank sum test
## 
## data:  Accuracy by Dataset_Type
## Kruskal-Wallis chi-squared = 6.5511, df = 3, p-value = 0.08767

Vemos valores de 1.4043, 1.5348, 1.4043 y 6.5511. Los 3 primeros valores nos permiten pensar que no hay diferencias significativas entre los diferentes algoritmos, frameworks y tipos de problema. Sin embargo, entre los diferentes tipos de dataset tenemos un p-value más alto, por lo que podemos pensar que existen diferencias en el Accuracy según el tipo de dataset.

Categórica vs Categórica

Algorithm vs Framework
chisq.test(table(data_imputados$Algorithm, data_imputados$Framework))
## 
##  Pearson's Chi-squared test
## 
## data:  table(data_imputados$Algorithm, data_imputados$Framework)
## X-squared = 5.6463, df = 9, p-value = 0.7747
Algorithm vs Dataset_Type
chisq.test(table(data_imputados$Algorithm, data_imputados$Dataset_Type))
## 
##  Pearson's Chi-squared test
## 
## data:  table(data_imputados$Algorithm, data_imputados$Dataset_Type)
## X-squared = 4.9299, df = 9, p-value = 0.8404
Algorithm vs Problem_Type
chisq.test(table(data_imputados$Algorithm, data_imputados$Problem_Type))
## 
##  Pearson's Chi-squared test
## 
## data:  table(data_imputados$Algorithm, data_imputados$Problem_Type)
## X-squared = 6.5396, df = 6, p-value = 0.3655

Los resultados de estas chi-cuadrado nos permiten concluir que no hay asociación significativa entre ninguno de estos pares de variables. Por tanto los algoritmos no influyen significativamente entre el tipo de dataset, problema o framework.

Numérica vs Numérica

Accuracy vs Training_Time
cor.test(data_imputados$Accuracy, data_imputados$Training_Time, method = "spearman")
## Warning in cor.test.default(data_imputados$Accuracy,
## data_imputados$Training_Time, : Cannot compute exact p-value with ties
## 
##  Spearman's rank correlation rho
## 
## data:  data_imputados$Accuracy and data_imputados$Training_Time
## S = 30398237, p-value = 0.3622
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
##         rho 
## -0.03857282
Precision vs Training_Time
cor.test(data_imputados$Precision, data_imputados$Training_Time, method = "spearman")
## Warning in cor.test.default(data_imputados$Precision,
## data_imputados$Training_Time, : Cannot compute exact p-value with ties
## 
##  Spearman's rank correlation rho
## 
## data:  data_imputados$Precision and data_imputados$Training_Time
## S = 28219396, p-value = 0.3969
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
##        rho 
## 0.03586852
Recall vs Training_Time
cor.test(data_imputados$Recall, data_imputados$Training_Time, method = "spearman")
## Warning in cor.test.default(data_imputados$Recall,
## data_imputados$Training_Time, : Cannot compute exact p-value with ties
## 
##  Spearman's rank correlation rho
## 
## data:  data_imputados$Recall and data_imputados$Training_Time
## S = 30938845, p-value = 0.1777
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
##         rho 
## -0.05704299

Al observar los coeficientes de correlación vemos que no hay evidencia para suficiente para afirmar que haya relación entre las variables comparadas previamente.

Pregunta problema

Luego de haber hecho una exploración general del dataset, se retomará la pregunta planteada en la introducción del presente trabajo.
¿Qué frameworks producen los modelos más precisos en cada tipo de problema?.
Primero, procederemos a graficar las variables framework, precision y Problem_Type, luego realizaremos una prueba Krustal-Wallis

summary_accuracy <- data_imputados %>%
  group_by(Framework, Problem_Type) %>%
  summarise(mean_accuracy = mean(Accuracy, na.rm = TRUE)) %>%
  arrange(Problem_Type, desc(mean_accuracy))
## `summarise()` has grouped output by 'Framework'. You can override using the
## `.groups` argument.
ggplot(summary_accuracy, aes(x = Framework, y = mean_accuracy, fill = Problem_Type)) +
  geom_bar(stat = "identity", position = position_dodge()) +
  labs(title = "Precisión Media por Framework y Tipo de Problema",
       x = "Framework", y = "Precisión Media") +
  theme_minimal()

data_filtered <- data_imputados %>% filter(Problem_Type == "Classification")
data_filtered2 <- data_imputados %>% filter(Problem_Type == "Clustering")
data_filtered3 <- data_imputados %>% filter(Problem_Type == "Regression")

# Realizar la prueba de Kruskal-Wallis
kruskal_test <- kruskal.test(Accuracy ~ Framework, data = data_filtered)
kruskal_test2 <- kruskal.test(Accuracy ~ Framework, data = data_filtered2)
kruskal_test3 <- kruskal.test(Accuracy ~ Framework, data = data_filtered3)
print(kruskal_test)
## 
##  Kruskal-Wallis rank sum test
## 
## data:  Accuracy by Framework
## Kruskal-Wallis chi-squared = 0.77939, df = 3, p-value = 0.8544
print(kruskal_test2)
## 
##  Kruskal-Wallis rank sum test
## 
## data:  Accuracy by Framework
## Kruskal-Wallis chi-squared = 0.24542, df = 3, p-value = 0.9699
print(kruskal_test3)
## 
##  Kruskal-Wallis rank sum test
## 
## data:  Accuracy by Framework
## Kruskal-Wallis chi-squared = 5.537, df = 3, p-value = 0.1364
# Realizar la prueba de Kruskal-Wallis

Las pruebas anteriores concluyen, de manera general, que no hay diferencias significativas entre uno u otro framework. Sin embargo, si miramos detenidamente el gráfico, se puede pensar que, para problemas de tipo Regression, sería óptimo usar Scikit-learn, pues tiene la media más alta de Accuracy entre todos los frameworks, mientras que Clustering o Classification no podríamos seleccionar uno sobre otro, concidiendo así con la prueba analítica realizada.

Referencias