Introducción

La Inteligencia Artificial (IA) ha revolucionado numerosos campos en los últimos años, convirtiéndose en una herramienta clave para resolver problemas complejos y optimizar procesos en una amplia variedad de industrias. En el núcleo de la IA se encuentran los algoritmos que permiten a las máquinas aprender y tomar decisiones basadas en grandes volúmenes de datos. Estos algoritmos, cuando son entrenados adecuadamente, pueden clasificar datos, hacer predicciones y encontrar patrones en situaciones donde las soluciones tradicionales no son suficientes. Sin embargo, la elección del algoritmo adecuado, el framework de desarrollo y el tipo de problema a abordar juegan un papel crucial en la efectividad y eficiencia de los modelos de IA.

Este trabajo tiene como propósito realizar un Análisis Exploratorio de Datos (EDA) sobre un conjunto de datos sintético relacionado con problemas de Inteligencia Artificial (IA). El dataset fue generado con fines educativos y contiene información sobre diversos algoritmos de IA, frameworks, tipos de problemas abordados, y métricas de desempeño como la precisión y el tiempo de entrenamiento. A través de este análisis, se busca obtener una mejor comprensión del comportamiento de los modelos y su desempeño en diferentes contextos.

El análisis se centrará en investigar el tipo de problema que presenta el menor tiempo de entrenamiento sin comprometer la precisión, utilizando específicamente el algoritmo Random Forest y el framework Scikit-learn. Además, se hará un análisis de la distribución de las variables categóricas, se revisarán los valores faltantes y se identificarán outliers que podrían influir en los resultados del análisis. Este enfoque permitirá explorar las diferencias entre estos tipos de problemas y cómo se comportan en términos de tiempo y precisión, aportando valiosa información para la optimización de los modelos en aplicaciones prácticas de IA.

Objetivo del Análisis

El objetivo de este Análisis Exploratorio de Datos (EDA) es responder a la pregunta: ¿Qué tipo de problema logra el menor tiempo de entrenamiento sin comprometer la precisión, utilizando el algoritmo Random Forest y el framework Scikit-learn?. Para lograrlo, se analizarán los patrones de comportamiento de los modelos de inteligencia artificial en función de estas variables clave, evaluando el equilibrio entre el tiempo de entrenamiento y la precisión alcanzada por cada tipo de problema.

Además, se llevará a cabo una revisión exhaustiva de los valores faltantes, se detectarán y tratarán outliers, y se aplicarán técnicas de limpieza de datos, todo con el fin de preparar adecuadamente el conjunto de datos, con el fin de asegurar que la calidad del conjunto de datos sea adecuada para futuros análisis.

Carga y Estructura del Dataset

Inicialmente, los datos estaban almacenados en un archivo Excel (.xlsx), por lo que se intentó leer utilizando la librería readxl, la cual permite manejar archivos en este formato. Sin embargo, parecía vacío al cargarse en R. Debido a esta situación, se decidió convertir el archivo de Excel a formato CSV para garantizar su correcta lectura y manipulación.

# Cargar la base de datos en la variable "datos"
datos <- read.csv(file.choose(), 
        sep=",", 
        header=TRUE, 
        fileEncoding = "UTF-8")

# Inspección de la estructura del dataset
library(kableExtra)
kable(datos[1:10,], caption= "Tabla 1: DataSet ") %>%
  kable_styling(full_width = F) %>%
  column_spec(2, width = "20em") %>%
  scroll_box(width = "900px", height = "450px")
