Pregunta problema

En el ámbito del análisis de datos y el aprendizaje automático, uno de los desafíos más relevantes es determinar qué algoritmo ofrece la mayor precisión en problemas de regresión aplicados a series de tiempo, considerando el tiempo de entrenamiento como un factor crítico. La eficacia de un modelo no solo se mide por su capacidad para hacer predicciones precisas, sino también por el tiempo que requiere para entrenarse adecuadamente.

En este contexto, surge la pregunta: ¿Qué algoritmo logra la mayor precisión relativa al tiempo de entrenamiento en problemas de regresión sobre datos de series de tiempo, y cómo varía esta eficiencia entre diferentes frameworks, como PyTorch, TensorFlow, Keras y Scikit-learn?

Con el objetivo de abordar esta cuestión, llevaremos a cabo un análisis comparativo entre los frameworks mencionados, enfocados en identificar cuál de ellos permite un entrenamiento más eficiente sin comprometer la eficacia del modelo. Para establecer un criterio de evaluación, consideraremos como umbral de eficacia aquellos modelos que logren alcanzar valores superiores a 0.6 en su desempeño. De este modo, buscamos no solo optimizar el tiempo de entrenamiento, sino también garantizar resultados satisfactorios en la calidad de las predicciones.

Contexto del EDA y su base de datos

Este conjunto de datos está diseñado para abordar problemas relacionados con Inteligencia Artificial (IA), donde se analizan algoritmos, frameworks, tipos de problemas y tipos de datos. Los valores numéricos representan métricas de desempeño, como la precisión o el tiempo de entrenamiento de los modelos. Diccionario de variables:

1.Algorithm (categórica): Tipo de algoritmo de IA utilizado(‘Neural Network’, ‘Random Forest’, ‘SVM’, ‘K-Means’).

2.Framework (categórica): Framework o biblioteca utilizada para la implementación del modelo de IA(‘TensorFlow’, ‘PyTorch’, ‘Keras’, ‘Scikit-learn’).

3.Problem_Type (categórica): Tipo de problema abordado por el modelo.(‘Classification’, ‘Regression’, ‘Clustering’).

4.Dataset_Type (categórica): Tipo de datos utilizados en el entrenamiento del modelo(‘Image’, ‘Text’, ‘Tabular’, ‘Time Series’.).

5.Accuracy (numérica, continua): Precisión del modelo en el conjunto de prueba (entre 0 y 1).

6.Precision (numérica, continua): Precisión del modelo (valor entre 0 y 1).

7.Recall (numérica, continua): Sensibilidad o capacidad del modelo para identificar correctamente los positivos (entre 0 y 1).

8.F1_Score (numérica, continua): Medida armónica entre precisión y recall (entre 0 y 1).

9.Training_Time (numérica, continua): Tiempo de entrenamiento del modelo en horas.

10.Date (fecha): Fecha en la que se realizó la evaluación del modelo, cubriendo el último año.

Para que quede más claro, expresemos todas las variables mediante una tabla con la función kable:

# Crear un dataframe con la información de las variables
variables_df <- data.frame(
  Variable = c("Algorithm", "Framework", "Problem_Type", "Dataset_Type", 
               "Accuracy", "Precision", "Recall", "F1_Score", 
               "Training_Time", "Date"),
  Naturaleza = c("Cualitativa/Categórica", "Cualitativa/Categórica", "Cualitativa/Categórica", "Cualitativa/Categórica", 
                 "Cuantitativa/Numérica", "Cuantitativa/Numérica", 
                 "Cuantitativa/Numérica", "Cuantitativa/Numérica", 
                 "Cuantitativa/Numérica", "Cualitativa/Catégorica"),
  Nivel_de_Medición = c("Nominal", "Nominal", "Nominal", "Nominal", 
                        "Continua", "Continua", "Continua", "Continua", 
                        "Continua", "Ordinal"),
  Criterio_de_Clasificación = c("Tipo de algoritmo de IA", "Framework o biblioteca utilizada", 
                                "Tipo de problema", "Tipo de datos", 
                                "Precisión del modelo (0 a 1)", 
                                "Precisión del modelo (0 a 1)", 
                                "Capacidad de identificar positivos (0 a 1)", 
                                "Medida armónica (0 a 1)", 
                                "Tiempo en horas", "Fecha")
)
# Quitar los guiones bajos de los nombres de las columnas si fuera necesario
names(variables_df) <- gsub("_", " ", names(variables_df))
# Mostrar la tabla con kable y aplicar estilos
kable(variables_df, caption = "Tabla 1: Clasificación de Variables", align = "c") %>%  # Alinear columnas al centro
  kable_styling(full_width = TRUE, bootstrap_options = c("striped", "hover", "condensed", "responsive"), font_size = 15, fixed_thead = TRUE) %>%
  column_spec(1, bold = TRUE, width = "10em") %>%   # Resaltar la columna de Variable
  column_spec(2, width = "12em") %>%                # Ajustar ancho de Naturaleza
  column_spec(3, width = "12em") %>%                # Ajustar ancho de Nivel de Medición
  column_spec(4, width = "22em") %>%                # Ajustar ancho de Criterio de Clasificación
  row_spec(0, bold = TRUE, background = "#ccc", color = "black")   # Resaltar la primera fila (encabezado)
Tabla 1: Clasificación de Variables
Variable Naturaleza Nivel de Medición Criterio de Clasificación
Algorithm Cualitativa/Categórica Nominal Tipo de algoritmo de IA
Framework Cualitativa/Categórica Nominal Framework o biblioteca utilizada
Problem_Type Cualitativa/Categórica Nominal Tipo de problema
Dataset_Type Cualitativa/Categórica Nominal Tipo de datos
Accuracy Cuantitativa/Numérica Continua Precisión del modelo (0 a 1)
Precision Cuantitativa/Numérica Continua Precisión del modelo (0 a 1)
Recall Cuantitativa/Numérica Continua Capacidad de identificar positivos (0 a 1)
F1_Score Cuantitativa/Numérica Continua Medida armónica (0 a 1)
Training_Time Cuantitativa/Numérica Continua Tiempo en horas
Date Cualitativa/Catégorica Ordinal Fecha

Resumen de la base de datos

Primero, iniciaremos haciendo un resumen de la base de datos con la función summary:

summary(Base)
##   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

Características generales de la base de datos.

La dimension de la base de datos es:

dim(Base)
## [1] 560  10

Es decir, se compone de 560 filas y 10 columnas.

Filtros de limpieza

Tratamiento de NA

Para comenzar, eliminaremos las entradas de la base de datos que contengan datos faltantes. Esto garantizará que solo se consideren registros completos y relevantes para el análisis, evitando cualquier sesgo o distorsión en los resultados debido a la falta de información clave.

Veamos la cantidad de NA mediante un gráfico:

missmap(Base, col = c("red", "blue"), legend = TRUE)

El gráfico nos indica que existe un 2% de información faltante. El primer indicio que nos permite aplicar la técnica de eliminación, es que la muestra o el porcentaje de valores valtantes no es mayor al 5%, por lo que, no se perdera mucha información; el segundo indicio, la base de datos fue generada aleatoriamente, lo que hace que los datos sean del tipo MCAR, por lo tanto, la eliminación no nos llevará a resultados sesgados. Analizemos cuantos NA hay:

# Calcular el número de NAs
na_counts <- sapply(Base[, c("Algorithm", "Framework", "Precision", "Training_Time", 
                             "Problem_Type", "Dataset_Type", "Accuracy", 
                             "Recall", "F1_Score", "Date")], 
                    function(x) sum(is.na(x)))

# Usar una única llamada a cat() para mostrar el texto y resultados en un solo bloque
cat("Cantidad de NA o entradas vacías por columna:\n\n", 
    paste(names(na_counts), na_counts, sep = ": ", collapse = "\n"))
## Cantidad de NA o entradas vacías por columna:
## 
##  Algorithm: 0
## Framework: 0
## Precision: 19
## Training_Time: 20
## Problem_Type: 0
## Dataset_Type: 0
## Accuracy: 39
## Recall: 20
## F1_Score: 20
## Date: 0
# Crear un dataframe con la información de las variables
variables_df <- data.frame(
  Variable = c("Algorithm", "Framework", "Problem_Type", "Dataset_Type", 
               "Accuracy", "Precision", "Recall", "F1_Score", 
               "Training_Time", "Date"),
  Cantidad = c("0", "0", "0", "0", 
                 "39", "19", 
                 "20", "20", 
                 "20", "0"),
  Porcentaje = c("0", "0", "0", "0", 
                 round(((39 * 100) / 560), 3), round(((19 * 100) / 560), 3), 
                 round(((20 * 100) / 560), 3), round(((20 * 100) / 560), 3), 
                 round(((20 * 100) / 560), 3), "0")
)

# Quitar los guiones bajos de los nombres de las columnas si fuera necesario
names(variables_df) <- gsub("_", " ", names(variables_df))

# Mostrar la tabla con kable y aplicar estilos
kable(variables_df, caption = "Tabla 2: Cantidad de NA por Variable", align = "c") %>%  # Alinear columnas al centro
  kable_styling(full_width = TRUE, bootstrap_options = c("striped", "hover", "condensed", "responsive"), font_size = 15, fixed_thead = TRUE) %>%
  column_spec(1, bold = TRUE, width = "10em") %>%   # Resaltar la columna de Variable
  column_spec(2, width = "10em") %>%                # Ajustar ancho de Naturaleza
  column_spec(3, width = "10em") %>%                # Ajustar ancho de Nivel de Medición
  row_spec(0, bold = TRUE, background = "#ccc", color = "black")   # Resaltar la primera fila (encabezado)
Tabla 2: Cantidad de NA por Variable
Variable Cantidad Porcentaje
Algorithm 0 0
Framework 0 0
Problem_Type 0 0
Dataset_Type 0 0
Accuracy 39 6.964
Precision 19 3.393
Recall 20 3.571
F1_Score 20 3.571
Training_Time 20 3.571
Date 0 0

La variable porcentaje es basada en el total de filas que hay, en otras palabras, representa la cantidad de NA, por ejemplo, los 39 datos faltantes representa cierto porcentaje del 560.

Sabiendo lo anterior, apliquemos la función na.omit para eliminar los NA de la base de datos.

Base_omit <- na.omit(Base)

head(Base_omit)
## # A tibble: 6 × 10
##   Algorithm      Framework   Problem_Type Dataset_Type Accuracy Precision Recall
##   <chr>          <chr>       <chr>        <chr>           <dbl>     <dbl>  <dbl>
## 1 Neural Network Keras       Clustering   Image           0.885     0.595  0.969
## 2 SVM            Keras       Clustering   Text            0.842     0.842  0.875
## 3 SVM            Scikit-lea… Regression   Tabular         0.723     0.686  0.301
## 4 K-Means        PyTorch     Regression   Image           0.637     0.626  7.45 
## 5 Neural Network Scikit-lea… Regression   Image           0.713     0.676  0.480
## 6 SVM            PyTorch     Regression   Image           0.897     9.73   0.781
## # ℹ 3 more variables: F1_Score <dbl>, Training_Time <dbl>, Date <dttm>

A continuación, verifiquemos la distribución de cada variable númerica para ver si hay un cambio o no.

# Cargar las librerías necesarias
library(ggplot2)
library(gridExtra)

# Crear una función para generar los histogramas
crear_histogramas <- function(data_original, data_sin_na, variable, nombre_variable) {
  # Histograma de datos originales
  ggp1 <- ggplot(data_original, aes_string(x = variable)) +
    geom_histogram(aes(y = ..density..), bins = 30, fill = "#FFB3B3", color = "#FF6666", alpha = 0.8) +
    geom_density(color = "#FF3333", size = 1) +
    ggtitle(paste("Distribución original de", nombre_variable)) +
    xlab(nombre_variable) + ylab("Densidad") +
    theme_light() +
    theme(plot.title = element_text(size = 14, hjust = 0.5, margin = margin(b = 10))) +
    xlim(0, 2)  # Ajusta este límite según tus datos
  
  # Histograma de datos sin NA
  ggp2 <- ggplot(data_sin_na, aes_string(x = variable)) +
    geom_histogram(aes(y = ..density..), bins = 30, fill = "#B3D9FF", color = "#66B2FF", alpha = 0.8) +
    geom_density(color = "#3399FF", size = 1) +
    ggtitle(paste("Distribución sin NA de", nombre_variable)) +
    xlab(nombre_variable) + ylab("Densidad") +
    theme_light() +
    theme(plot.title = element_text(size = 14, hjust = 0.5, margin = margin(b = 10))) +
    xlim(0, 2)  # Ajusta este límite según tus datos
  
  # Mostrar ambos gráficos uno al lado del otro
  grid.arrange(ggp1, ggp2, ncol = 2)
}

# Eliminar los NA una vez y guardar en una variable
Base_omit <- na.omit(Base)

# Lista de variables cuantitativas y sus nombres legibles
variables <- list(
  Accuracy = "Accuracy",
  Precision = "Precision",
  Recall = "Recall",
  F1_Score = "F1 Score",
  Training_Time = "Training Time"
)

# Iterar sobre las variables y generar los histogramas
for (var in names(variables)) {
  crear_histogramas(Base, Base_omit, var, variables[[var]])
  cat("\n\n\n\n\n\n\n\n")
}

Conclusión

