Machine Learning. Taller 1: Gestión de datos

Etapa 1: Dominio del problema

Una consultora de desarrollo de software busca mejorar sus procesos de gestión de proyectos y la calidad del software que produce. Para ello, ha recopilado datos de sus últimos 1000 proyectos con el objetivo de:

  1. Desarrollar modelos predictivos para estimar el nivel de éxito de futuros proyectos.
  2. Descubrir patrones y grupos de proyectos similares.
  3. Desplegar una herramienta de apoyo a la toma de decisiones basada en la estimación de éxito.

Descripción de variables

Variables independientes

  1. id_proyecto:
    • Identificador único del proyecto
    • Formato: PROJXXXX donde XXXX es un número secuencial
    • Sin valores faltantes ni problemas de calidad
  2. fecha_inicio:
    • Fecha en que comenzó el proyecto
    • Rango: 2020-01-01 a 2023-12-31
    • Sin valores faltantes ni problemas de calidad
  3. tamano_equipo:
    • Número de desarrolladores en el equipo
    • Rango válido: 3-15 personas
    • Puede contener valores NA y valores fuera de rango
  4. complejidad_proyecto:
    • Nivel de complejidad técnica y funcional
    • Escala: 1 (muy simple) a 10 (extremadamente complejo)
    • Puede contener valores NA y valores fuera de rango
  5. metodologia:
    • Enfoque metodológico utilizado
    • Valores válidos: “Agil”, “Cascada”, “Hibrida”
    • Puede contener inconsistencias en mayúsculas/minúsculas, valores NA y valores no válidos
  6. stack_tecnologico:
    • Tecnología principal utilizada
    • Valores válidos: “Java”, “Python”, “JavaScript”, “C++”, “.NET”
    • Puede contener variaciones en la escritura, valores NA y valores no válidos
  7. tiene_pruebas_automatizadas:
    • Indica si el proyecto implementó pruebas automatizadas
    • Valor booleano: TRUE/FALSE
    • Puede contener valores NA
  8. cobertura_revision_codigo:
    • Porcentaje del código que pasa por revisión
    • Rango válido: 0 a 0.4 (0% a 40%)
    • Puede contener valores NA y valores fuera de rango
  9. duracion_meses:
    • Duración real del proyecto en meses
    • Unidad: meses
    • Calculada a partir de fecha_inicio y complejidad:
      • Complejidad 1-3: 1-3 meses
      • Complejidad 4-6: 3-6 meses
      • Complejidad 7-8: 6-12 meses
      • Complejidad 9-10: 12-24 meses
    • Sin valores problemáticos
  10. errores_por_kloc: - Número de errores por cada mil líneas de código - Valor mínimo: 0 - Puede contener valores atípicos que requieren limpieza
  11. puntuacion_calidad: - Métrica compuesta de calidad del código - Escala: 0-100 - Puede contener valores fuera de rango
  12. productividad_equipo: - Medida de la productividad del equipo - Escala: 0-100 - Puede contener valores NA
  13. satisfaccion_cliente: - Nivel de satisfacción reportado por el cliente - Escala: 0-100 - Puede contener valores fuera de rango y valores atípicos que requieren limpieza

Variable dependiente

  1. exito_proyecto: - Clasificación final del éxito del proyecto - Valores: “Bajo”, “Medio”, “Alto” - Sin valores faltantes ni problemas de calidad

Preguntas a responder

A partir de la información proporcionada, se responden las siguientes preguntas:

  1. ¿Qué exactamente deseamos hacer?

    Estimar el nivel de éxito de futuros proyectos a partir de registros históricos

  2. ¿Es factible alcanzar lo que buscamos con los datos disponibles?

    Si, las variables proporcionadas aparentemente parecen ser suficientes para alcanzar la clasificación.

  3. ¿Cómo podemos lograrlo?

    Aplicar técnicas predictivas de Machine Learning.

  4. ¿Qué tipo de problema se va a resolver?

    Problema de clasificación (éxito de proyectos).

  5. ¿El objetivo es?

    Predecir a nivel categórico (éxito de proyecto).

Precisando:

Variable a predecir:

  • Éxito del proyecto (exito_proyecto): clasificación del éxito del proyecto de software en función de las características proporcionadas en proyectos previos. Clases disponibles:

    • Bajo: Proyecto que no alcanza objetivos principales, presenta problemas significativos y genera insatisfacción en cliente y equipo.

    • Medio: Proyecto que cumple requisitos básicos con algunos inconvenientes menores, manteniendo niveles aceptables de calidad y satisfacción.

    • Alto: Proyecto que cumple objetivos superando expectativas, con cliente satisfecho, código de calidad y equipo altamente productivo.