Tabla 1: DataSet
Algorithm Framework Problem_Type Dataset_Type Accuracy Precision Recall F1_Score Training_Time Date
SVM Scikit-learn Regression Time Series 0.6618051 0.6929447 NA 0.4426950 4.978592 2023-03-08 11:26:21
K-Means Keras Clustering Time Series 0.7443216 0.4900292 0.8766533 0.4414046 NA 2023-03-09 11:26:21
Neural Network Keras Clustering Image 0.8852037 0.5948056 0.9685424 0.9644707 3.282594 2023-03-10 11:26:21
SVM Keras Clustering Text 0.8416477 0.8424142 0.8748388 0.7041523 4.041629 2023-03-11 11:26:21
SVM Scikit-learn Regression Tabular 0.7229514 0.6856109 0.3010956 0.6456472 3.603991 2023-03-12 11:26:21
K-Means PyTorch Regression Image 0.6368133 0.6255330 7.4548096 0.8865271 3.006475 2023-03-13 11:26:21
Neural Network PyTorch Regression Text 0.9985622 0.6366858 0.3357948 0.9014956 NA 2023-03-14 11:26:21
Neural Network Scikit-learn Regression Image 0.7130907 0.6756681 0.4803251 0.5993146 2.328345 2023-03-15 11:26:21
SVM Keras Regression Time Series NA 0.8710099 0.3416673 0.8161708 3.406453 2023-03-16 11:26:21
Random Forest Keras Regression Text 0.5818119 0.9352508 NA 0.8626737 3.419905 2023-03-17 11:26:21

Dimensiones

dim(datos)
## [1] 560  10

El resultado muestra un data frame con 560 observaciones y 10 variables. Este tamaño de muestra es suficiente para realizar un análisis significativo, ya que permite identificar patrones y relaciones en los datos.

Nombres de las Variables y Tipo de Dato (Estructura):

sapply(datos, class)
##     Algorithm     Framework  Problem_Type  Dataset_Type      Accuracy 
##   "character"   "character"   "character"   "character"     "numeric" 
##     Precision        Recall      F1_Score Training_Time          Date 
##     "numeric"     "numeric"     "numeric"     "numeric"   "character"

Clasificación de las Variables:

Cualitativas Categóricas:

  • Algorithm: Indica el tipo de algoritmo de inteligencia artificial utilizado.

  • Framework: Indica el framework o biblioteca utilizada para la implementación del modelo.

  • Problem_Type: Identifica el tipo de problema abordado por el modelo

  • Dataset_Type: Describe el tipo de datos utilizados en el entrenamiento del modelo.

  • Date: Representa la fecha en la que se realizó la evaluación del modelo.

Cuantitativas Continuas:

  • Accuracy: Mide la precisión del modelo.

  • Precision: Mide la precisión del modelo en términos de la fracción de positivos verdaderos sobre el total de positivos predichos.

  • Recall: Mide la capacidad del modelo para identificar correctamente los positivos verdaderos.

  • F1_Score: Mide el balance entre precisión y recall, medida armónica de ambas métricas.

  • Training_Time: Indica el tiempo de entrenamiento del modelo en horas.

str(datos)
## 'data.frame':    560 obs. of  10 variables:
##  $ Algorithm    : chr  "SVM" "K-Means" "Neural Network" "SVM" ...
##  $ Framework    : chr  "Scikit-learn" "Keras" "Keras" "Keras" ...
##  $ Problem_Type : chr  "Regression" "Clustering" "Clustering" "Clustering" ...
##  $ Dataset_Type : chr  "Time Series" "Time Series" "Image" "Text" ...
##  $ Accuracy     : num  0.662 0.744 0.885 0.842 0.723 ...
##  $ Precision    : num  0.693 0.49 0.595 0.842 0.686 ...
##  $ Recall       : num  NA 0.877 0.969 0.875 0.301 ...
##  $ F1_Score     : num  0.443 0.441 0.964 0.704 0.646 ...
##  $ Training_Time: num  4.98 NA 3.28 4.04 3.6 ...
##  $ Date         : chr  "2023-03-08 11:26:21" "2023-03-09 11:26:21" "2023-03-10 11:26:21" "2023-03-11 11:26:21" ...

Resumen Estadístico

Variables Cualitativas

En esta sección se presenta el Resumen Estadístico del dataset. El análisis de las variables cualitativas se realiza mediante tablas de frecuencia y diagramas de barras, que muestran la distribución de las categorías en cada una. Esto es esencial para identificar cómo se distribuyen las observaciones entre las distintas categorías.