A partir de los histogramas, se puede observar que las distribuciones de las variables Accuracy, Precision, Recall, F1 Score, Training Time permacenen prácticamente inalteradas antes y después de la eliminación de los valores faltantes (NA). Lo que nos indica que la eliminación de los datos no afectó significativamente la estructura de los datos, por ende, nos permite trabajar con una base de datos más limpia y sin sesgos ni problemas derivados de los valores faltantes. Con la base de datos sin NA, podemos avanzar hacia los siguientes pasos de este análisis con mayor confianza en la viabilidad de los datos.

missmap(Base_omit, col = c("red", "blue"), legend = TRUE)

Después de la eliminación ya no queda ningún valor faltante en la base de datos.

Distribución de combinaciones de algoritmos y frameworks

combinations <- expand.grid(
  Algorithm = c("Neural Network", "Random Forest", "SVM", "K-Means"),
  Framework = c("TensorFlow", "PyTorch", "Keras", "Scikit-learn"),
  stringsAsFactors = FALSE
)

combination_counts <- data.frame(Algorithm = character(), Framework = character(), Count = integer(), stringsAsFactors = FALSE)

for (i in 1:nrow(combinations)) {
  algo <- combinations$Algorithm[i]
  framework <- combinations$Framework[i]
  count <- nrow(Base_omit[Base_omit$Algorithm == algo & Base_omit$Framework == framework, ])
  combination_counts <- rbind(combination_counts, data.frame(Algorithm = algo, Framework = framework, Count = count))
}

combination_counts <- combination_counts %>%
  mutate(Percentage = Count / sum(Count) * 100)

plot <- plot_ly(combination_counts, 
                labels = ~paste(Algorithm, "y", Framework),
                values = ~Percentage, 
                type = 'pie',
                textinfo = 'none',
                insidetextorientation = 'radial') %>%
  layout(title = "Distribución de combinaciones de Algoritmos y Frameworks", showlegend = TRUE)

plot

El análisis de la distribución de combinaciones de algoritmos y frameworks muestra una visualización clara que permite identificar qué combinaciones son más y menos frecuentes en el conjunto de datos. Al observar el gráfico de torta, se puede notar que ciertas combinaciones, como K-Means con TensorFlow con el 8.24% del total, son las más comunes, lo que podría indicar una tendencia en la preferencia de estas herramientas en la comunidad. Además, la identificación de combinaciones menos frecuentes podría resaltar oportunidades para explorar modelos alternativos que, aunque no sean ampliamente utilizados, podrían ofrecer resultados competitivos. Asimismo, la identificación de combinaciones menos frecuentes, como Random Forest con Keras con solo el 3.83%. Este análisis preliminar proporciona un contexto valioso que complementa la evaluación de eficacia y precisión en los filtros posteriores.

Resumen Estadístico

Catégoricas

A continuación, haremos un resumen estadístico de las variables catégoricas, creando tablas de frecuencia para tener una representación mejor a la hora de analizar. Cable aclarar que no se tomará en cuenta la variable Date para términos de eficiencia.

# Frecuencia absoluta
freq_absoluta <- table(Base$Algorithm)


# Frecuencia relativa
freq_relativa <- prop.table(freq_absoluta)


# Crear un data frame con las frecuencias absolutas y relativas
tabla_frecuencias <- data.frame(
  Framework = names(freq_absoluta),
  Frecuencia_Absoluta = as.numeric(freq_absoluta),
  Frecuencia_Relativa = round(100 * as.numeric(freq_relativa), 2)  # Convertir a porcentaje
)

# Quitar los guiones bajos de los nombres de las columnas si fuera necesario
names(tabla_frecuencias) <- gsub("_", " ", names(tabla_frecuencias))

# Mostrar la tabla con kable y aplicar estilos
tabla_alg <- kable(tabla_frecuencias, caption = "Tabla 3: Frecuencia de Algorithm", align = "c") %>%  # Alinear columnas al centro
  kable_styling(full_width = TRUE, bootstrap_options = c("striped", "hover", "condensed", "responsive"), font_size = 15, fixed_thead = TRUE) %>%
  column_spec(1, bold = TRUE, width = "10em") %>%   # Resaltar la columna de Variable
  column_spec(2, width = "10em") %>%                # Ajustar ancho de Naturaleza
  column_spec(3, width = "10em") %>%                # Ajustar ancho de Nivel de Medición
  row_spec(0, bold = TRUE, background = "#9eb5d9", color = "black")   # Resaltar la primera fila (encabezado)


# Gráfico de barras corregido
G1 <- ggplot(tabla_frecuencias, aes(x = Framework, y = `Frecuencia Absoluta`)) + 
  geom_bar(width = 0.7, stat = "identity", position = position_dodge(), fill = "#9eb5d9", color="black") +  
  ylim(c(0, max(tabla_frecuencias$`Frecuencia Absoluta`) + 10)) +  # Ajustar el límite superior del eje Y para mejor visualización
  labs(x = "Tipo de Algoritmo", y = "Frecuencias (Porcentajes)") +   
  
  # Añadir las etiquetas de texto
  geom_text(aes(label = paste0(`Frecuencia Absoluta`, " (", `Frecuencia Relativa`, "%)")),  
            vjust = -0.9, color = "black", hjust = 0.5,
            position = position_dodge(0.9), size = 4.5) +   
  
  # Estilo del texto en el eje X
  theme(axis.text.x = element_text(angle = 0, vjust = 1, hjust = 1)) +      
  theme_bw(base_size = 16) +
  
  # Añadir el título del gráfico
  facet_wrap(~"Distribución de Algorithm")

# Mostrar el gráfico

tabla_alg
Tabla 3: Frecuencia de Algorithm
Framework Frecuencia Absoluta Frecuencia Relativa
K-Means 163 29.11
Neural Network 135 24.11
Random Forest 126 22.50
SVM 136 24.29
G1

La tabla nos muestra la distribución de las frecuencias de la variable Algorithm. Podemos destacar:

  • La moda es K-Means siendo el algoritmo más utilizado en esta base de datos con una frecuencia de 163.
  • Random Forest obtuvo la menor frecuencia, es decir, es el menos utilizado.
  • Se puede concluir que si se utiliza más K-Means puede ser porque sea más rápido, tenga menos costo, etc. Pueden ser varias las razones.
# Frecuencia absoluta
freq_absoluta <- table(Base$Framework)


# Frecuencia relativa
freq_relativa <- prop.table(freq_absoluta)


# Crear un data frame con las frecuencias absolutas y relativas
tabla_frecuencias <- data.frame(
  Framework = names(freq_absoluta),
  Frecuencia_Absoluta = as.numeric(freq_absoluta),
  Frecuencia_Relativa = round(100 * as.numeric(freq_relativa), 2)  # Convertir a porcentaje
)

# Quitar los guiones bajos de los nombres de las columnas si fuera necesario
names(tabla_frecuencias) <- gsub("_", " ", names(tabla_frecuencias))

# Mostrar la tabla con kable y aplicar estilos
tabla_alg <- kable(tabla_frecuencias, caption = "Tabla 3: Frecuencia de Framework", align = "c") %>%  # Alinear columnas al centro
  kable_styling(full_width = TRUE, bootstrap_options = c("striped", "hover", "condensed", "responsive"), font_size = 15, fixed_thead = TRUE) %>%
  column_spec(1, bold = TRUE, width = "10em") %>%   # Resaltar la columna de Variable
  column_spec(2, width = "10em") %>%                # Ajustar ancho de Naturaleza
  column_spec(3, width = "10em") %>%                # Ajustar ancho de Nivel de Medición
  row_spec(0, bold = TRUE, background = "#d8bfd8", color = "black")   # Resaltar la primera fila (encabezado)


# Gráfico de barras corregido
G2 <- ggplot(tabla_frecuencias, aes(x = Framework, y = `Frecuencia Absoluta`)) + 
  geom_bar(width = 0.7, stat = "identity", position = position_dodge(), fill = "#d8bfd8", color="black") +  
  ylim(c(0, max(tabla_frecuencias$`Frecuencia Absoluta`) + 10)) +  # Ajustar el límite superior del eje Y para mejor visualización
  labs(x = "Tipo de Framework", y = "Frecuencias (Porcentajes)") +   
  
  # Añadir las etiquetas de texto
  geom_text(aes(label = paste0(`Frecuencia Absoluta`, " (", `Frecuencia Relativa`, "%)")),  
            vjust = -0.9, color = "black", hjust = 0.5,
            position = position_dodge(0.9), size = 4.5) +   
  
  # Estilo del texto en el eje X
  theme(axis.text.x = element_text(angle = 0, vjust = 1, hjust = 1)) +      
  theme_bw(base_size = 16) +
  
  # Añadir el título del gráfico
  facet_wrap(~"Distribución de Framework")

# Mostrar el gráfico

tabla_alg
Tabla 3: Frecuencia de Framework
Framework Frecuencia Absoluta Frecuencia Relativa
Keras 124 22.14
PyTorch 135 24.11
Scikit-learn 134 23.93
TensorFlow 167 29.82
G2

El gráfico muestra la distribución de la frecuencia de uso de diferentes frameworks de machine learning en el dataset. Podemos destacar lo siguiente:

  • TensorFlow es el framework más utilizado, con un 29.82% de frecuencia relativa, lo que indica una clara preferencia por este framework en comparación con los otros. Es decir, es la moda.
  • PyTorch y Scikit-learn tienen una adopción similar, con un 24.11% y 23.93%, respectivamente, lo que refleja una popularidad relativamente equilibrada.
  • Keras es el menos utilizado, con un 22.14%, aunque la diferencia con los otros frameworks no es muy significativa.
# Frecuencia absoluta
freq_absoluta <- table(Base$Problem_Type)


# Frecuencia relativa
freq_relativa <- prop.table(freq_absoluta)


# Crear un data frame con las frecuencias absolutas y relativas
tabla_frecuencias <- data.frame(
  Framework = names(freq_absoluta),
  Frecuencia_Absoluta = as.numeric(freq_absoluta),
  Frecuencia_Relativa = round(100 * as.numeric(freq_relativa), 2)  # Convertir a porcentaje
)

# Quitar los guiones bajos de los nombres de las columnas si fuera necesario
names(tabla_frecuencias) <- gsub("_", " ", names(tabla_frecuencias))

# Mostrar la tabla con kable y aplicar estilos
tabla_alg <- kable(tabla_frecuencias, caption = "Tabla 3: Frecuencia de Problem Type", align = "c") %>%  # Alinear columnas al centro
  kable_styling(full_width = TRUE, bootstrap_options = c("striped", "hover", "condensed", "responsive"), font_size = 15, fixed_thead = TRUE) %>%
  column_spec(1, bold = TRUE, width = "10em") %>%   # Resaltar la columna de Variable
  column_spec(2, width = "10em") %>%                # Ajustar ancho de Naturaleza
  column_spec(3, width = "10em") %>%                # Ajustar ancho de Nivel de Medición
  row_spec(0, bold = TRUE, background = "#bdecb6", color = "black")   # Resaltar la primera fila (encabezado)


# Gráfico de barras corregido
G3 <- ggplot(tabla_frecuencias, aes(x = Framework, y = `Frecuencia Absoluta`)) + 
  geom_bar(width = 0.7, stat = "identity", position = position_dodge(), fill = "#bdecb6", color="black") +  
  ylim(c(0, max(tabla_frecuencias$`Frecuencia Absoluta`) + 10)) +  # Ajustar el límite superior del eje Y para mejor visualización
  labs(x = "Tipo de Problema", y = "Frecuencias (Porcentajes)") +   
  
  # Añadir las etiquetas de texto
  geom_text(aes(label = paste0(`Frecuencia Absoluta`, " (", `Frecuencia Relativa`, "%)")),  
            vjust = -0.9, color = "black", hjust = 0.5,
            position = position_dodge(0.9), size = 4.5) +   
  
  # Estilo del texto en el eje X
  theme(axis.text.x = element_text(angle = 0, vjust = 1, hjust = 1)) +      
  theme_bw(base_size = 16) +
  
  # Añadir el título del gráfico
  facet_wrap(~"Distribución de Problem Type")

# Mostrar el gráfico

tabla_alg
Tabla 3: Frecuencia de Problem Type
Framework Frecuencia Absoluta Frecuencia Relativa
Classification 175 31.25
Clustering 196 35.00
Regression 189 33.75
G3

El gráfico muestra la distribución de los tipos de problemas abordados en el dataset. Podemos destacar las siguientes conclusiones:

  • Clustering es el tipo de problema más común en el dataset (es la moda) con una frecuencia del 35% (196 casos). Esto indica que una proporción considerable de los modelos está orientada hacia la agrupación de datos.

  • Regression ocupa el segundo lugar, con un 33.75% (189 casos), lo que sugiere que también es un enfoque muy utilizado en los modelos de este conjunto de datos.

  • Classification tiene la menor frecuencia relativa, con un 31.25% (175 casos), aunque sigue siendo un tipo de problema importante dentro del dataset.

  • En general, no existe una diferencia abismal entre cada uno de los problemas. Es decir, son todos muy comúnes.

# Frecuencia absoluta
freq_absoluta <- table(Base$Dataset_Type)


# Frecuencia relativa
freq_relativa <- prop.table(freq_absoluta)