Etapa 2: Adquisición de datos

En esta etapa, se procederá a la adquisición de los datos necesarios para el análisis. Los datos serán leídos desde un archivo CSV y se inspeccionarán para asegurar que se hayan cargado correctamente.

# Cargar librerías necesarias
library(tidyverse)
library(lubridate) # Para trabajar con fechas
library(skimr)       # Para resumen de datos
library(DataExplorer) # Para análisis exploratorio
library(corrplot)    # Para matrices de correlación
library(scales)      # Para formato de escalas
library(knitr)      # Para tablas
library(gridExtra)   # Para organizar gráficos
library(GGally) # Para GGally

 
# Cargar datos
datos_raw <- read.csv("datos_proyectos_software.csv")

# Mostrar estructura inicial
glimpse(datos_raw)
Rows: 1,000
Columns: 14
$ id_proyecto                 <chr> "PROJ0001", "PROJ0002", "PROJ0003", "PROJ0…
$ fecha_inicio                <chr> "2023-12-26", "2020-02-06", "2020-02-24", …
$ tamano_equipo               <int> 9, 13, 8, 5, -1, 13, NA, -1, 8, 9, 5, 5, N…
$ complejidad_proyecto        <int> 3, 2, 9, 3, 15, 10, -5, -5, 3, 10, 4, 6, -…
$ metodologia                 <chr> "Hibrida", "Cascada", "Agil", "Agil", "agi…
$ stack_tecnologico           <chr> ".NET", "C++", "Python", "Python", "PYTHON…
$ tiene_pruebas_automatizadas <lgl> TRUE, FALSE, FALSE, FALSE, NA, FALSE, NA, …
$ cobertura_revision_codigo   <dbl> 0.83708072, 0.96360922, 0.73966576, 0.9403…
$ duracion_meses              <dbl> 2.214286, 2.142857, 16.250000, 2.107143, 1…
$ errores_por_kloc            <dbl> 2.04783674, 0.00000000, 2.02785552, 2.1322…
$ puntuacion_calidad          <dbl> 94.55915, 90.42903, 63.23575, 90.96441, 10…
$ productividad_equipo        <dbl> 86.13118, 59.56815, 90.25260, 92.75201, NA…
$ satisfaccion_cliente        <dbl> 90.320365, 70.252079, 78.663337, 89.935600…
$ exito_proyecto              <chr> "Alto", "Bajo", "Medio", "Alto", "Bajo", "…
# Resumen inicial del dataset
skim(datos_raw)
Data summary
Name datos_raw
Number of rows 1000
Number of columns 14
_______________________
Column type frequency:
character 5
logical 1
numeric 8
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
id_proyecto 0 1.00 8 8 0 1000 0
fecha_inicio 0 1.00 10 10 0 1000 0
metodologia 30 0.97 4 11 0 6 0
stack_tecnologico 20 0.98 3 10 0 8 0
exito_proyecto 0 1.00 4 5 0 3 0

Variable type: logical

skim_variable n_missing complete_rate mean count
tiene_pruebas_automatizadas 100 0.9 0.55 TRU: 492, FAL: 408

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
tamano_equipo 26 0.97 41.06 176.70 -1.00 5.00 9.00 12.75 999.00 ▇▁▁▁▁
complejidad_proyecto 31 0.97 5.29 3.85 -5.00 3.00 5.00 8.00 15.00 ▁▆▇▆▁
cobertura_revision_codigo 0 1.00 0.64 0.25 -0.20 0.51 0.66 0.83 1.00 ▁▁▃▇▇
duracion_meses 5 1.00 7.23 5.25 2.11 2.18 5.46 7.64 16.36 ▆▇▁▁▃
errores_por_kloc 0 1.00 5.78 17.44 -9.47 0.02 0.93 2.12 100.00 ▇▁▁▁▁
puntuacion_calidad 0 1.00 79.92 19.62 40.35 67.04 77.01 88.54 149.69 ▂▇▃▁▁
productividad_equipo 100 0.90 79.23 13.42 53.57 61.53 86.10 89.55 96.89 ▆▁▁▇▇
satisfaccion_cliente 0 1.00 79.85 17.85 -19.91 71.30 87.13 90.08 118.78 ▁▁▁▇▁

Etapa 3: Preprocesamiento

En esta etapa, realizaremos el preprocesamiento de los datos para asegurarnos de que estén listos para el análisis posterior. Esto incluye: a. limpieza, b. transformación y c. análisis exploratorio de datos (EDA)

Para comenzar, contestamos las siguientes preguntas:

¿Existen valores ausentes o datos faltantes inconsistentes en las variables?