Por otro lado, el análisis de las variables cuantitativas incluye un resumen estadístico que muestra medidas como la media, mediana, mínimo, máximo y los cuartiles. Estas medidas son clave para comprender la dispersión y tendencia central de los datos numéricos.

Es importante destacar que la variable “Date” no fue considerada para este análisis exploratorio, ya que su relevancia en el contexto no era crítica para los objetivos actuales.

# Resumen estadístico para las variables cualitativas
table(datos$Algorithm)
## 
##        K-Means Neural Network  Random Forest            SVM 
##            163            135            126            136
counts <- table(datos$Algorithm)
bp <- barplot(counts, 
              main="Frecuencia de Algoritmos", 
              xlab="Algoritmo", 
              ylab="Frecuencia", 
              col="lightblue", 
              ylim=c(0, max(counts) + 10), 
              cex.main=1.5,  
              cex.lab=1.2,   
              cex.axis=1)   
text(x=bp, y=counts + 6, labels=counts, cex=1, col="black")

Esta información permite observar que K-Means es el algoritmo más común, mientras que Random Forest es el menos frecuente en los datos analizados. Los otros dos algoritmos, Neural Network y SVM, presentan frecuencias similares.

table(datos$Framework)
## 
##        Keras      PyTorch Scikit-learn   TensorFlow 
##          124          135          134          167
counts <- table(datos$Framework)
bp <- barplot(counts, 
        main="Frecuencia de Frameworks", 
        xlab="Framework", 
        ylab="Frecuencia", 
        col="lightgreen",
        ylim=c(0, max(counts) + 10), 
        cex.main=1.5, 
        cex.lab=1.2,   
        cex.axis=1)    
text(x=bp, y=counts+6, labels=counts, cex=1, col="black")

A partir de esto, se observa que TensorFlow es el framework preferido, con una diferencia significativa en relación con los demás. Por otro lado, Keras es el menos utilizado. Tanto PyTorch como Scikit-learn tienen frecuencias muy cercanas entre sí, lo que indica que son opciones igualmente populares.

table(datos$Problem_Type)
## 
## Classification     Clustering     Regression 
##            175            196            189
counts <- table(datos$Problem_Type)
bp <- barplot(counts, 
              main="Frecuencia de Tipos de Problema", 
              xlab="Tipo de Problema", 
              ylab="Frecuencia", 
              col="lightcoral",
              ylim=c(0, max(counts) + 10), 
              cex.main=1.5, 
              cex.lab=1.2,   
              cex.axis=1)    
text(x=bp, y=counts + 6, labels=counts, cex=1, col="black")

El análisis muestra que el Clustering es el tipo de problema más frecuente, seguido de cerca por la Regresión y la Clasificación. Aunque las diferencias en frecuencia no son drásticas, esta variabilidad sugiere que cada tipo de problema podría afectar de manera distinta el balance entre precisión y tiempo de entrenamiento.

table(datos$Dataset_Type)
## 
##       Image     Tabular        Text Time Series 
##         157         136         143         124
counts <- table(datos$Dataset_Type)
bp <- barplot(counts, 
              main="Frecuencia de Tipos de Dataset", 
              xlab="Tipo de Dataset", 
              ylab="Frecuencia", 
              col="lightgoldenrod",  
              ylim=c(0, max(counts) + 15),  
              cex.main=1.5,  
              cex.lab=1.2,   
              cex.axis=1)    
text(x=bp, y=counts+6, labels=counts, cex=1, col="black")

En este análisis, el tipo de dataset más frecuente es Image, lo que lo convierte en la moda. Le sigue el tipo de dataset Text, luego Tabular. Finalmente, el tipo de dataset menos común es Time Series.

Variables Cuantitativas

# Resumen estadístico para las variables cuantitativas
datos_numericos <- datos[, sapply(datos, is.numeric)]

summary(datos_numericos)
##     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    
##  Min.   : 0.1032  
##  1st Qu.: 1.2441  
##  Median : 2.4347  
##  Mean   : 2.9910  
##  3rd Qu.: 3.8131  
##  Max.   :46.9856  
##  NA's   :20