# Crear un data frame con las frecuencias absolutas y relativas
tabla_frecuencias <- data.frame(
  Framework = names(freq_absoluta),
  Frecuencia_Absoluta = as.numeric(freq_absoluta),
  Frecuencia_Relativa = round(100 * as.numeric(freq_relativa), 2)  # Convertir a porcentaje
)

# Quitar los guiones bajos de los nombres de las columnas si fuera necesario
names(tabla_frecuencias) <- gsub("_", " ", names(tabla_frecuencias))

# Mostrar la tabla con kable y aplicar estilos
tabla_alg <- kable(tabla_frecuencias, caption = "Tabla 3: Frecuencia de Data Type", align = "c") %>%  # Alinear columnas al centro
  kable_styling(full_width = TRUE, bootstrap_options = c("striped", "hover", "condensed", "responsive"), font_size = 15, fixed_thead = TRUE) %>%
  column_spec(1, bold = TRUE, width = "10em") %>%   # Resaltar la columna de Variable
  column_spec(2, width = "10em") %>%                # Ajustar ancho de Naturaleza
  column_spec(3, width = "10em") %>%                # Ajustar ancho de Nivel de Medición
  row_spec(0, bold = TRUE, background = "#c78676", color = "black")   # Resaltar la primera fila (encabezado)


# Gráfico de barras corregido
G3 <- ggplot(tabla_frecuencias, aes(x = Framework, y = `Frecuencia Absoluta`)) + 
  geom_bar(width = 0.7, stat = "identity", position = position_dodge(), fill = "#c78676", color="black") +  
  ylim(c(0, max(tabla_frecuencias$`Frecuencia Absoluta`) + 10)) +  # Ajustar el límite superior del eje Y para mejor visualización
  labs(x = "Tipo de Dato", y = "Frecuencias (Porcentajes)") +   
  
  # Añadir las etiquetas de texto
  geom_text(aes(label = paste0(`Frecuencia Absoluta`, " (", `Frecuencia Relativa`, "%)")),  
            vjust = -0.9, color = "black", hjust = 0.5,
            position = position_dodge(0.9), size = 4.5) +   
  
  # Estilo del texto en el eje X
  theme(axis.text.x = element_text(angle = 0, vjust = 1, hjust = 1)) +      
  theme_bw(base_size = 16) +
  
  # Añadir el título del gráfico
  facet_wrap(~"Distribución de Data Type")

# Mostrar el gráfico

tabla_alg
Tabla 3: Frecuencia de Data Type
Framework Frecuencia Absoluta Frecuencia Relativa
Image 157 28.04
Tabular 136 24.29
Text 143 25.54
Time Series 124 22.14
G3

El gráfico muestra los diferentes tipos de datos utilizados en la base de datos. Se puede destacar lo siguiente:

  • Image es el tipo de dato más utilizado, con un 28.04% (157 casos). Esto sugiere que una gran parte de los modelos se está aplicando a datos visuales, lo que podría implicar un enfoque importante en tareas como el reconocimiento de imágenes.
  • Text ocupa el segundo lugar, representando el 25.54% (143 casos). Esto indica que también hay un interés significativo en el procesamiento de datos de texto, como la clasificación de documentos.
  • Tabular y Time Series tienen frecuencias más bajas, con 24.29% (136 casos) y 22.14% (124 casos), respectivamente, pero siguen siendo relevantes en el conjunto de datos.

Númericas

A continuación, haremos un resumen estadístico de las variables númericas, analizando las medidades de tendencia central. Cable aclarar que no se tomará en cuenta la variable Date para términos de eficiencia.

# Filtrar solo las variables numéricas
Base_numericas <- Base_omit %>% select_if(is.numeric)

# Crear una tabla con media, mediana, varianza, desviación estándar, rango, mínimo y máximo
resumen_estadistico <- data.frame(
  Variable = colnames(Base_numericas),  
  Media = sapply(Base_numericas, function(x) round(mean(x, na.rm = TRUE), 4)),    
  Mediana = sapply(Base_numericas, function(x) round(median(x, na.rm = TRUE), 4)),
  Moda = sapply(Base_numericas, function(x) round(mfv(x)[1], 4)),
  Varianza = sapply(Base_numericas, function(x) round(var(x, na.rm = TRUE), 4)),
  Desviacion_Estandar = sapply(Base_numericas, function(x) round(sd(x, na.rm = TRUE), 4)),  
  Minimo = sapply(Base_numericas, function(x) round(min(x, na.rm = TRUE), 4)),
  Maximo = sapply(Base_numericas, function(x) round(max(x, na.rm = TRUE), 4)),
  Rango = sapply(Base_numericas, function(x) round(max(x, na.rm = TRUE) - min(x, na.rm = TRUE), 4)),
  Q1 = sapply(Base_numericas, function(x) round(quantile(x, 0.25, na.rm = TRUE), 4)),
  Q3 = sapply(Base_numericas, function(x) round(quantile(x, 0.75, na.rm = TRUE), 4))
)

# Cambiar el nombre de la columna 'Desviacion_Estandar' a 'Desviación Estándar'
colnames(resumen_estadistico) <- gsub("Desviacion_Estandar", "Desviación Estándar", colnames(resumen_estadistico))

rownames(resumen_estadistico) <- NULL

# Mostrar la tabla usando kable con más espacio entre las columnas
kable(resumen_estadistico, caption = "Resumen Estadístico de Variables Cuantitativas") %>%
  kable_styling(full_width = TRUE, bootstrap_options = c("striped", "hover", "condensed", "responsive"), font_size = 16) %>%
  column_spec(1, bold = TRUE, width = "15em") %>%   # Aumentar el ancho de la columna Variable
  column_spec(2, width = "8em") %>% 
  column_spec(3, width = "8em") %>%
  column_spec(4, width = "8em") %>%
  column_spec(5, width = "8em") %>%
  column_spec(6, width = "10em") %>%
  column_spec(7, width = "10em") %>%
  column_spec(8, width = "8em") %>%
  column_spec(9, width = "8em") %>%
  column_spec(10, width = "8em") %>%
  column_spec(11, width = "8em") %>%
  
  # Aumentar el ancho de las demás columnas
  row_spec(0, bold = TRUE, background = "#ccc", color = "black")   # Resaltar el encabezado de la tabla
Resumen Estadístico de Variables Cuantitativas
Variable Media Mediana Moda Varianza Desviación Estándar Minimo Maximo Rango Q1 Q3
Accuracy 0.8458 0.7490 0.5038 0.6751 0.8217 0.5038 9.7181 9.2143 0.6183 0.8698
Precision 0.8387 0.7275 0.4031 0.8664 0.9308 0.4031 9.7320 9.3289 0.5661 0.8670
Recall 0.7611 0.6513 0.3001 0.6912 0.8314 0.3001 9.3662 9.0661 0.4898 0.8365
F1_Score 0.8014 0.7031 0.4000 0.7655 0.8749 0.4000 9.3740 8.9740 0.5486 0.8396
Training_Time 3.0598 2.4809 0.1032 21.5892 4.6464 0.1032 46.9856 46.8824 1.2982 3.8446

Aquí hemos creado una tabla con las medidas, rangos, y cálculos más importantes del resumen estadístico. Vamos a interpretarlos:

# Crear un data frame con las medias y desviaciones estándar
data_grafico <- data.frame(
  Variable = colnames(Base_numericas),
  Media = sapply(Base_numericas, mean, na.rm = TRUE),
  Desviacion = sapply(Base_numericas, sd, na.rm = TRUE)
)

# Gráfico de barras con líneas de error utilizando ggplot
grafico_barras <- ggplot(data_grafico, aes(x = Variable, y = Media)) +
  geom_bar(stat = "identity", fill = "steelblue", alpha = 0.8) +
  geom_errorbar(aes(ymin = Media - Desviacion, ymax = Media + Desviacion), width = 0.2) +
  ylab("Media con Desviación Estándar") +
  xlab("Variable") +
  ggtitle("Media y Desviación Estándar por Métrica") +
  theme_minimal()

# Convertir el gráfico a interactivo con plotly
grafico_interactivo <- ggplotly(grafico_barras)

# Mostrar el gráfico interactivo
grafico_interactivo

El gráfico nos muestra la media y la distribución estándar de cada variable. Las variables Accuracy, F1 Score, Precision, Recall tienen una media similar, lo cual es lógico puesto que están limitadas con un rango de 0-1. Por otro lado, Training Time tiene una media más alta puesto que no esta limitada de alguna manera. Respecto a la desviación estándar, Accuracy, F1 Score, Precision, Recall no presentan una distribución tan alta, sin embargo, training time varia considerablemente en comparación con las demás métricas.

Pruebas de normalidad y simetria

1.Algorithm (categórica): Tipo de algoritmo de IA utilizado(‘Neural Network’, ‘Random Forest’, ‘SVM’, ‘K-Means’).

cat("Frecuencia de Algoritmos:\n")
## Frecuencia de Algoritmos:
print(table(Base_omit$Algorithm))
## 
##        K-Means Neural Network  Random Forest            SVM 
##            135            113             95            105
barplot(table(Base_omit$Algorithm), main="Frecuencia de Algoritmos", col="lightblue")

En la gráfica de frecuencia de algoritmos, se observa que K-Means es el algoritmo más utilizado con una frecuencia superior a 140 casos. Este alto uso puede deberse a su simplicidad y su aplicabilidad en problemas de agrupamiento. Neural Networks, con aproximadamente 120 casos, también es un algoritmo popular, especialmente en tareas que requieren modelado avanzado y reconocimiento de patrones complejos. Por otro lado, Random Forest y SVM muestran frecuencias similares, ambas alrededor de 100. Estos algoritmos, aunque robustos para clasificación y regresión, parecen ser algo menos utilizados en comparación con K-Means y Neural Networks, lo que podría reflejar su mayor complejidad.

2.Framework (categórica): Framework o biblioteca utilizada para la implementación del modelo de IA(‘TensorFlow’, ‘PyTorch’, ‘Keras’, ‘Scikit-learn’).

cat("Frecuencia de Frameworks:\n")
## Frecuencia de Frameworks:
print(table(Base_omit$Framework))
## 
##        Keras      PyTorch Scikit-learn   TensorFlow 
##           97          111           98          142
barplot(table(Base_omit$Framework), main="Frecuencia de Frameworks", col="lightgreen")

Podemos observar que TensorFlow es el framework más empleado, con una frecuencia cercana a los 150 casos. Este resultado es consistente con la popularidad de TensorFlow en la industria, dado su amplio soporte para el desarrollo de modelos de aprendizaje profundo y su comunidad activa. PyTorch y Scikit-learn presentan frecuencias similares, alrededor de los 120 casos cada uno. PyTorch, conocido por su flexibilidad y eficiencia en la investigación, y Scikit-learn, por su facilidad de uso en modelos más tradicionales de machine learning, parecen ser opciones preferidas en varios contextos. Keras, con aproximadamente 100 casos, es el menos utilizado, probablemente porque muchos desarrolladores prefieren TensorFlow, que ahora incorpora Keras como una API de alto nivel.

3.Problem_Type (categórica): Tipo de problema abordado por el modelo.(‘Classification’, ‘Regression’, ‘Clustering’).

cat("Frecuencia de Tipos de Problemas:\n")
## Frecuencia de Tipos de Problemas:
print(table(Base_omit$Problem_Type))
## 
## Classification     Clustering     Regression 
##            139            153            156
barplot(table(Base_omit$Problem_Type), main="Frecuencia de Tipos de Problemas", col="lightcoral")

Existe un comportamiento equilibrado entre los tres tipos de problemas principales abordados por los modelos: Classification, Clustering y Regression, cada uno con una frecuencia cercana a 150. Este equilibrio refleja una diversidad de aplicaciones, lo que sugiere que se abordan tanto tareas de clasificación supervisada como de predicción continua y agrupamiento no supervisado. La Clasificación es clave en aplicaciones como el reconocimiento de patrones y el análisis de texto, mientras que la Regresión es fundamental para predicciones cuantitativas, como el pronóstico de ventas o precios. El uso frecuente de Agrupamiento indica un enfoque fuerte en el análisis exploratorio de datos.

4.Dataset_Type (categórica): Tipo de datos utilizados en el entrenamiento del modelo(‘Image’, ‘Text’, ‘Tabular’, ‘Time Series’.).

cat("Frecuencia de Tipos de Datos:\n")
## Frecuencia de Tipos de Datos:
print(table(Base_omit$Dataset_Type))
## 
##       Image     Tabular        Text Time Series 
##         122         112         117          97
barplot(table(Base_omit$Dataset_Type), main="Frecuencia de Tipos de Datos", col="lightyellow")

El gráfico muestra que los datos tipo “Image” son los más utilizados en el entrenamiento de modelos, con una frecuencia cercana a 130, mientras que los tipos “Tabular” y “Text” tienen frecuencias similares, alrededor de 110, indicando un uso significativo pero menor que el de imágenes. Los datos tipo “Time Series” son los menos comunes, con una frecuencia cercana a 90. Aunque hay una distribución relativamente equilibrada entre los diferentes tipos de datos, se observa una ligera preferencia hacia el uso de datos tipo imagen.

5.Accuracy (numérica, continua): Precisión del modelo en el conjunto de prueba (entre 0 y 1).

cat("Análisis de Accuracy:\n")
## Análisis de Accuracy:
hist(Base_omit$Accuracy, main="Distribucion de Accuracy", col="skyblue", breaks=20)