# Visualizar datos faltantes
plot_missing(datos_raw)

Observamos que si existen algunos valores ausentes o faltantes en 7 de las 14 variables.

¿Existen valores fuera de rango en las variables?

# Visualizar datos fuera de rango
fuera_de_rango <- datos_raw %>%
  summarise(
    # Verificar si las variables numéricas (9) tienen valores fuera de rango
    tamano_equipo_fr = sum(tamano_equipo < 3 | tamano_equipo > 15, na.rm = TRUE),
    complejidad_fr = sum(complejidad_proyecto < 1 | complejidad_proyecto > 10, na.rm = TRUE),
    cobertura_fr = sum(cobertura_revision_codigo < 0 | cobertura_revision_codigo > 1, na.rm = TRUE), 
    duracion_meses_fr = sum(duracion_meses < 0 , na.rm = TRUE),
    errores_por_kloc_fr= sum(errores_por_kloc < 0 , na.rm = TRUE),
    puntuacion_calidad_fr = sum(puntuacion_calidad < 0 | puntuacion_calidad > 100, na.rm = TRUE),
    productividad_equipo_fr = sum(productividad_equipo < 0 | productividad_equipo > 100, na.rm = TRUE),
    satisfaccion_cliente_fr = sum(satisfaccion_cliente < 0 | satisfaccion_cliente > 100, na.rm = TRUE),  

  )

kable(gather(fuera_de_rango), 
      col.names = c("Variable", "Casos fuera de rango"))
Variable Casos fuera de rango
tamano_equipo_fr 74
complejidad_fr 69
cobertura_fr 32
duracion_meses_fr 0
errores_por_kloc_fr 6
puntuacion_calidad_fr 100
productividad_equipo_fr 0
satisfaccion_cliente_fr 26

Observamos que existen 6 de las 8 variables numéricas con valores fuera de rango.

¿Existen valores inconsistentes en las variables?

# Verificar inconsistencias (diversas formas de escribir misma categoría)
datos_raw %>%
  select(metodologia, stack_tecnologico, exito_proyecto, tiene_pruebas_automatizadas) %>%
  map(table)
$metodologia

       agil        Agil        AGIL     Cascada Desconocido     Hibrida 
         25         378          13         285          32         237 

$stack_tecnologico

      .NET        C++       java       Java JavaScript       Otro     Python 
       125        182         25        222        196         23        175 
    PYTHON 
        32 

$exito_proyecto

 Alto  Bajo Medio 
  522   253   225 

$tiene_pruebas_automatizadas

FALSE  TRUE 
  408   492 

Observamos que existen 2 variables categóricas (metodologia y stack_tecnologico) que presentan diversas formas de escritura de una misma categoría.

¿Existen valores atípicos en las variables?

# Función para detectar valores atípicos usando IQR

# Un valor atípico es aquel que está muy alejado del grueso de los datos - imagina una persona de 3 metros de altura cuando la mayoría mide entre 1.50 y 1.90 metros. La fórmula solo pone números a qué consideramos "muy alejado".

detectar_atipicos <- function(x) {
  q1 <- quantile(x, 0.25, na.rm = TRUE)
  q3 <- quantile(x, 0.75, na.rm = TRUE)
  iqr <- q3 - q1
  limite_inferior <- q1 - 1.5 * iqr
  limite_superior <- q3 + 1.5 * iqr
  return(list(inf = limite_inferior, sup = limite_superior))
}

# Cálculo de límites para valores atípicos para las 2 variables que en descripción se dijo tenian valores atipicos
limites_errores <- detectar_atipicos(datos_raw$errores_por_kloc) 
limites_satisfaccion <- detectar_atipicos(datos_raw$satisfaccion_cliente)

# Para cada variable, contar cuántos valores atípicos hay
atipicos <- datos_raw %>%
 summarise(
   atipicos_errores = sum(!between(errores_por_kloc, limites_errores$inf, limites_errores$sup)), 
   atipicos_satisfaccion = sum(!between(satisfaccion_cliente, limites_satisfaccion$inf, limites_satisfaccion$sup))
 )

kable(gather(atipicos), 
      col.names = c("Variable", "Atípicos"))
Variable Atípicos
atipicos_errores 94
atipicos_satisfaccion 43

Se observan que efectivamente ambas variables presentan valores atípicos.

a. limpieza

Se realiza la limpieza para cada una de los problemas de calidad encontrados

# 1. Limpieza de valores ausentes (NA)
datos_clean <- datos_raw %>%
 drop_na()
 