Para el análisis de las variables cuantitativas, primero se utilizará la Prueba de Shapiro-Wilk con el objetivo de evaluar si los datos de cada variable siguen una distribución normal. Antes de proceder con la visualización gráfica, se realizará una limpieza de los datos, eliminando tanto los valores atípicos como los casos con valores faltantes, ya que estos pueden distorsionar la visualización. Después de depurar los datos de outliers y NAs, se procederá a graficar las distribuciones sin la interferencia de estos casos, permitiendo obtener una representación más clara y precisa de la distribución de los datos.

shapiro.test(datos$Accuracy)
## 
##  Shapiro-Wilk normality test
## 
## data:  datos$Accuracy
## W = 0.22771, p-value < 2.2e-16
shapiro.test(datos$Precision)
## 
##  Shapiro-Wilk normality test
## 
## data:  datos$Precision
## W = 0.25643, p-value < 2.2e-16
shapiro.test(datos$Recall)
## 
##  Shapiro-Wilk normality test
## 
## data:  datos$Recall
## W = 0.31806, p-value < 2.2e-16
shapiro.test(datos$F1_Score)
## 
##  Shapiro-Wilk normality test
## 
## data:  datos$F1_Score
## W = 0.2547, p-value < 2.2e-16
shapiro.test(datos$Training_Time)
## 
##  Shapiro-Wilk normality test
## 
## data:  datos$Training_Time
## W = 0.34923, p-value < 2.2e-16

Los resultados de las pruebas de normalidad de Shapiro-Wilk para las variables Accuracy, Precision, Recall, F1_Score, y Training_Time indican que ninguna de las variables sigue una distribución normal, ya que en todos los casos el p-valor fue significativamente menor a 0.05. Dado que los datos no son normales, el enfoque más adecuado sería imputar los valores faltantes usando la mediana.

library(Amelia)
## Cargando paquete requerido: Rcpp
## ## 
## ## Amelia II: Multiple Imputation
## ## (Version 1.8.2, built: 2024-04-10)
## ## Copyright (C) 2005-2024 James Honaker, Gary King and Matthew Blackwell
## ## Refer to http://gking.harvard.edu/amelia/ for more information
## ##
missmap(datos, 
        main = "Mapa de Valores Faltantes", 
        col = c("black", "yellow"), 
        legend = TRUE)

# Imputar NAs con la mediana

datos$Accuracy[is.na(datos$Accuracy)] <- median(datos$Accuracy, na.rm = TRUE)
datos$Precision[is.na(datos$Precision)] <- median(datos$Precision, na.rm = TRUE)
datos$Recall[is.na(datos$Recall)] <- median(datos$Recall, na.rm = TRUE)
datos$F1_Score[is.na(datos$F1_Score)] <- median(datos$F1_Score, na.rm = TRUE)
datos$Training_Time[is.na(datos$Training_Time)] <- median(datos$Training_Time, na.rm = TRUE)
missmap(datos, 
        main = "Mapa de Valores Faltantes", 
        col = c("black", "yellow"), 
        legend = TRUE)

Ahora, procederemos a graficar las variables para detectar posibles valores atípicos, utilizaremos diagramas de caja y bigotes (boxplots).

# Boxplot para Accuracy
boxplot(datos$Accuracy, 
        main="Distribución de Accuracy", 
        ylab="Valores de Accuracy", 
        col="lightgoldenrod", 
        ylim=c(min(datos$Accuracy, na.rm = TRUE) - 1, max(datos$Accuracy, na.rm = TRUE) + 1),  
        cex.main=1.5,  
        cex.lab=1.2,   
        cex.axis=1, 
        outline=TRUE)

datos$Accuracy[datos$Accuracy > 4] <- median(datos$Accuracy, na.rm = TRUE)

# Boxplot para Accuracy sin los outliers con anotaciones
boxplot(datos$Accuracy,
        main="Distribución de Accuracy (Sin Outliers)", 
        ylab="Valores de Accuracy", 
        col="lightgoldenrod",  
        ylim=c(min(datos$Accuracy, na.rm = TRUE) - 1, max(datos$Accuracy, na.rm = TRUE) + 1),  
        cex.main=1.5,  
        cex.lab=1.2,   
        cex.axis=1, 
        outline=TRUE)