shapiro_test <- shapiro.test(Base_omit$Accuracy)
cat("Prueba de Normalidad (Shapiro-Wilk):\n")
## Prueba de Normalidad (Shapiro-Wilk):
print(shapiro_test)
## 
##  Shapiro-Wilk normality test
## 
## data:  Base_omit$Accuracy
## W = 0.22539, p-value < 2.2e-16
library(moments)
skewness_acc <- skewness(Base_omit$Accuracy, na.rm = TRUE)
cat("Simetría (Skewness) de Accuracy:\n")
## Simetría (Skewness) de Accuracy:
print(skewness_acc)
## [1] 8.121139
qqnorm(Base_omit$Accuracy)
qqline(Base_omit$Accuracy, col = "red")

El análisis de la variable Accuracy muestra una distribución altamente asimétrica y no normal. El histograma revela que la mayoría de los valores de precisión del modelo se agrupan en rangos muy bajos, lo que indica que el modelo tiende a tener una precisión limitada en el conjunto de prueba. El Q-Q plot confirma la falta de normalidad, ya que los datos se desvían significativamente de la línea de referencia, especialmente en las colas, sugiriendo la presencia de valores extremos (outliers). Esto es corroborado por el test de normalidad de Shapiro-Wilk, que arroja un valor de p extremadamente bajo (p < 2.2e-16), rechazando la hipótesis de normalidad. Además, la alta simetría positiva (skewness de 7.84) indica que la distribución está sesgada hacia la derecha, lo que refuerza la idea de que existen pocos casos con alta precisión, mientras que la mayoría están concentrados en valores bajos.

6.Precision (numérica, continua): Precisión del modelo (valor entre 0 y 1).

cat("Análisis de Precision:\n")
## Análisis de Precision:
hist(Base_omit$Precision, main="Distribucion de Precision", col="lightpink", breaks=20)

shapiro_test <- shapiro.test(Base_omit$Precision)
cat("Prueba de Normalidad (Shapiro-Wilk):\n")
## Prueba de Normalidad (Shapiro-Wilk):
print(shapiro_test)
## 
##  Shapiro-Wilk normality test
## 
## data:  Base_omit$Precision
## W = 0.26457, p-value < 2.2e-16
skewness_prec <- skewness(Base_omit$Precision, na.rm = TRUE)
cat("Simetría (Skewness) de Precision:\n")
## Simetría (Skewness) de Precision:
print(skewness_prec)
## [1] 7.305445
qqnorm(Base_omit$Precision)
qqline(Base_omit$Precision, col = "red")

El análisis de la variable Precision muestra una distribución marcadamente asimétrica, donde la mayoría de los valores se concentran en la parte inferior, cerca de 0, tal como se ilustra en el histograma. Esta gráfica revela que los modelos evaluados tienen predominantemente una precisión baja, con pocos casos que se extienden hacia valores más altos. La larga cola a la derecha del histograma indica la presencia de algunos valores atípicos que logran puntuaciones más elevadas, pero que son significativamente menos frecuentes. Además, el alto valor de skewness (7.85) refuerza la observación de esta asimetría positiva, indicando que la mayoría de las observaciones se agrupan en la parte inferior de la escala, mientras que unos pocos casos alcanzan valores más altos.

7.Recall (numérica, continua): Sensibilidad o capacidad del modelo para identificar correctamente los positivos (entre 0 y 1).

cat("Análisis de Recall:\n")
## Análisis de Recall:
hist(Base_omit$Recall, main="Distribucion de Recall", col="lightgreen", breaks=20)

shapiro_test <- shapiro.test(Base_omit$Recall)
cat("Prueba de Normalidad (Shapiro-Wilk):\n")
## Prueba de Normalidad (Shapiro-Wilk):
print(shapiro_test)
## 
##  Shapiro-Wilk normality test
## 
## data:  Base_omit$Recall
## W = 0.31101, p-value < 2.2e-16
skewness_rec <- skewness(Base_omit$Recall, na.rm = TRUE)
cat("Simetría (Skewness) de Recall:\n")
## Simetría (Skewness) de Recall:
print(skewness_rec)
## [1] 7.071078
qqnorm(Base_omit$Recall)
qqline(Base_omit$Recall, col = "red")

La variable Recall muestra una distribución altamente asimétrica y no normal, con una asimetría positiva significativa (Skewness = 7.10). La mayoría de los valores se concentran cerca de 0. Sin embargo, existen algunos valores atípicos altos, lo que refleja que en ciertas instancias el modelo logra un mejor desempeño. El Q-Q plot confirma la desviación significativa de la normalidad, en las colas, mientras que la prueba de Shapiro-Wilk (W = 0.31686, p < 2.2e-16) refuerza el rechazo de la hipótesis de normalidad. Por tanto, este comportamiento sugiere la necesidad de aplicar transformaciones o modelos robustos que no asuman una distribución normal para analizar y mejorar el rendimiento del modelo respecto a esta métrica.

8.F1_Score (numérica, continua): Medida armónica entre precisión y recall (entre 0 y 1).

cat("Análisis de F1_Score:\n")
## Análisis de F1_Score:
hist(Base_omit$F1_Score, main="Distribucion de F1_Score", col="lightgray", breaks=20)

shapiro_test <- shapiro.test(Base_omit$F1_Score)
cat("Prueba de Normalidad (Shapiro-Wilk):\n")
## Prueba de Normalidad (Shapiro-Wilk):
print(shapiro_test)
## 
##  Shapiro-Wilk normality test
## 
## data:  Base_omit$F1_Score
## W = 0.25105, p-value < 2.2e-16
skewness_f1 <- skewness(Base_omit$F1_Score, na.rm = TRUE)
cat("Simetría (Skewness) de F1_Score:\n")
## Simetría (Skewness) de F1_Score:
print(skewness_f1)
## [1] 7.718489
qqnorm(Base_omit$F1_Score)
qqline(Base_omit$F1_Score, col = "red")

El análisis de la variable F1_Score, que mide el balance entre precisión y recall, revela una distribución altamente asimétrica y no normal. El modelo tiene un bajo rendimiento en cuanto a la precisión y el recall combinados para la mayoría de las instancias. Al igual que en el análisis de Recall, algunos valores atípicos aparecen en el extremo superior, aunque son poco frecuentes. El valor de Skewness es extremadamente alto (7.64), lo que sugiere una asimetría positiva, mientras que la prueba de normalidad Shapiro-Wilk (W = 0.24849, p < 2.2e-16) confirma que los datos no siguen una distribución normal.

9.Training_Time (numérica, continua): Tiempo de entrenamiento del modelo en horas.

cat("Análisis de Training_Time:\n")
## Análisis de Training_Time:
hist(Base_omit$Training_Time, main="Distribucion de Training Time", col="orange", breaks=20)

shapiro_test <- shapiro.test(Base_omit$Training_Time)
cat("Prueba de Normalidad (Shapiro-Wilk):\n")
## Prueba de Normalidad (Shapiro-Wilk):
print(shapiro_test)
## 
##  Shapiro-Wilk normality test
## 
## data:  Base_omit$Training_Time
## W = 0.33971, p-value < 2.2e-16
skewness_tt <- skewness(Base_omit$Training_Time, na.rm = TRUE)
cat("Simetría (Skewness) de Training Time:\n")
## Simetría (Skewness) de Training Time:
print(skewness_tt)
## [1] 7.532192
qqnorm(Base_omit$Training_Time)
qqline(Base_omit$Training_Time, col = "red")

El análisis del tiempo de entrenamiento (Training_Time) revela que los datos no siguen una distribución normal, como lo indica la prueba de normalidad de Shapiro-Wilk (W = 0.3478, p-valor < 2.2e-16), lo que nos lleva a rechazar la hipótesis nula de normalidad. La simetría (skewness) de 7.55 muestra una distribución sesgada a la derecha. Esto se refleja en el histograma, donde la mayor frecuencia de datos se concentra en los tiempos más bajos. Además, el gráfico Q-Q normal respalda esta conclusión, mostrando desviaciones notables en los extremos, especialmente en la cola izquierda, confirmando la no normalidad de los datos.

10.Date (fecha): Fecha en la que se realizó la evaluación del modelo, cubriendo el último año.

cat("Análisis de Date:\n")
## Análisis de Date:
plot(Base_omit$Date, Base_omit$Accuracy, main="Accuracy a lo largo del tiempo", xlab="Fecha", ylab="Accuracy", col="blue", type="o")

El gráfico de dispersión muestra la precisión del modelo desde julio de 2023 hasta julio de 2024. En el eje horizontal se representan las fechas y en el eje vertical los valores de precisión, que varían de 0 a 10. La mayoría de los puntos de datos se agrupan cerca del valor de precisión cero, indicando que en general la precisión del modelo ha sido baja durante este período. Sin embargo, hay algunos valores atípicos que alcanzan hasta 10, lo que sugiere que en ciertas ocasiones el modelo ha logrado una precisión significativamente alta. Estos picos de alta precisión, aunque esporádicos, destacan momentos en los que el modelo ha funcionado excepcionalmente bien.

Tratamiento de Datos Atípicos

Para comenzar, imputaremos según sea necesario las entradas de la base de datos que contengan datos atípicos. Esto garantizará que solo se consideren registros completos y relevantes para el análisis, evitando cualquier sesgo o distorsión en los resultados.

Histogramas y Caja de Bigotes

Comenzaremos graficando los histogramas y caja de bigotes para verificar la presencia de datos atípicos:

# Librería necesaria
library(ggplot2)
library(patchwork)

# Crear gráficos
graf1 <- ggplot(data.frame(value = Base_omit$Accuracy), aes(x = value)) +
  geom_histogram(aes(y = ..density..), bins = 30, fill = "#99CCFF", color = "black", alpha = 0.7) +
  stat_function(fun = dnorm, args = list(mean = mean(Base_omit$Accuracy), sd = sd(Base_omit$Accuracy)), color = "red", size = 1) +
  ggtitle("Histograma Acurracy") +
  xlab("Acurracy") + ylab("Densidad") +
  theme_minimal() +
  theme(plot.title = element_text(size = 14, hjust = 0.5))

box1 <- ggplot(data.frame(value = Base_omit$Accuracy), aes(x = "", y = value)) +
  geom_boxplot(fill = "#99CCFF", color = "black", outlier.colour = "red", outlier.size = 3, width = 0.4) + 
  ggtitle("Boxplot de Accuracy") +
  ylab("Accuracy") + 
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 14))

# Repetimos para Precision, Recall, F1_Score y Training_Time (mismo formato)
graf2 <- ggplot(data.frame(value = Base_omit$Precision), aes(x = value)) +
  geom_histogram(aes(y = ..density..), bins = 30, fill = "#99CCFF", color = "black", alpha = 0.7) +
  stat_function(fun = dnorm, args = list(mean = mean(Base_omit$Precision), sd = sd(Base_omit$Precision)), color = "red", size = 1) +
  ggtitle("Histograma Precision") +
  xlab("Precision") + ylab("Densidad") +
  theme_minimal() +
  theme(plot.title = element_text(size = 14, hjust = 0.5))

box2 <- ggplot(data.frame(value = Base_omit$Precision), aes(x = "", y = value)) +
  geom_boxplot(fill = "#99CCFF", color = "black", outlier.colour = "red", outlier.size = 3, width = 0.4) + 
  ggtitle("Boxplot de Precision") +
  ylab("Precision") + 
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 14))

graf3 <- ggplot(data.frame(value = Base_omit$Recall), aes(x = value)) +
  geom_histogram(aes(y = ..density..), bins = 30, fill = "#99CCFF", color = "black", alpha = 0.7) +
  stat_function(fun = dnorm, args = list(mean = mean(Base_omit$Recall), sd = sd(Base_omit$Recall)), color = "red", size = 1) +
  ggtitle("Histograma Recall") +
  xlab("Recall") + ylab("Densidad") +
  theme_minimal() +
  theme(plot.title = element_text(size = 14, hjust = 0.5))

box3 <- ggplot(data.frame(value = Base_omit$Recall), aes(x = "", y = value)) +
  geom_boxplot(fill = "#99CCFF", color = "black", outlier.colour = "red", outlier.size = 3, width = 0.4) + 
  ggtitle("Boxplot de Recall") +
  ylab("Recall") + 
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 14))

graf4 <- ggplot(data.frame(value = Base_omit$F1_Score), aes(x = value)) +
  geom_histogram(aes(y = ..density..), bins = 30, fill = "#99CCFF", color = "black", alpha = 0.7) +
  stat_function(fun = dnorm, args = list(mean = mean(Base_omit$F1_Score), sd = sd(Base_omit$F1_Score)), color = "red", size = 1) +
  ggtitle("Histograma F1 Score") +
  xlab("F1 Score") + ylab("Densidad") +
  theme_minimal() +
  theme(plot.title = element_text(size = 14, hjust = 0.5))

box4 <- ggplot(data.frame(value = Base_omit$F1_Score), aes(x = "", y = value)) +
  geom_boxplot(fill = "#99CCFF", color = "black", outlier.colour = "red", outlier.size = 3, width = 0.4) + 
  ggtitle("Boxplot de F1 Score") +
  ylab("F1 Score") + 
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 14))