# 2. Limpieza de valores fuera de rango
datos_clean <- datos_clean %>%
  filter(
    between(tamano_equipo, 3, 15),
    between(complejidad_proyecto, 1, 10),
    between(cobertura_revision_codigo, 0, 1),
    duracion_meses >= 0,
    errores_por_kloc >= 0,
    between(puntuacion_calidad, 0, 100),
    between(productividad_equipo, 0, 100),
    between(satisfaccion_cliente, 0, 100)
  )

# 3. Limpieza de inconsistencias en categóricas
datos_clean <- datos_clean %>%
  mutate(
    metodologia = case_when(
      str_to_lower(metodologia) %in% c("agil", "Agil", "AGIL") ~ "Agil",
      str_to_lower(metodologia) %in% c("cascada", "cascada") ~ "Cascada",
      str_to_lower(metodologia) %in% c("hibrida", "desconocido") ~ "Hibrida"
    ),
    
    stack_tecnologico = case_when(
      str_to_lower(stack_tecnologico) %in% c("java", "Java") ~ "Java",
      str_to_lower(stack_tecnologico) %in% c("python", "PYTHON") ~ "Python",
      str_to_lower(stack_tecnologico) %in% c("javascript") ~ "JavaScript",
      str_to_lower(stack_tecnologico) %in% c("c++", "cpp","Otro") ~ "C++",
      str_to_lower(stack_tecnologico) %in% c(".NET", ".net") ~ ".NET"
    )
  )

# 4. Limpieza de valores atípicos (solo para las variables mencionadas)
datos_clean <- datos_clean %>%
  filter(
    between(errores_por_kloc, limites_errores$inf, limites_errores$sup),
    between(satisfaccion_cliente, limites_satisfaccion$inf, limites_satisfaccion$sup)
  )

# 5. Verificación final
glimpse(datos_clean)
Rows: 896
Columns: 14
$ id_proyecto                 <chr> "PROJ0001", "PROJ0002", "PROJ0003", "PROJ0…
$ fecha_inicio                <chr> "2023-12-26", "2020-02-06", "2020-02-24", …
$ tamano_equipo               <int> 9, 13, 8, 5, 13, 8, 9, 5, 5, 5, 14, 3, 4, …
$ complejidad_proyecto        <int> 3, 2, 9, 3, 10, 3, 10, 4, 6, 5, 10, 6, 7, …
$ metodologia                 <chr> "Hibrida", "Cascada", "Agil", "Agil", "Agi…
$ stack_tecnologico           <chr> ".NET", "C++", "Python", "Python", ".NET",…
$ tiene_pruebas_automatizadas <lgl> TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TR…
$ cobertura_revision_codigo   <dbl> 0.8370807, 0.9636092, 0.7396658, 0.9403910…
$ duracion_meses              <dbl> 2.214286, 2.142857, 16.250000, 2.107143, 1…
$ errores_por_kloc            <dbl> 2.04783674, 0.00000000, 2.02785552, 2.1322…
$ puntuacion_calidad          <dbl> 94.55915, 90.42903, 63.23575, 90.96441, 60…
$ productividad_equipo        <dbl> 86.13118, 59.56815, 90.25260, 92.75201, 90…
$ satisfaccion_cliente        <dbl> 90.32037, 70.25208, 78.66334, 89.93560, 79…
$ exito_proyecto              <chr> "Alto", "Bajo", "Medio", "Alto", "Medio", …
# Resumen de la limpieza
resumen_limpieza <- data.frame(
  Metrica = c("Registros originales", "Registros después de limpieza",
              "Registros eliminados", "Porcentaje de datos conservados"),
  Valor = c(
    nrow(datos_raw), 
    nrow(datos_clean),
    nrow(datos_raw) - nrow(datos_clean),
    round(nrow(datos_clean) / nrow(datos_raw) * 100, 2)
  )
)

kable(resumen_limpieza)
Metrica Valor
Registros originales 1000.0
Registros después de limpieza 896.0
Registros eliminados 104.0
Porcentaje de datos conservados 89.6

Se eliminan cerca de 100 instancias. Lo que representa un 10% de pérdida aproximadamente. Un porcentaje dentro de lo permitido.

b. transformación

Para procesos de transformación, únicamente se numerizan las variables categóricas (opcionalmente se pudo haber normalizado las variables, usando por ejemplo, normalización z).

