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.
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", "Fecha"),
Nivel_de_Medición = c("Nominal", "Nominal", "Nominal", "Nominal",
"Continua", "Continua", "Continua", "Continua",
"Continua", "Fecha"),
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", "Último año")
)
# 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)
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 | Fecha | Fecha | Último año |
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
La dimension de la base de datos es:
dim(Base)
## [1] 560 10
Es decir, se compone de 560 filas y 10 columnas.
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)
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")
}
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.
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.
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.
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, 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)
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.
El objetivo de esta sección es entender cómo se distribuyen las combinaciones de diferentes algoritmos y frameworks dentro del dataset. Esta información es crucial para identificar posibles sesgos en el conjunto de datos y para asegurar una representación equilibrada de las combinaciones al analizar el rendimiento.
Primero, creamos una cuadrícula (expand.grid
) que
contiene todas las combinaciones posibles de algoritmos y
frameworks.
Luego, contamos cuántas veces aparece cada combinación en el
conjunto de datos limpio (Base_clean
) y almacenamos estos
recuentos en combination_counts
.
Calculamos el porcentaje de cada combinación para facilitar la interpretación.
Finalmente, visualizamos la distribución con un gráfico de torta
utilizando plot_ly
.
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 gráfico de torta muestra visualmente cómo se distribuyen las combinaciones de algoritmos y frameworks. Esto ayuda a identificar rápidamente cuáles son las combinaciones más y menos frecuentes en el conjunto de datos.
1: Algoritmo con mayor eficacia en su tiempo de entrenamiento.
El objetivo de esta sección es identificar cuál de los algoritmos presenta la mejor relación entre la precisión obtenida y el tiempo de entrenamiento requerido. Este análisis es fundamental, ya que una alta precisión con un tiempo de entrenamiento razonable es ideal en aplicaciones prácticas de inteligencia artificial.
En este análisis, calculamos la media del tiempo de entrenamiento para cada algoritmo en el conjunto de datos limpio. Esto nos permite evaluar cómo se comporta cada algoritmo en términos de eficiencia de entrenamiento.
El uso de un bucle for
permite iterar sobre cada
algoritmo y calcular su tiempo medio, guardando también el conteo de
entradas que contribuyen a esa media.
## 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 dataframe time_means_algorithms
muestra el tiempo
promedio que toma entrenar cada algoritmo.
Neural Network
y
Random Forest
tienen tiempos medios de entrenamiento
relativamente bajos. Esto sugiere que estos algoritmos pueden ser
entrenados de manera eficiente en términos de tiempo, lo cual es un
factor crítico cuando se trabaja con grandes volúmenes de datos o en
entornos con recursos computacionales limitados.