graf5 <- ggplot(data.frame(value = Base_omit$Training_Time), aes(x = value)) +
  geom_histogram(aes(y = ..density..), bins = 30, fill = "#99CCFF", color = "black", alpha = 0.7) +
  stat_function(fun = dnorm, args = list(mean = mean(Base_omit$Training_Time), sd = sd(Base_omit$Training_Time)), color = "red", size = 1) +
  ggtitle("Histograma Training Time") +
  xlab("Training Time") + ylab("Densidad") +
  theme_minimal() +
  theme(plot.title = element_text(size = 14, hjust = 0.5))

box5 <- ggplot(data.frame(value = Base_omit$Training_Time), aes(x = "", y = value)) +
  geom_boxplot(fill = "#99CCFF", color = "black", outlier.colour = "red", outlier.size = 3, width = 0.4) + 
  ggtitle("Boxplot de Training Time") +
  ylab("Training Time") + 
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 14))


(graf1 + box1)

Se pueden ver a simple vista 6 valores atípicos por encima del tercer cuartil. Estos outliers podrían influir de manera significativa en la media y otros estadísticos. Además, la distribución de Accuracy presenta un sesgo hacia la izquierda, con la mayoría de los valores concentrados cerca de 0.

(graf2 + box2)

Se observan varios valores atípicos por encima del tercer cuartil (aproximadamente 7) en el boxplot de Precision. Estos outliers podrían estar afectando la media y otros estadísticos importantes, lo que será considerado en los análisis posteriores. La distribución de Precision está claramente sesgada hacia la izquierda, con la mayoría de los valores concentrados cerca de 0.

(graf3 + box3)

A simple vista se observan 7 valores atípicos por encima del tercer cuartil. La distribución está visiblemente sesgada hacia la izquierda, con la mayoría de los valores concentrados cerca de 0.

(graf4 + box4)

Se observan 6 valores atípicos por encima del tercer cuartil. Al igual que los otros estos outliers podrían estar afectando la media y otros estadísticos importantes. La distribución está sesgada hacia la izquierda, con la mayoría de los valores concentrados cerca de 0.

(graf5 + box5)

Se ven 6 valores atípicos por encima del tercer cuartil. También presenta un sesgo hacia la izquierda y como los otros la mayoría de sus valores concentrados cerca de 0.

Pruebas

Gracias a los histogramas y al diagrama de caja de bigotes nos dimos cuenta de dos cosas: Ninguna de las variables númericas sigue una distribución normal (Probado con Shapiro), y existen a simple vista valores atípicos. Con todo esto, haremos una prueba para comprobar la presencia de estos valores. Descartamos la prueba de Grubbs; porque nuestros datos no siguen una distribución normal; la prueba de Dixon; ya que la muestra es mayor a 25. Por lo que, proseguiremos con la prueba de Rosner:

test <- rosnerTest(Base_omit$Accuracy, k = 10)

test$all.stats
##    i    Mean.i      SD.i     Value Obs.Num     R.i+1 lambda.i+1 Outlier
## 1  0 0.8457621 0.8216572 9.7180796      15 10.798076   3.833870    TRUE
## 2  1 0.8259135 0.7069241 8.2944274     196 10.564803   3.833271    TRUE
## 3  2 0.8091679 0.6125670 7.9008618     232 11.577010   3.832670    TRUE
## 4  3 0.7932315 0.5124044 7.1274667     110 12.361788   3.832068    TRUE
## 5  4 0.7789652 0.4151830 5.9788899     239 12.524415   3.831464    TRUE
## 6  5 0.7672273 0.3338475 5.2598564      77 13.457130   3.830859    TRUE
## 7  6 0.7570629 0.2565839 5.2005460     112 17.317859   3.830252    TRUE
## 8  7 0.7469870 0.1449455 0.9997069     200  1.743551   3.829643   FALSE
## 9  8 0.7464126 0.1446072 0.9983484     422  1.742208   3.829033   FALSE
## 10 9 0.7458388 0.1442696 0.9981670      99  1.749004   3.828422   FALSE

Basándonos en la prueba de Rosner, identificamos 7 valores atípicos en las observaciones: 15, 196, 232, 110, 239, 77 y 112. Estos valores, al estar por encima de 1 en una métrica que debe estar dentro del rango 0-1, no son correctos y afectan negativamente las medidas de tendencia central como la media.

test <- rosnerTest(Base_omit$Precision, k = 10)

test$all.stats
##    i    Mean.i      SD.i    Value Obs.Num    R.i+1 lambda.i+1 Outlier
## 1  0 0.8386718 0.9308086 9.732008       6  9.55442   3.833870    TRUE
## 2  1 0.8187762 0.8310328 9.674189     223 10.65591   3.833271    TRUE
## 3  2 0.7989210 0.7180191 8.932619     250 11.32797   3.832670    TRUE
## 4  3 0.7806431 0.6061150 7.044472     439 10.33439   3.832068    TRUE
## 5  4 0.7665353 0.5286183 6.207645     111 10.29308   3.831464    TRUE
## 6  5 0.7542529 0.4614512 5.760933     433 10.84986   3.830859    TRUE
## 7  6 0.7429256 0.3955383 5.432777     241 11.85688   3.830252    TRUE
## 8  7 0.7322910 0.3266570 4.145151     157 10.44784   3.829643    TRUE
## 9  8 0.7245345 0.2834703 4.075645     288 11.82174   3.829033    TRUE
## 10 9 0.7169010 0.2341822 4.055990      96 14.25851   3.828422    TRUE

Basándonos en la prueba de Rosner, identificamos 10 valores atípicos, lo cual difiere de los 7 valores atípicos previamente observados en el boxplot. Esta diferencia puede deberse a la mayor sensibilidad de la prueba de Rosner para detectar outliers. Los 10 valores identificados, al estar por encima de 1 en una métrica que debe estar entre 0 y 1, no son válidos.

test <- rosnerTest(Base_omit$Recall, k = 10)

test$all.stats
##    i    Mean.i      SD.i     Value Obs.Num     R.i+1 lambda.i+1 Outlier
## 1  0 0.7610971 0.8314054 9.3661823     114 10.350047   3.833870    TRUE
## 2  1 0.7418463 0.7255257 7.7377491     157  9.642529   3.833271    TRUE
## 3  2 0.7261605 0.6460189 7.4548096       4 10.415561   3.832670    TRUE
## 4  3 0.7110399 0.5622109 5.7659164     270  8.991068   3.832068    TRUE
## 5  4 0.6996551 0.5089064 5.7263733      88  9.877490   3.831464    TRUE
## 6  5 0.6883081 0.4497505 5.4998481     420 10.698244   3.830859    TRUE
## 7  6 0.6774222 0.3874519 5.4366692     308 12.283452   3.830252    TRUE
## 8  7 0.6666303 0.3144283 4.8590798     303 13.333562   3.829643    TRUE
## 9  8 0.6571020 0.2428199 3.4388274     221 11.455922   3.829033    TRUE
## 10 9 0.6507655 0.2034434 0.3000943     218  1.723680   3.828422   FALSE

Basándonos en la prueba de Rosner, identificamos 9 valores atípicos, lo cual difiere de los 7 valores atípicos previamente observados en el boxplot. Esta diferencia puede deberse a la mayor sensibilidad de la prueba de Rosner para detectar outliers. Los 9 valores identificados, al estar por encima de 1 en una métrica que debe estar entre 0 y 1, no son válidos.

test <- rosnerTest(Base_omit$F1_Score, k = 10)

test$all.stats
##    i    Mean.i      SD.i     Value Obs.Num     R.i+1 lambda.i+1 Outlier
## 1  0 0.8013885 0.8749189 9.3740487     296  9.798234   3.833870    TRUE
## 2  1 0.7822103 0.7759213 9.2953593     316 10.971665   3.833271    TRUE
## 3  2 0.7631225 0.6634602 8.1785788     281 11.176942   3.832670    TRUE
## 4  3 0.7464585 0.5630661 7.7476843     437 12.434110   3.832068    TRUE
## 5  4 0.7306900 0.4548205 5.4997417     230 10.485569   3.831464    TRUE
## 6  5 0.7199247 0.3946604 5.3206680     333 11.657473   3.830859    TRUE
## 7  6 0.7095157 0.3286397 5.1312436     160 13.454635   3.830252    TRUE
## 8  7 0.6994892 0.2524146 4.6320729     267 15.579855   3.829643    TRUE
## 9  8 0.6905515 0.1689673 0.9993356     418  1.827479   3.829033   FALSE
## 10 9 0.6898481 0.1685139 0.9985770     422  1.832068   3.828422   FALSE

Basándonos en la prueba de Rosner, identificamos 8 valores atípicos, lo cual difiere de los 6 valores atípicos previamente observados en el boxplot. Esta diferencia puede deberse a la mayor sensibilidad de la prueba de Rosner para detectar outliers. Los 8 valores identificados, al estar por encima de 1 en una métrica que debe estar entre 0 y 1, no son válidos.

test <- rosnerTest(Base_omit$Training_Time, k = 10)

test$all.stats
##    i   Mean.i     SD.i     Value Obs.Num     R.i+1 lambda.i+1 Outlier
## 1  0 3.059781 4.646419 46.985626     324  9.453699   3.833870    TRUE
## 2  1 2.961513 4.159537 46.838741     217 10.548585   3.833271    TRUE
## 3  2 2.863133 3.606191 44.586446     100 11.569913   3.832670    TRUE
## 4  3 2.769373 3.017332 44.357901     201 13.783214   3.832068    TRUE
## 5  4 2.675705 2.282925 28.294985     344 11.222129   3.831464    TRUE
## 6  5 2.617874 1.932676 20.933435     109  9.476788   3.830859    TRUE
## 7  6 2.576436 1.726646 20.251860     417 10.236856   3.830252    TRUE
## 8  7 2.536356 1.508782 13.791378     214  7.459672   3.829643    TRUE
## 9  8 2.510776 1.411524  4.997833     156  1.761966   3.829033   FALSE
## 10 9 2.505111 1.408117  4.986466      43  1.762179   3.828422   FALSE

Basándonos en la prueba de Rosner, identificamos 8 valores atípicos, lo cual difiere de los 6 valores atípicos previamente observados en el boxplot. Esta diferencia puede deberse a la mayor sensibilidad de la prueba de Rosner para detectar outliers. Los 8 valores identificados, al estar por encima de 2.5 (Mediana) en una métrica que esta alrededor de 2-4, los hace menos válidos.

Con las pruebas ya listas, vamos a imputar los datos con la técnica imputación con la Mediana, ya que los datos no siguen una distribución normal.

# Calcular la mediana de los valores que están en el rango permitido (0 a 1)
mediana_accuracy <- median(Base_omit$Accuracy[Base_omit$Accuracy <= 1], na.rm = TRUE)

# Reemplazar los valores atípicos (mayores a 1) por la mediana
Base_omit$Accuracy[Base_omit$Accuracy > 1] <- mediana_accuracy



boxp1 <- ggplot(data.frame(value = Base_omit$Accuracy), aes(x = "", y = value)) +
  geom_boxplot(fill = "#99CCFF", color = "black", outlier.colour = "red", outlier.size = 3, width = 0.4) + 
  ggtitle("Boxplot de Accuracy") +
  ylab("Accuracy") + 
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 14))


mediana_2 <- median(Base_omit$Precision[Base_omit$Precision <= 1], na.rm = TRUE)

# Reemplazar los valores atípicos (mayores a 1) por la mediana
Base_omit$Precision[Base_omit$Precision > 1] <- mediana_2



boxp2 <- ggplot(data.frame(value = Base_omit$Precision), aes(x = "", y = value)) +
  geom_boxplot(fill = "#99CCFF", color = "black", outlier.colour = "red", outlier.size = 3, width = 0.4) + 
  ggtitle("Boxplot de Precision") +
  ylab("Precision") + 
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 14))



# Calcular la mediana de los valores que están en el rango permitido (0 a 1)
mediana_3 <- median(Base_omit$Recall[Base_omit$Recall <= 1], na.rm = TRUE)

# Reemplazar los valores atípicos (mayores a 1) por la mediana
Base_omit$Recall[Base_omit$Recall > 1] <- mediana_3



boxp3 <- ggplot(data.frame(value = Base_omit$Recall), aes(x = "", y = value)) +
  geom_boxplot(fill = "#99CCFF", color = "black", outlier.colour = "red", outlier.size = 3, width = 0.4) + 
  ggtitle("Boxplot de Recall") +
  ylab("Recall") + 
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 14))


# Calcular la mediana de los valores que están en el rango permitido (0 a 1)
mediana_4 <- median(Base_omit$F1_Score[Base_omit$F1_Score <= 1], na.rm = TRUE)

# Reemplazar los valores atípicos (mayores a 1) por la mediana
Base_omit$F1_Score[Base_omit$F1_Score > 1] <- mediana_4



boxp4 <- ggplot(data.frame(value = Base_omit$F1_Score), aes(x = "", y = value)) +
  geom_boxplot(fill = "#99CCFF", color = "black", outlier.colour = "red", outlier.size = 3, width = 0.4) + 
  ggtitle("Boxplot de F1 Score") +
  ylab("F1 Score") + 
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 14))

Base_omit$Training_Time[Base_omit$Training_Time > 10] <- median(Base_omit$Training_Time, na.rm = TRUE)