# Transformación de datos categóricos a numéricos
datos_clean <- datos_clean %>%
  mutate(
    # Transformar metodologia
    metodologia_num = case_when(
      metodologia == "Agil" ~ 1,
      metodologia == "Cascada" ~ 2,
      metodologia == "Hibrida" ~ 3
    ),
    
    # Transformar stack_tecnologico
    stack_tecnologico_num = case_when(
      stack_tecnologico == "Java" ~ 1,
      stack_tecnologico == "Python" ~ 2,
      stack_tecnologico == "JavaScript" ~ 3,
      stack_tecnologico == "C++" ~ 4,
      stack_tecnologico == ".NET" ~ 5
    ),
    
    # Transformar tiene_pruebas_automatizadas
    pruebas_automatizadas_num = if_else(tiene_pruebas_automatizadas == TRUE, 1, 2),
    
    # Convertir la variable a clasificar 'exito_proyecto' en factor
     exito_proyecto_factor = factor(exito_proyecto, 
                                  levels = c("Bajo", "Medio", "Alto"), labels = c(1, 2, 3))
  )  

  # Contar los registros que existen de cada clase de uso del suelo
  datos_clean %>%
    select_if(is.factor) %>% count(exito_proyecto_factor)
  exito_proyecto_factor   n
1                     1 153
2                     2 224
3                     3 519

Se observa la creación de 4 nuevas variables con la información numerizada de las variables metodologia, stack_tecnologico, pruebas_automatizadas y exito_proyecto.

c. análisis exploratorio de datos (EDA)

Distribución de datos

¿Cuál es la distribución de cada variable en el conjunto de datos?

Distribución de variables cuantitativas

Para explorar la distribución de las variables cuantitativas las convertimos en un formato largo y generamos histogramas.

# Seleccionar solo las variables numéricas
datos_numericos <- datos_clean %>% select(where(is.numeric)) # Excluir variables cualitativas

# Convertir el conjunto de datos en formato largo
datos_long <- pivot_longer(datos_numericos, cols = everything())

# Trazar histogramas facetas para todas las variables numéricas
ggplot(datos_long, aes(x = value)) +
   geom_histogram(aes(y = ..density..), bins = 30, fill = "skyblue", alpha = 0.7) +
    geom_density(color = "red") +
  facet_wrap(~name, scales = "free") +
  labs(title = "Distribución de variables cuantitativas", x = "Valor", y = "Frecuencia") +
  theme_minimal()

Distribución de variables cualitativas

Para la variable cualitativa exito_proyecto, utilizamos un gráfico de barras para visualizar su distribución.

# Análisis de variables cualitativas
vars_categoricas <- datos_clean %>%
  select(metodologia, stack_tecnologico, tiene_pruebas_automatizadas, exito_proyecto)

# Crear gráficos de barras
plots_categoricas <- map(names(vars_categoricas), function(var) {
  
  ggplot(datos_clean, aes_string(x = var, fill = var)) +
    geom_bar() +
    labs(title = str_to_title(str_replace_all(var, "_", " "))) +
    theme_minimal() +
    theme(axis.text.x = element_text(angle = 45, hjust = 1),
          legend.position = "none")
  
})

# Mostrar plots en grid
do.call(grid.arrange, c(plots_categoricas, ncol = 3))

A partir de la distribución de las variables observamos que el proceso de limpieza fue exitoso toda vez que no existen problemas de calidad de datos.

Correlaciones

El análisis de correlación es fundamental para comprender cómo se relacionan las diferentes variables entre sí dentro de un conjunto de datos. Por ello se plantea la interrogante ¿Existen correlaciones entre pares de variables independientes en el conjunto de datos?

# Seleccionar variables independientes para correlación
datos_correlacion <- datos_clean %>%
  select(tamano_equipo, 
         complejidad_proyecto, 
         metodologia_num, 
         stack_tecnologico_num, 
         pruebas_automatizadas_num,
         cobertura_revision_codigo,
         duracion_meses,
         errores_por_kloc, 
         puntuacion_calidad,
         productividad_equipo,
         satisfaccion_cliente) 

# Matriz de correlación
matriz_correlacion <- cor(datos_correlacion, use = "complete.obs")

# Visualizar matriz de correlación
corrplot(matriz_correlacion, 
         method = "color",
         type = "upper",
         addCoef.col = "black",
         tl.col = "black",
         tl.srt = 45,
         diag = FALSE)

Como identificamos pares de variables con alta correlación (mas del 0.50), procedemos a eliminar una de ellas para reducir la multicolinealidad de los datos.

# Eliminar la variables correlacionadas
datos_correlacion <- datos_correlacion %>% select(-c(complejidad_proyecto, cobertura_revision_codigo, productividad_equipo ) )

¿Hay correlación entre cada variable independiente y la variable objetivo?

La idea es identificar qué variables independientes tienen la mayor influencia en la variable objetivo (exito_num). Esto es crucial para modelos predictivos y para entender los factores más relevantes que afectan el fenómeno estudiado. Variables con alta correlación con la variable objetivo son generalmente más importantes en los modelos predictivos.