# Boxplot para Precision          
boxplot(datos$Precision, 
        main="Distribución de Precision", 
        ylab="Valores de Precision", 
        col="blue", 
        ylim=c(min(datos$Precision, na.rm = TRUE) - 1, max(datos$Precision, na.rm = TRUE) + 1),  
        cex.main=1.5,  
        cex.lab=1.2,   
        cex.axis=1, 
        outline=TRUE)

datos$Precision[datos$Precision > 2] <- median(datos$Precision, na.rm = TRUE)

# Boxplot para Precision sin los outliers con anotaciones
boxplot(datos$Precision,
        main="Distribución de Precision (Sin Outliers)", 
        ylab="Valores de Precision", 
        col="blue",  
        ylim=c(min(datos$Precision, na.rm = TRUE) - 1, max(datos$Precision, na.rm = TRUE) + 1),  
        cex.main=1.5,  
        cex.lab=1.2,   
        cex.axis=1, 
        outline=TRUE)

# Boxplot para Recall          
boxplot(datos$Recall, 
        main="Distribución de Recall", 
        ylab="Valores de Recall", 
        col="red", 
        ylim=c(min(datos$Recall, na.rm = TRUE) - 1, max(datos$Recall, na.rm = TRUE) + 1),  
        cex.main=1.5,  
        cex.lab=1.2,   
        cex.axis=1, 
        outline=TRUE)

datos$Recall[datos$Recall > 2] <- median(datos$Recall, na.rm = TRUE)

# Boxplot para Recall sin los outliers con anotaciones
boxplot(datos$Recall,
        main="Distribución de Recall (Sin Outliers)", 
        ylab="Valores de Recall", 
        col="red",  
        ylim=c(min(datos$Recall, na.rm = TRUE) - 1, max(datos$Recall, na.rm = TRUE) + 1),  
        cex.main=1.5,  
        cex.lab=1.2,   
        cex.axis=1, 
        outline=TRUE)

# Boxplot para F1_Score               
boxplot(datos$F1_Score, 
        main="Distribución de F1_Score", 
        ylab="Valores de F1_Score", 
        col="orange", 
        ylim=c(min(datos$F1_Score, na.rm = TRUE) - 1, max(datos$F1_Score, na.rm = TRUE) + 1),  
        cex.main=1.5,  
        cex.lab=1.2,   
        cex.axis=1, 
        outline=TRUE)

datos$F1_Score[datos$F1_Score > 4 ] <- median(datos$F1_Score, na.rm = TRUE)

# Boxplot para Recall sin los outliers con anotaciones
boxplot(datos$F1_Score,
        main="Distribución de F1_Score (Sin Outliers)", 
        ylab="Valores de F1_Score", 
        col="orange",  
        ylim=c(min(datos$F1_Score, na.rm = TRUE) - 1, max(datos$F1_Score, na.rm = TRUE) + 1),  
        cex.main=1.5,  
        cex.lab=1.2,   
        cex.axis=1, 
        outline=TRUE)

# Boxplot para Training_Time               
boxplot(datos$Training_Time, 
        main="Distribución de Training_Time", 
        ylab="Valores de Training_Time", 
        col="green", 
        ylim=c(min(datos$Training_Time, na.rm = TRUE) - 1, max(datos$Training_Time, na.rm = TRUE) + 1),  
        cex.main=1.5,  
        cex.lab=1.2,   
        cex.axis=1, 
        outline=TRUE)

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

# Boxplot para Recall sin los outliers con anotaciones
boxplot(datos$Training_Time,
        main="Distribución de Training_Time (Sin Outliers)", 
        ylab="Valores de Training_Time", 
        col="green",  
        ylim=c(min(datos$Training_Time, na.rm = TRUE) - 1, max(datos$Training_Time, na.rm = TRUE) + 1),  
        cex.main=1.5,  
        cex.lab=1.2,   
        cex.axis=1, 
        outline=TRUE)