boxp5 <- ggplot(data.frame(value = Base_omit$Precision), aes(x = "", y = value)) +
  geom_boxplot(fill = "#99CCFF", color = "black", outlier.colour = "red", outlier.size = 3, width = 0.4) + 
  ggtitle("Boxplot de Training Time") +
  ylab("Training Time") + 
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 14))

Veamos los boxplot ahora:

(boxp1 | boxp2 | boxp3) / (boxp4 | boxp5)

grafi1 <- ggplot(data.frame(value = Base_omit$Accuracy), aes(x = value)) +
  geom_histogram(aes(y = ..density..), bins = 30, fill = "#99CCFF", color = "black", alpha = 0.7) +
  stat_function(fun = dnorm, args = list(mean = mean(Base_omit$Accuracy), sd = sd(Base_omit$Accuracy)), color = "red", size = 1) +
  ggtitle("Histograma Acurracy") +
  xlab("Acurracy") + ylab("Densidad") +
  theme_minimal() +
  theme(plot.title = element_text(size = 14, hjust = 0.5))


# Repetimos para Precision, Recall, F1_Score y Training_Time (mismo formato)
grafi2 <- ggplot(data.frame(value = Base_omit$Precision), aes(x = value)) +
  geom_histogram(aes(y = ..density..), bins = 30, fill = "#99CCFF", color = "black", alpha = 0.7) +
  stat_function(fun = dnorm, args = list(mean = mean(Base_omit$Precision), sd = sd(Base_omit$Precision)), color = "red", size = 1) +
  ggtitle("Histograma Precision") +
  xlab("Precision") + ylab("Densidad") +
  theme_minimal() +
  theme(plot.title = element_text(size = 14, hjust = 0.5))


grafi3 <- ggplot(data.frame(value = Base_omit$Recall), aes(x = value)) +
  geom_histogram(aes(y = ..density..), bins = 30, fill = "#99CCFF", color = "black", alpha = 0.7) +
  stat_function(fun = dnorm, args = list(mean = mean(Base_omit$Recall), sd = sd(Base_omit$Recall)), color = "red", size = 1) +
  ggtitle("Histograma Recall") +
  xlab("Recall") + ylab("Densidad") +
  theme_minimal() +
  theme(plot.title = element_text(size = 14, hjust = 0.5))


grafi4 <- ggplot(data.frame(value = Base_omit$F1_Score), aes(x = value)) +
  geom_histogram(aes(y = ..density..), bins = 30, fill = "#99CCFF", color = "black", alpha = 0.7) +
  stat_function(fun = dnorm, args = list(mean = mean(Base_omit$F1_Score), sd = sd(Base_omit$F1_Score)), color = "red", size = 1) +
  ggtitle("Histograma F1 Score") +
  xlab("F1 Score") + ylab("Densidad") +
  theme_minimal() +
  theme(plot.title = element_text(size = 14, hjust = 0.5))


grafi5 <- ggplot(data.frame(value = Base_omit$Training_Time), aes(x = value)) +
  geom_histogram(aes(y = ..density..), bins = 30, fill = "#99CCFF", color = "black", alpha = 0.7) +
  stat_function(fun = dnorm, args = list(mean = mean(Base_omit$Training_Time), sd = sd(Base_omit$Training_Time)), color = "red", size = 1) +
  ggtitle("Histograma Training Time") +
  xlab("Training Time") + ylab("Densidad") +
  theme_minimal() +
  theme(plot.title = element_text(size = 14, hjust = 0.5))

Observemos los histogramas ahora:

(grafi1 | grafi2 | grafi3) / (grafi4 | grafi5)

Conclusión

Tras la imputación de los valores atípicos detectados, los boxplots confirman que ya no hay outliers en la base de datos. Esto nos da más certeza y confianza en la calidad de los datos, lo cual es crucial para la precisión de los análisis y resúmenes estadísticos.

Analisis de Numérico y Catégorico

Procederemos a realizar un análisis detallado de las variables numéricas y categóricas mediante la aplicación de filtros de información. Dado que ya se ha calculado el resumen estadístico de las variables, el uso de estos filtros nos permitirá profundizar en el comportamiento individual de cada variable, proporcionando una comprensión más precisa y contextualizada de los datos analizados.

Filtros de información

1: Distribución Uniforme de Precisión en Modelos con Valores Mayores a 0.7

# Filtrar las filas donde Precision es mayor a 0.7
Base_filtrada <- Base_omit %>% filter(Precision > 0.7)

# Crear un gráfico de densidad para visualizar la distribución de Precision filtrada
ggplot(Base_filtrada, aes(x = Precision)) +
  geom_density(fill = "#FF6666", alpha = 0.7, color = "black") +
  ggtitle("Distribucion de Precision (Mayor a 0.7)") +
  xlab("Precision") +
  ylab("Densidad") +
  theme_minimal()

El gráfico tiene variaciones suaves en la densidad, no muestra grandes picos o caídas bruscas. Esto sugiere que los valores de precisión mayores a 0.7 están distribuidos de manera bastante uniforme. Se puede decir que es común encontrar modelos con precisiones cercanas a 1, lo que indica un buen desempeño de los modelos de machine learning.

2: Algoritmo con mayor eficacia en su tiempo de entrenamiento.

##        Algorithm Mean_Time Entries
## 1            SVM  2.293516     105
## 2  Random Forest  2.427681      95
## 3        K-Means  2.606262     135
## 4 Neural Network  2.666324     113

El análisis revela que el algoritmo SVM presenta el tiempo de entrenamiento promedio más bajo con 2.29 horas, seguido de Random Forest con 2.43 horas, como se observa en el gráfico de barras que muestra la media del tiempo de entrenamiento para cada algoritmo. Otros algoritmos, como K-Means y Neural Network, tienen tiempos de entrenamiento de 2.61 horas y 2.67 horas, respectivamente. Estos resultados sugieren que SVM y Random Forest son altamente eficientes, permitiendo una rápida implementación en escenarios donde el tiempo es un recurso crítico. La identificación de estos algoritmos como los más eficientes sugiere que pueden ser preferidos para proyectos que requieren una respuesta rápida, especialmente cuando se trabaja con grandes conjuntos de datos.

3: Framework con mayor eficacia en su tiempo de entrenamiento.

##      Framework Mean_Time Entries
## 1 Scikit-learn  2.410524      98
## 2      PyTorch  2.422791     111
## 3   TensorFlow  2.541135     142
## 4        Keras  2.665840      97

Los resultados obtenidos muestran que Keras y TensorFlow tienen los tiempos de entrenamiento promedio más altos, con 2.67 horas y 2.54 horas, respectivamente. Estos frameworks cuentan con 97 y 142 entradas, lo que proporciona una base sólida para estos promedios. En contraste, Scikit-learn y PyTorch presentan tiempos de entrenamiento promedio más bajos, con 2.41 horas y 2.42 horas, respectivamente. Esta diferencia en los tiempos de entrenamiento sugiere que Scikit-learn y PyTorch podrían ofrecer una mayor eficiencia en el tiempo de entrenamiento, lo que permitiría a los desarrolladores trabajar de forma más rápida. Sin embargo, la elección del framework dependerá también de otros factores como la precisión y la facilidad de uso en cada caso.

4: Framework con presición mayor a 0.6.

suppressPackageStartupMessages({
  library(dplyr)
  library(ggplot2)
  library(tidyr)
})

precision_intervals <- c(0.6, 0.7, 0.8, 0.9, 1.0)
precision_labels <- c("0.6-0.7", "0.7-0.8", "0.8-0.9", "0.9-1.0")

Base_filtered <- Base_omit %>% filter(Precision > 0.6)

precision_counts <- Base_filtered %>%
  mutate(Precision_Range = cut(Precision, breaks = precision_intervals, right = FALSE, labels = precision_labels)) %>%
  group_by(Framework, Precision_Range) %>%
  summarise(Count = n(), .groups = 'drop') %>%
  arrange(Framework, desc(Count))

if (nrow(precision_counts) > 0) {
  ggplot(precision_counts, aes(x = Precision_Range, y = Count, fill = Framework)) +
    geom_bar(stat = "identity", position = "dodge") +
    labs(title = "Cantidad de Modelos por Intervalos de Precision por Framework",
         x = "Intervalo de Precision",
         y = "Cantidad de Modelos") +
    theme_minimal() +
    scale_fill_brewer(palette = "Set1") +
    scale_y_continuous(trans = "reverse") # Invertir el eje y
} else {
  cat("No hay modelos con precisión superior a 0.6.")
}

El gráfico muestra la distribución de la cantidad de modelos por intervalos de precisión (de 0.6 a 1.0) clasificados según los frameworks. En el intervalo de 0.6-0.7, TensorFlow y Keras dominan con apróximadamente 17-21 modelos cada uno, mientras que Scikit-learn y PyTorch tienen una menor representación. En el rango de 0.7-0.8, TensorFlow continúa liderando, aunque Keras, PyTorch, muestran una distribución más equilibrada con alrededor de 20 modelos cada uno. En el intervalo de 0.8-0.9, TensorFlow sigue siendo el dominante, con Scikit-learn y Keras manteniendo una representación intermedia. Finalmente, en el rango de 0.9-1.0, TensorFlow es el framework con la mayor cantidad de modelos, mientras que los demás tienen menos de 20.

En conclusión, TensorFlow es el framework con más modelos en los intervalos de precisión inferiores e intermedios. Keras muestra una fuerte presencia en los intervalos intermedios, mientras que PyTorch tiene una menor representación en general. Esto sugiere que la elección del framework puede influir tanto en la cantidad de modelos generados como en su capacidad para alcanzar altos niveles de precisión.

5: Algoritmo con mayor eficacia en su tiempo de entrenamiento y presición.

suppressPackageStartupMessages({
  library(dplyr)
  library(ggplot2)
})

Base_filtered <- Base_omit %>%
  filter(Precision > 0.6 & Training_Time > 0)

Base_efficiency <- Base_omit %>%
  mutate(Efficacy = Precision / Training_Time) %>%
  group_by(Algorithm, Framework) %>%
  summarise(Average_Efficacy = mean(Efficacy, na.rm = TRUE), .groups = 'drop') %>%
  arrange(desc(Average_Efficacy))

if (nrow(Base_efficiency) > 0) {
  ggplot(Base_efficiency, aes(x = reorder(paste(Algorithm, Framework), -Average_Efficacy), y = Average_Efficacy, fill = Algorithm)) +
    geom_bar(stat = "identity") +
    labs(title = "Eficacia Media por Algoritmo y Framework",
         x = "Combinacion de Algoritmo y Framework",
         y = "Eficacia Media") +
    theme_minimal() +
    theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
    scale_fill_brewer(palette = "Set1")
} else {
  cat("No hay modelos con precision superior a 0.6 y tiempo de entrenamiento positivo.")
}

El análisis de la eficacia media por combinación de algoritmo y framework revela diferencias significativas en el rendimiento de los modelos. SVM con TensorFlow alcanza la eficacia media más alta, superando 0.75, lo que lo posiciona como la combinación más eficaz en el gráfico. K-Means con PyTorch y SVM con Scikit-learn también muestran un rendimiento elevado, con eficacias medias cercanas a 0.65. En contraste, combinaciones como Random Forest con Keras y SVM con PyTorch muestran eficacias medias más bajas, por debajo de 0.5, lo que sugiere un menor rendimiento. Esto resalta la importancia de la elección adecuada de la combinación de algoritmo y framework para maximizar la eficacia en aplicaciones específicas.

6: Eficacia en su tiempo de entrenamiento y precisión.

suppressPackageStartupMessages({
  library(dplyr)
  library(ggplot2)
})

Base_filtered <- Base_omit %>%
  filter(Precision > 0.6 & Training_Time > 0)

Base_efficiency <- Base_filtered %>%
  mutate(Efficacy = Precision / Training_Time) %>%
  group_by(Framework) %>%
  summarise(Average_Efficacy = mean(Efficacy, na.rm = TRUE), .groups = 'drop') %>%
  arrange(desc(Average_Efficacy))

if (nrow(Base_efficiency) > 0) {
  ggplot(Base_efficiency, aes(x = reorder(Framework, -Average_Efficacy), y = Average_Efficacy, fill = Framework)) +
    geom_bar(stat = "identity") +
    labs(title = "Eficacia Media por Framework",
         x = "Framework",
         y = "Eficacia Media") +
    theme_minimal() +
    scale_fill_brewer(palette = "Set1")
} else {
  cat("No hay modelos con precisión superior a 0.6 y tiempo de entrenamiento positivo.")
}

El análisis de la eficacia media por framework revela diferencias significativas en el rendimiento de los modelos. En primer lugar, TensorFlow alcanza una eficacia media con más del 0.7, lo que lo posiciona como el framework más eficaz entre los evaluados. Scikit-learn se aproxima con una eficacia media ligeramente inferior, lo que resalta su competencia en términos de rendimiento. En contraste, PyTorch muestra una eficacia media moderada, situada entre 0.55, lo que indica que, aunque es menos eficaz que TensorFlow y Scikit-learn, sigue siendo una opción válida en muchos contextos. Por otro lado, Keras tiene la eficacia media más baja, sugiriendo que este framework es el menos eficaz en comparación con los otros, lo que podría limitar su uso en aplicaciones que requieren altos niveles de rendimiento Sin embargo, entre Keras y Pytorch no hay tanta diferencia. El framework más eficaz fue TensorFlow.