# Correlaciones con la variable objetivo
datos_clean$exito_num <- as.numeric(datos_clean$exito_proyecto_factor)

correlaciones_objetivo <- data.frame(
  Variable = names(datos_correlacion),
  Correlacion = cor(datos_correlacion, datos_clean$exito_num)
) %>%
  arrange(desc(abs(Correlacion)))

# Visualizar correlaciones con la variable objetivo
ggplot(correlaciones_objetivo, aes(x = reorder(Variable, abs(Correlacion)), y = Correlacion)) +
  geom_bar(stat = "identity", fill = "skyblue") +
  coord_flip() +
  labs(title = "Correlaciones con éxito del proyecto",
       x = "Variables",
       y = "Coeficiente de correlación") +
  theme_minimal() +
  theme(axis.text.y = element_text(size = 8))  # Hacer los nombres de variables más legibles

# Mostrar tabla de correlaciones
kable(correlaciones_objetivo, 
      col.names = c("Variable", "Correlación con éxito"),
      digits = 3)
Variable Correlación con éxito
satisfaccion_cliente satisfaccion_cliente 0.922
puntuacion_calidad puntuacion_calidad 0.194
metodologia_num metodologia_num -0.098
tamano_equipo tamano_equipo -0.047
errores_por_kloc errores_por_kloc -0.037
stack_tecnologico_num stack_tecnologico_num -0.029
pruebas_automatizadas_num pruebas_automatizadas_num -0.026
duracion_meses duracion_meses 0.020

Para las variables independientes y exito_num se realiza el comportamiento para cada una de las clases:

# Preparar datos con variables seleccionadas y éxito
datos_viz <- datos_clean %>%
  select(all_of(names(datos_correlacion)), exito_proyecto) %>%
  mutate(exito_proyecto = factor(exito_proyecto, 
                                levels = c("Bajo", "Medio", "Alto")))  # Asegurar orden correcto de factores

# Crear matriz de gráficos de dispersión
GGally::ggpairs(datos_viz, 
        legend = 1,
        mapping = ggplot2::aes(colour = exito_proyecto),
        lower = list(continuous = wrap("smooth", alpha = 0.3, size = 0.1))) +
  theme_minimal() +
  theme(legend.position = "bottom",
        axis.text = element_text(size = 8),
        strip.text = element_text(size = 8))

De la gráfica se observa que ciertas variables parecen tener alguna relación (se muestrán con asteriscos) con ciertas clases de la variable dependiente. Esto da idea de que estás variables pueden ser buenas al momento de realizar predicciones.

Estadística descriptiva

 # Cargar las bibliotecas necesarias
library(dplyr)      # Para manipulación de datos
library(ggplot2)    # Para visualización
library(tidyr)      # Para transformación de datos
library(gridExtra)  # Para organizar múltiples gráficos
library(lubridate)  # Para manejo de fechas
library(knitr)      # Para presentación de tablas

 
# Calcular estadísticas descriptivas detalladas para cada variable numérica
# agrupadas por nivel de éxito del proyecto
stats_descriptivas <- datos_viz %>%
  group_by(exito_proyecto) %>%
  summarise(
    across(
      where(is.numeric),
      list(
        media = ~round(mean(., na.rm = TRUE), 2),          # Media con 2 decimales
        mediana = ~round(median(., na.rm = TRUE), 2),      # Mediana con 2 decimales
        desv_est = ~round(sd(., na.rm = TRUE), 2),         # Desviación estándar
        cv = ~round(sd(., na.rm = TRUE) / mean(., na.rm = TRUE) * 100, 2), # Coeficiente de variación
        q25 = ~round(quantile(., 0.25, na.rm = TRUE), 2),  # Primer cuartil
        q75 = ~round(quantile(., 0.75, na.rm = TRUE), 2),  # Tercer cuartil
        iqr = ~round(IQR(., na.rm = TRUE), 2),             # Rango intercuartílico
        min = ~round(min(., na.rm = TRUE), 2),             # Valor mínimo
        max = ~round(max(., na.rm = TRUE), 2)              # Valor máximo
      )
    )
  ) %>%
  # Ordenar por nivel de éxito (Bajo, Medio, Alto)
  arrange(factor(exito_proyecto, levels = c("Bajo", "Medio", "Alto")))

# Mostrar las estadísticas descriptivas en formato tabular
print(kable(stats_descriptivas, 
            caption = "Estadísticas descriptivas por nivel de éxito",
            format = "pipe"))


Table: Estadísticas descriptivas por nivel de éxito