7: Mejor combinacion de algoritmo y framework con mayor precision en menor tiempo

suppressPackageStartupMessages({
  library(dplyr)
  library(ggplot2)
})

Base_filtered <- Base_omit %>%
  filter(Precision > 0.6 & Training_Time > 0)

Base_best_combination <- Base_filtered %>%
  mutate(Score = Precision / Training_Time) %>%
  group_by(Algorithm, Framework) %>%
  summarise(Average_Precision = mean(Precision, na.rm = TRUE),
            Average_Training_Time = mean(Training_Time, na.rm = TRUE),
            Score = mean(Score, na.rm = TRUE), .groups = 'drop') %>%
  arrange(desc(Score))

if (nrow(Base_best_combination) > 0) {
  ggplot(Base_best_combination, aes(x = reorder(paste(Algorithm, Framework), -Score), y = Score, fill = Algorithm)) +
    geom_bar(stat = "identity") +
    labs(title = "Mejor Combinacion de Algoritmo y Framework",
         x = "Combinacion de Algoritmo y Framework",
         y = "Score (Precision / Tiempo de Entrenamiento)") +
    theme_minimal() +
    theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
    scale_fill_brewer(palette = "Set1")
} else {
  cat("No hay modelos con precision superior a 0.6 y tiempo de entrenamiento positivo.")
}

Finalmente, al evaluar la mejor combinación de algoritmo y framework, podemos notar que la combinación de SVM con TensorFlow también resulta ser la mejor opción en cuanto a mayor precisión en menor tiempo de entrenamiento. Con un puntaje máximo en el gráfico de aproximadamente 0.97, esta combinación logra equilibrar de manera sobresaliente ambos factores. Como motivos, sabemos que SVM es conocido por ser muy eficaz en problemas de clasificación y detección de patrones complejos, y TensorFlow maximiza su rendimiento al optimizar el uso de recursos computacionales y reducir los tiempos de procesamiento. El framework facilita el entrenamiento de modelos de gran escala sin comprometer la precisión.

Analisis Bivariado

Análisis Bivariado con “Algorithm”, “Framework”, “Precision”, y “Training_Time” El objetivo de este análisis es explorar las relaciones entre diferentes características relacionadas con la implementación de modelos de inteligencia artificial (IA), como el tipo de algoritmo, el framework utilizado, la precisión alcanzada por el modelo y el tiempo de entrenamiento necesario. A continuación, realizaremos un análisis detallado de las variables categóricas y numéricas para identificar patrones relevantes que podrían influir en la eficacia y eficiencia de los modelos.

Variables involucradas:

  1. Algorithm (Categórica): Tipo de algoritmo de IA utilizado, con categorías Neural Network, Random Forest, SVM, y K-Means.

  2. Framework (Categórica): Framework o biblioteca utilizada para la implementación del modelo de IA, con categorías TensorFlow, PyTorch, Keras, y Scikit-learn.

  3. Precision (Numérica, Continua): Precisión del modelo, con un valor entre 0 y 1 que mide qué tan preciso es el modelo en términos de clasificación.

  4. Training_Time (Numérica, Continua): Tiempo de entrenamiento del modelo, medido en horas.

Paso 1: Análisis de “Algorithm” y “Precision”

Comenzaremos evaluando la relación entre el tipo de algoritmo utilizado y la precisión alcanzada por el modelo, lo que nos permitirá entender si ciertos algoritmos tienden a generar modelos más precisos que otros. Para ello, primero calcularemos estadísticas descriptivas (media, mediana, desviación estándar) de la variable Precisión para cada algoritmo. Esto nos ayudará a observar si algunos algoritmos, como las Neural Network, ofrecen en promedio una mayor precisión comparado con otros, como K-Means. Posteriormente, utilizaremos un boxplot para visualizar la distribución de la precisión por cada algoritmo, lo que facilitará la comparación visual de la dispersión y las posibles diferencias entre ellos. El objetivo final es comparar la precisión entre los diferentes algoritmos y determinar si algunos tienen una ventaja significativa en términos de exactitud.

# Resumen estadístico de Precision por Algorithm
summary_algorithm_precision <- Base_omit %>%
  group_by(Algorithm) %>%
  summarise(
    mean_precision = mean(Precision, na.rm = TRUE),
    median_precision = median(Precision, na.rm = TRUE),
    sd_precision = sd(Precision, na.rm = TRUE)
  )
summary_algorithm_precision
## # A tibble: 4 × 4
##   Algorithm      mean_precision median_precision sd_precision
##   <chr>                   <dbl>            <dbl>        <dbl>
## 1 K-Means                 0.721            0.750        0.175
## 2 Neural Network          0.693            0.718        0.159
## 3 Random Forest           0.698            0.704        0.171
## 4 SVM                     0.724            0.724        0.171
# Prueba ANOVA para comparar Precision por Algorithm
anova_algorithm_precision <- aov(Precision ~ Algorithm, data = Base_omit)

# Mostrar el resultado de la ANOVA
summary_anova_algorithm_precision <- summary(anova_algorithm_precision)
summary_anova_algorithm_precision
##              Df Sum Sq Mean Sq F value Pr(>F)
## Algorithm     3  0.082 0.02725   0.948  0.417
## Residuals   444 12.766 0.02875
# Interpretación:
# Si el valor de p en el resultado (Pr(>F)) es menor a 0.05, indica que hay diferencias significativas
# en las medias de precisión entre al menos dos algoritmos.
if (summary_anova_algorithm_precision[[1]]["Algorithm", "Pr(>F)"] < 0.05) {
  cat("El resultado de la ANOVA muestra diferencias significativas en la precisión entre los diferentes algoritmos.\n")
  
  # Prueba post-hoc Tukey HSD para determinar entre qué algoritmos existen diferencias
  tukey_algorithm_precision <- TukeyHSD(anova_algorithm_precision)
  tukey_algorithm_precision
  cat("El test Tukey HSD identifica cuáles algoritmos tienen diferencias significativas en su precisión.\n")
} else {
  cat("No se encontraron diferencias significativas en la precisión entre los algoritmos.\n")
}
## No se encontraron diferencias significativas en la precisión entre los algoritmos.
# Boxplot de Precision por Algorithm
ggplot(Base_omit, aes(x = Algorithm, y = Precision)) +
  geom_boxplot() +
  labs(title = "Distribucion de Precision por Algorithm", 
       x = "Tipo de Algoritmo", 
       y = "Precision del Modelo") +
  theme_minimal()

El gráfico de Distribución de Precisión por Algoritmo revela la variabilidad en el rendimiento de los modelos basados en diferentes algoritmos. K-Means muestra una amplia distribución de precisiones, con una mediana cercana a 0.75, lo que indica que este algoritmo tiende a ser eficaz en la mayoría de los casos, aunque la dispersión sugiere que algunos modelos tienen un rendimiento menor. Neural Networks tiene una mediana de precisión más baja que K-Means, pero presenta variabilidad, lo que implica que, dependiendo de la configuración del modelo, las redes neuronales pueden tener tanto modelos altamente precisos como menos precisos. Random Forest presenta una mediana similar a la de K-Means, pero con menos dispersión, lo que sugiere que este algoritmo ofrece resultados más consistentes. Finalmente, SVM tiene una alta variabilidad en sus resultados, con una mediana de precisión alta (alrededor de 0.71), lo que indica que, en escenarios bien configurados, puede lograr modelos altamente precisos.

Paso 2: Análisis de “Algorithm” y “Training_Time”

Después, analizaremos el tiempo de entrenamiento (Training_Time) necesario para cada tipo de algoritmo, lo cual nos permitirá comprender si los modelos más complejos, como las redes neuronales, requieren más tiempo de entrenamiento que modelos más simples, como K-Means. Para ello, calcularemos estadísticas descriptivas como la media, mediana y desviación estándar del tiempo de entrenamiento por algoritmo, lo que nos permitirá identificar si algunos algoritmos consistentemente requieren más tiempo de cómputo. Posteriormente, visualizaremos esta información mediante un boxplot que mostrará la variabilidad y las diferencias en los tiempos de entrenamiento entre los distintos algoritmos. El objetivo es determinar si el tiempo de entrenamiento varía significativamente entre algoritmos y cómo esta diferencia se relaciona con la complejidad del modelo.

# Resumen estadístico de Training_Time por Algorithm
summary_algorithm_time <- Base_omit %>%
  group_by(Algorithm) %>%
  summarise(
    mean_time = mean(Training_Time, na.rm = TRUE),
    median_time = median(Training_Time, na.rm = TRUE),
    sd_time = sd(Training_Time, na.rm = TRUE)
  )
summary_algorithm_time
## # A tibble: 4 × 4
##   Algorithm      mean_time median_time sd_time
##   <chr>              <dbl>       <dbl>   <dbl>
## 1 K-Means             2.61        2.56    1.43
## 2 Neural Network      2.67        2.51    1.40
## 3 Random Forest       2.43        2.25    1.36
## 4 SVM                 2.29        2.12    1.38
# Prueba ANOVA para comparar Training_Time por Algorithm
anova_algorithm_time <- aov(Training_Time ~ Algorithm, data = Base_omit)

# Mostrar el resultado de la ANOVA
summary_anova_algorithm_time <- summary(anova_algorithm_time)
summary_anova_algorithm_time
##              Df Sum Sq Mean Sq F value Pr(>F)
## Algorithm     3    9.6   3.192   1.638   0.18
## Residuals   444  865.1   1.948
# Interpretación:
# Si el valor de p es menor a 0.05, hay diferencias significativas en el tiempo de entrenamiento
# entre los algoritmos.
if (summary_anova_algorithm_time[[1]]["Algorithm", "Pr(>F)"] < 0.05) {
  cat("El resultado de la ANOVA muestra diferencias significativas en el tiempo de entrenamiento entre los diferentes algoritmos.\n")
  
  # Prueba post-hoc Tukey HSD para determinar entre qué algoritmos existen diferencias
  tukey_algorithm_time <- TukeyHSD(anova_algorithm_time)
  tukey_algorithm_time
  cat("El test Tukey HSD identifica cuáles algoritmos tienen diferencias significativas en el tiempo de entrenamiento.\n")
} else {
  cat("No se encontraron diferencias significativas en el tiempo de entrenamiento entre los algoritmos.\n")
}
## No se encontraron diferencias significativas en el tiempo de entrenamiento entre los algoritmos.
# Boxplot de Training_Time por Algorithm
ggplot(Base_omit, aes(x = Algorithm, y = Training_Time)) +
  geom_boxplot() +
  labs(title = "Distribucion de Training_Time por Algorithm", 
       x = "Tipo de Algoritmo", 
       y = "Tiempo de Entrenamiento (Horas)") +
  theme_minimal()

El análisis de la Distribución de Training Time por Algoritmo revela que K-Means tiene tiempos de entrenamiento relativamente cortos y estables, con una mediana cercana a las 2.5 horas y una dispersión moderada, sin outliers, lo que lo convierte en una opción eficiente. Por otro lado, las Neural Networks presentan una mayor variabilidad en los tiempos, que oscilan entre 1.5 horas y 4.1 horas, lo que indica que el tiempo de entrenamiento puede variar considerablemente según las configuraciones. Random Forest tiene una variabilidad moderada, con tiempos que van de 1.4 hora a 3.7 horas, siendo más estable que Neural Networks pero con una mediana similar, cerca de las 2.25 horas. Finalmente, SVM muestra una variabilidad más controlada, con una mediana en 2.12 horas, en general es más predecible en términos de tiempo. En resumen, K-Means es el más rápido y estable, mientras que Neural Network y Random Forest pueden tener tiempos de entrenamiento prolongados en ciertos casos, y SVM se encuentra en un punto intermedio, con tiempos generalmente más predecibles pero con valores altos.

Paso 3: Análisis de “Framework” y “Precision”

El siguiente análisis evaluará la relación entre el framework utilizado (Framework) y la precisión del modelo (Precision), ya que diferentes bibliotecas tienen optimizaciones y características que podrían influir en el rendimiento del modelo. Para esto, calcularemos estadísticas descriptivas como la media, mediana y desviación estándar de la precisión alcanzada por los modelos en cada framework. Además, utilizaremos un boxplot que mostrará la distribución de la precisión para cada framework, permitiendo observar visualmente cómo varía la precisión según la herramienta utilizada. El objetivo de este análisis es comparar el rendimiento de los frameworks en términos de precisión y determinar si alguno de ellos ofrece una ventaja significativa en ciertos contextos.

# Resumen estadístico de Precision por Framework
summary_framework_precision <- Base_omit %>%
  group_by(Framework) %>%
  summarise(
    mean_precision = mean(Precision, na.rm = TRUE),
    median_precision = median(Precision, na.rm = TRUE),
    sd_precision = sd(Precision, na.rm = TRUE)
  )
summary_framework_precision
## # A tibble: 4 × 4
##   Framework    mean_precision median_precision sd_precision
##   <chr>                 <dbl>            <dbl>        <dbl>
## 1 Keras                 0.693            0.704        0.163
## 2 PyTorch               0.701            0.724        0.173
## 3 Scikit-learn          0.735            0.746        0.171
## 4 TensorFlow            0.709            0.724        0.170
# Prueba ANOVA para comparar Precision por Framework
anova_framework_precision <- aov(Precision ~ Framework, data = Base_omit)