|exito_proyecto | tamano_equipo_media| tamano_equipo_mediana| tamano_equipo_desv_est| tamano_equipo_cv| tamano_equipo_q25| tamano_equipo_q75| tamano_equipo_iqr| tamano_equipo_min| tamano_equipo_max| metodologia_num_media| metodologia_num_mediana| metodologia_num_desv_est| metodologia_num_cv| metodologia_num_q25| metodologia_num_q75| metodologia_num_iqr| metodologia_num_min| metodologia_num_max| stack_tecnologico_num_media| stack_tecnologico_num_mediana| stack_tecnologico_num_desv_est| stack_tecnologico_num_cv| stack_tecnologico_num_q25| stack_tecnologico_num_q75| stack_tecnologico_num_iqr| stack_tecnologico_num_min| stack_tecnologico_num_max| pruebas_automatizadas_num_media| pruebas_automatizadas_num_mediana| pruebas_automatizadas_num_desv_est| pruebas_automatizadas_num_cv| pruebas_automatizadas_num_q25| pruebas_automatizadas_num_q75| pruebas_automatizadas_num_iqr| pruebas_automatizadas_num_min| pruebas_automatizadas_num_max| duracion_meses_media| duracion_meses_mediana| duracion_meses_desv_est| duracion_meses_cv| duracion_meses_q25| duracion_meses_q75| duracion_meses_iqr| duracion_meses_min| duracion_meses_max| errores_por_kloc_media| errores_por_kloc_mediana| errores_por_kloc_desv_est| errores_por_kloc_cv| errores_por_kloc_q25| errores_por_kloc_q75| errores_por_kloc_iqr| errores_por_kloc_min| errores_por_kloc_max| puntuacion_calidad_media| puntuacion_calidad_mediana| puntuacion_calidad_desv_est| puntuacion_calidad_cv| puntuacion_calidad_q25| puntuacion_calidad_q75| puntuacion_calidad_iqr| puntuacion_calidad_min| puntuacion_calidad_max| satisfaccion_cliente_media| satisfaccion_cliente_mediana| satisfaccion_cliente_desv_est| satisfaccion_cliente_cv| satisfaccion_cliente_q25| satisfaccion_cliente_q75| satisfaccion_cliente_iqr| satisfaccion_cliente_min| satisfaccion_cliente_max|
|:--------------|-------------------:|---------------------:|----------------------:|----------------:|-----------------:|-----------------:|-----------------:|-----------------:|-----------------:|---------------------:|-----------------------:|------------------------:|------------------:|-------------------:|-------------------:|-------------------:|-------------------:|-------------------:|---------------------------:|-----------------------------:|------------------------------:|------------------------:|-------------------------:|-------------------------:|-------------------------:|-------------------------:|-------------------------:|-------------------------------:|---------------------------------:|----------------------------------:|----------------------------:|-----------------------------:|-----------------------------:|-----------------------------:|-----------------------------:|-----------------------------:|--------------------:|----------------------:|-----------------------:|-----------------:|------------------:|------------------:|------------------:|------------------:|------------------:|----------------------:|------------------------:|-------------------------:|-------------------:|--------------------:|--------------------:|--------------------:|--------------------:|--------------------:|------------------------:|--------------------------:|---------------------------:|---------------------:|----------------------:|----------------------:|----------------------:|----------------------:|----------------------:|--------------------------:|----------------------------:|-----------------------------:|-----------------------:|------------------------:|------------------------:|------------------------:|------------------------:|------------------------:|
|Bajo           |                9.34|                    10|                   3.99|            42.76|                 6|                13|                 7|                 3|                15|                  2.00|                       2|                     0.00|               0.00|                   2|                   2|                   0|                   2|                   2|                        2.98|                             3|                           1.46|                    49.06|                         2|                         4|                         2|                         1|                         5|                            1.44|                                 1|                                0.5|                        34.51|                             1|                             2|                             1|                             1|                             2|                 6.66|                   5.46|                    4.63|             69.57|               2.21|               7.64|               5.43|               2.11|              16.36|                   1.01|                     0.63|                      1.11|              110.09|                 0.00|                 1.55|                 1.55|                    0|                 4.41|                    74.49|                      73.85|                       12.53|                 16.82|                  64.63|                  85.42|                  20.79|                  45.75|                    100|                      69.86|                        69.89|                          0.89|                    1.27|                    69.28|                    70.35|                     1.08|                    67.74|                    72.21|
|Medio          |                8.99|                     9|                   3.88|            43.20|                 5|                12|                 7|                 3|                15|                  1.88|                       2|                     0.63|              33.60|                   1|                   2|                   1|                   1|                   3|                        2.66|                             3|                           1.36|                    51.20|                         1|                         4|                         3|                         1|                         5|                            1.50|                                 2|                                0.5|                        33.31|                             1|                             2|                             1|                             1|                             2|                 6.63|                   5.46|                    4.98|             75.12|               2.18|               7.62|               5.44|               2.11|              16.36|                   1.23|                     0.97|                      1.16|               94.42|                 0.22|                 1.94|                 1.72|                    0|                 4.57|                    68.07|                      64.13|                       14.79|                 21.73|                  57.53|                  79.86|                  22.33|                  40.35|                    100|                      78.07|                        79.34|                          4.39|                    5.63|                    78.53|                    80.33|                     1.79|                    68.02|                    90.63|
|Alto           |                8.84|                     9|                   3.68|            41.67|                 6|                12|                 6|                 3|                15|                  1.79|                       1|                     0.98|              54.74|                   1|                   3|                   2|                   1|                   3|                        2.80|                             3|                           1.35|                    48.45|                         2|                         4|                         2|                         1|                         5|                            1.43|                                 1|                                0.5|                        34.60|                             1|                             2|                             1|                             1|                             2|                 6.87|                   5.46|                    4.87|             70.87|               2.21|               7.64|               5.43|               2.11|              16.36|                   0.99|                     0.73|                      1.00|              100.82|                 0.00|                 1.64|                 1.64|                    0|                 3.95|                    78.08|                      77.25|                       10.41|                 13.34|                  70.63|                  85.24|                  14.61|                  55.14|                    100|                      89.14|                        89.89|                          2.85|                    3.20|                    89.07|                    90.58|                     1.51|                    80.00|                    93.08|
# Preparar datos temporales agregados por mes y nivel de éxito
datos_temporales <- datos_clean %>%
  # Convertir fecha_inicio a formato mes
  mutate(mes = floor_date(as_date(fecha_inicio), "month")) %>%
  # Agrupar por mes y nivel de éxito
  group_by(mes, exito_proyecto) %>%
  # Calcular promedios mensuales de métricas clave
  summarise(
    # Métricas del equipo
    tamano_equipo_promedio = mean(tamano_equipo, na.rm = TRUE),
    
    # Métricas de calidad
    errores_promedio = mean(errores_por_kloc, na.rm = TRUE),
    puntuacion_calidad_promedio = mean(puntuacion_calidad, na.rm = TRUE),
    
    # Métricas de satisfacción y duración
    satisfaccion_promedio = mean(satisfaccion_cliente, na.rm = TRUE),
    duracion_meses_promedio = mean(duracion_meses, na.rm = TRUE),
    
    # Contar número de proyectos
    n_proyectos = n()
  ) %>%
  ungroup()

# Definir variables para visualización temporal
metricas_temporales <- c(
  "tamano_equipo_promedio" = "Tamaño del Equipo",
  "errores_promedio" = "Errores por KLOC",
  "puntuacion_calidad_promedio" = "Puntuación de Calidad",
  "satisfaccion_promedio" = "Satisfacción del Cliente",
  "duracion_meses_promedio" = "Duración (Meses)"
)

# Crear gráficos de tendencia temporal
plots_tendencia <- lapply(names(metricas_temporales), function(var) {
  ggplot(datos_temporales, 
         aes_string(x = "mes", y = var, color = "exito_proyecto", group = "exito_proyecto")) +
    # Línea de tendencia
    geom_line(size = 0.8) +
    # Suavizado para ver tendencia general
    geom_smooth(method = "loess", se = FALSE, alpha = 0.3, size = 1) +
    # Etiquetas y títulos
    labs(title = metricas_temporales[var],
         x = "Fecha",
         y = "Valor Promedio",
         color = "Nivel de Éxito") +
    # Tema y formato
    theme_minimal() +
    theme(
      axis.text.x = element_text(angle = 45, hjust = 1),
      legend.position = "bottom",
      plot.title = element_text(hjust = 0.5, face = "bold"),
      panel.grid.minor = element_blank()
    ) +
    # Escala de colores personalizada
    scale_color_manual(values = c("Bajo" = "#FF9999", 
                                 "Medio" = "#66B2FF", 
                                 "Alto" = "#99FF99"))
})

# Mostrar gráficos en una cuadrícula
do.call(grid.arrange, c(plots_tendencia, ncol = 2))

Preparación para siguientes prácticas

# Guardar datos limpios para siguientes prácticas
write.csv(datos_viz, "datos_proyectos_software_limpio.csv", row.names = FALSE)

Los datos limpios generados en esta práctica servirán como base para: - Práctica 2: Implementación de modelos de árboles de decisión para clasificación - Práctica 3: Análisis de clustering para identificar patrones - Práctica 4: Desarrollo de una aplicación Shiny para predicciones