# Mostrar el resultado de la ANOVA
summary_anova_framework_precision <- summary(anova_framework_precision)
summary_anova_framework_precision
##              Df Sum Sq Mean Sq F value Pr(>F)
## Framework     3  0.098 0.03276   1.141  0.332
## Residuals   444 12.749 0.02871
# Interpretación:
# Si el valor de p es menor a 0.05, hay diferencias significativas en la precisión entre los frameworks.
if (summary_anova_framework_precision[[1]]["Framework", "Pr(>F)"] < 0.05) {
  cat("El resultado de la ANOVA muestra diferencias significativas en la precisión entre los diferentes frameworks.\n")
  
  # Prueba post-hoc Tukey HSD para determinar entre qué frameworks existen diferencias
  tukey_framework_precision <- TukeyHSD(anova_framework_precision)
  tukey_framework_precision
  cat("El test Tukey HSD identifica qué frameworks tienen diferencias significativas en la precisión.\n")
} else {
  cat("No se encontraron diferencias significativas en la precisión entre los frameworks.\n")
}
## No se encontraron diferencias significativas en la precisión entre los frameworks.
# Boxplot de Precision por Framework
ggplot(Base_omit, aes(x = Framework, y = Precision)) +
  geom_boxplot() +
  labs(title = "Distribucion de Precision por Framework", 
       x = "Framework Utilizado", 
       y = "Precision del Modelo") +
  theme_minimal()

El análisis de la Distribución de Precisión por Framework revela que los frameworks Keras, PyTorch, Scikit-learn y TensorFlow muestran medianas de precisión cercanas a 0.8, lo que sugiere que todos los frameworks ofrecen un rendimiento similar en cuanto a precisión. Sin embargo, se observa mayor variabilidad en Keras y PyTorch, ya que tienen una mayor dispersión en sus distribuciones, lo que sugiere que algunos modelos entrenados con estos frameworks pueden tener rendimientos menos consistentes. En cambio, Scikit-learn y TensorFlow presentan distribuciones más compactas, lo que indica que tienden a ofrecer una precisión más estable y predecible. Esto podría hacer que estos dos últimos frameworks sean opciones preferidas para proyectos donde la estabilidad en la precisión es crucial. No obstante, todos presentan un rendimiento casi igual.

Paso 4: Análisis de “Framework” y “Training_Time”

Finalmente, analizaremos la relación entre el framework utilizado y el tiempo de entrenamiento. Algunas bibliotecas, como TensorFlow o PyTorch, pueden estar más optimizadas para ciertos tipos de hardware o modelos, lo que afecta el tiempo necesario para entrenar un modelo. Para ello, calcularemos la media, mediana y desviación estándar del tiempo de entrenamiento para cada framework, lo que permitirá identificar cuáles bibliotecas ofrecen tiempos de entrenamiento más cortos o más largos. Luego, un boxplot del tiempo de entrenamiento por framework permitirá visualizar cómo varían los tiempos entre las diferentes herramientas y si algunos frameworks son más eficientes en términos de cómputo. El objetivo es evaluar la eficiencia de cada framework en términos de tiempo de entrenamiento y determinar si ciertas herramientas ofrecen ventajas significativas en cuanto a velocidad.

# Resumen estadístico de Training_Time por Framework
summary_framework_time <- Base_omit %>%
  group_by(Framework) %>%
  summarise(
    mean_time = mean(Training_Time, na.rm = TRUE),
    median_time = median(Training_Time, na.rm = TRUE),
    sd_time = sd(Training_Time, na.rm = TRUE)
  )
summary_framework_time
## # A tibble: 4 × 4
##   Framework    mean_time median_time sd_time
##   <chr>            <dbl>       <dbl>   <dbl>
## 1 Keras             2.67        2.80    1.45
## 2 PyTorch           2.42        2.48    1.33
## 3 Scikit-learn      2.41        2.26    1.34
## 4 TensorFlow        2.54        2.36    1.46
# Prueba ANOVA para comparar Training_Time por Framework
anova_framework_time <- aov(Training_Time ~ Framework, data = Base_omit)

# Mostrar el resultado de la ANOVA
summary_anova_framework_time <- summary(anova_framework_time)
summary_anova_framework_time
##              Df Sum Sq Mean Sq F value Pr(>F)
## Framework     3    4.3   1.436   0.732  0.533
## Residuals   444  870.4   1.960
# Interpretación:
# Si el valor de p en Pr(>F) es menor a 0.05, indica que existen diferencias significativas
# en el tiempo de entrenamiento entre los frameworks.
if (summary_anova_framework_time[[1]]["Framework", "Pr(>F)"] < 0.05) {
  cat("El resultado de la ANOVA muestra diferencias significativas en el tiempo de entrenamiento entre los diferentes frameworks.\n")
  
  # Prueba post-hoc Tukey HSD para determinar entre qué frameworks existen diferencias significativas
  tukey_framework_time <- TukeyHSD(anova_framework_time)
  tukey_framework_time
  cat("El test Tukey HSD identifica cuáles frameworks tienen diferencias significativas en el tiempo de entrenamiento.\n")
} else {
  cat("No se encontraron diferencias significativas en el tiempo de entrenamiento entre los frameworks.\n")
}
## No se encontraron diferencias significativas en el tiempo de entrenamiento entre los frameworks.
# Boxplot de Training_Time por Framework
ggplot(Base_omit, aes(x = Framework, y = Training_Time)) +
  geom_boxplot() +
  labs(title = "Distribucion de Training_Time por Framework", 
       x = "Framework Utilizado", 
       y = "Tiempo de Entrenamiento (Horas)") +
  theme_minimal()

El análisis de la distribución del tiempo de entrenamiento (Training_Time) por framework muestra que Keras, PyTorch, Scikit-learn y TensorFlow presentan medianas de tiempo de entrenamiento similares, alrededor de 2 a 2.8 horas, lo que indica una eficiencia comparable. Sin embargo, PyTorch y TensorFlow muestran una mayor variabilidad, como se observa en la longitud de sus cajas, lo que sugiere que algunos modelos requieren tiempos de entrenamiento más prolongados en comparación con Keras y Scikit-learn, que presentan distribuciones más compactas y consistentes. Esta menor variabilidad convierte a Keras y Scikit-learn en opciones más predecibles en cuanto al tiempo de entrenamiento. En general, Keras es la que presenta una mayor mediana, lo cual es indicio de que puede dar mejores resultados, sin embargo, tiene un variabilidad lo que puede llevar a mejores o peores resultados.

Paso 5: Análisis Combinado de “Algorithm”, “Framework” y “Precision”

Una vez completados los análisis anteriores, podemos explorar la interacción entre las tres variables: el tipo de algoritmo, el framework y la precisión alcanzada. Un gráfico de barras agrupadas permitiría visualizar cómo varía la precisión en función del algoritmo y el framework utilizados, proporcionando una visión clara de las combinaciones más efectivas.

El objetivo de este análisis es identificar si existe una combinación óptima de algoritmo y framework que maximice la precisión del modelo, lo que podría ser de gran utilidad para guiar futuras decisiones en la elección de herramientas y enfoques.

# Resumen estadístico combinado de Precision por Algorithm y Framework
summary_combined <- Base_omit %>%
  group_by(Algorithm, Framework) %>%
  summarise(
    mean_precision = mean(Precision, na.rm = TRUE),
    median_precision = median(Precision, na.rm = TRUE),
    sd_precision = sd(Precision, na.rm = TRUE)
  )
summary_combined
## # A tibble: 16 × 5
## # Groups:   Algorithm [4]
##    Algorithm      Framework    mean_precision median_precision sd_precision
##    <chr>          <chr>                 <dbl>            <dbl>        <dbl>
##  1 K-Means        Keras                 0.677            0.683        0.163
##  2 K-Means        PyTorch               0.688            0.724        0.185
##  3 K-Means        Scikit-learn          0.817            0.857        0.139
##  4 K-Means        TensorFlow            0.708            0.724        0.181
##  5 Neural Network Keras                 0.718            0.724        0.145
##  6 Neural Network PyTorch               0.675            0.723        0.170
##  7 Neural Network Scikit-learn          0.667            0.684        0.148
##  8 Neural Network TensorFlow            0.702            0.785        0.169
##  9 Random Forest  Keras                 0.707            0.713        0.184
## 10 Random Forest  PyTorch               0.724            0.739        0.171
## 11 Random Forest  Scikit-learn          0.630            0.582        0.165
## 12 Random Forest  TensorFlow            0.722            0.709        0.162
## 13 SVM            Keras                 0.677            0.664        0.177
## 14 SVM            PyTorch               0.717            0.724        0.168
## 15 SVM            Scikit-learn          0.799            0.830        0.160
## 16 SVM            TensorFlow            0.709            0.725        0.170
# Prueba ANOVA para evaluar la interacción entre Algorithm y Framework en Precision
anova_combined <- aov(Precision ~ Algorithm * Framework, data = Base_omit)

# Mostrar el resultado de la ANOVA
summary_anova_combined <- summary(anova_combined)
summary_anova_combined
##                      Df Sum Sq Mean Sq F value  Pr(>F)   
## Algorithm             3  0.082 0.02725   0.980 0.40184   
## Framework             3  0.099 0.03294   1.185 0.31500   
## Algorithm:Framework   9  0.658 0.07307   2.629 0.00573 **
## Residuals           432 12.009 0.02780                   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Interpretación:
# Si el valor de p es menor a 0.05 para los efectos principales (Algorithm o Framework) o la interacción (Algorithm:Framework),
# entonces hay una influencia significativa de estas variables en la precisión del modelo.
if (summary_anova_combined[[1]]["Algorithm:Framework", "Pr(>F)"] < 0.05) {
  cat("La interacción entre el tipo de algoritmo y el framework utilizado tiene un efecto significativo en la precisión del modelo.\n")
} else {
  cat("No se encontraron efectos significativos en la precisión debido a la interacción entre el algoritmo y el framework.\n")
}
## La interacción entre el tipo de algoritmo y el framework utilizado tiene un efecto significativo en la precisión del modelo.
# Prueba post-hoc para el análisis combinado si es significativo
if (summary_anova_combined[[1]]["Algorithm:Framework", "Pr(>F)"] < 0.05) {
  tukey_combined <- TukeyHSD(anova_combined)
  tukey_combined
  cat("El test Tukey HSD muestra qué combinaciones de algoritmo y framework tienen diferencias significativas en precisión.\n")
}
## El test Tukey HSD muestra qué combinaciones de algoritmo y framework tienen diferencias significativas en precisión.
# Gráfico de barras agrupadas para mostrar Precision por Algorithm y Framework
ggplot(Base_omit, aes(x = Algorithm, y = Precision, fill = Framework)) +
  geom_bar(stat = "summary", fun = "mean", position = "dodge") +
  labs(title = "Precision Media por Algorithm y Framework", 
       x = "Tipo de Algoritmo", 
       y = "Precision del Modelo",
       fill = "Framework") +
  theme_minimal()

El gráfico de barras ilustra la precisión media de los modelos en función del tipo de algoritmo utilizado (K-Means, Neural Network, Random Forest y SVM) y el framework (Keras, PyTorch, Scikit-learn, TensorFlow), proporcionando una visión comparativa que integra tres elementos clave: algoritmo, framework y precisión. En primer lugar, al analizar el algoritmo K-Means, se observa que la precisión media es bastante consistente entre los frameworks, destacando ligeramente TensorFlow y Scikit-learn por encima de Keras y PyTorch, lo que sugiere un rendimiento levemente superior en la implementación de este algoritmo. En el caso de las Neural Networks, Keras se posiciona como el mejor framework, mostrando la mayor precisión media en comparación con los demás; PyTorch y TensorFlow también ofrecen resultados competitivos, mientras que Scikit-learn presenta una precisión relativamente baja, indicando que podría no ser la mejor opción. En cuanto al algoritmo Random Forest, la precisión media entre los frameworks es bastante uniforme, sin diferencias significativas, lo que implica que los cuatro frameworks ofrecen un rendimiento similar en este caso, excepto por Scikit-learn que se quede bastante abajo comparandola con los otros. Finalmente, para el algoritmo SVM, Scikit-learn sobresale con la mayor precisión media, superando a Keras, PyTorch, y a TensorFlow, lo que indica que puede estar mejor optimizado para este tipo de algoritmo.

Conclusión

La pregunta inicial planteada fue: ¿Qué algoritmo logra la mayor precisión en relación al tiempo de entrenamiento en problemas de regresión sobre datos de series de tiempo, y cómo varía esta eficiencia entre diferentes frameworks como PyTorch, TensorFlow, Keras y Scikit-learn? A lo largo del análisis bivariado, encontramos varios hallazgos relevantes, pero uno de los más destacados fue el rendimiento del algoritmo K-Means. Este algoritmo no solo presentó el mejor tiempo de entrenamiento, con una mediana de 2.5 horas, sino que también se posicionó como el más preciso y eficaz en comparación con los demás algoritmos evaluados.

Además, al evaluar la precisión de los modelos en función del framework y el algoritmo, K-Means mostró ser adaptable y eficiente con los distintos frameworks, presentando resultados consistentemente altos. En conclusión, K-Means no solo fue el algoritmo que logró una mejor relación entre precisión y tiempo de entrenamiento, sino que también demostró ser eficaz en todos los frameworks considerados, destacándose como una opción robusta para resolver problemas de regresión en series de tiempo.