Tema 04 - Análisis Exploratorio de Datos (AED)

Proceso para conocer y entender nuestros datos

Pedro Albarrán

Dpto. de Fundamentos del Análisis Económico. Universidad de Alicante

Alberto Pérez

Introducción al AED

De los datos en bruto a la información

  • El AED es una fase inicial importante, con dos objetivos:

    1. Conocer nuestros datos e identificar problemas → Preprocesamiento

      • qué variables, tipo de información, calidad (información faltante, inconsistencias, problemas en combinación de datos)
    2. Análisis descriptivo: identificar patrones y encontrar escenarios de análisis

  • NO hay una “receta”: el proceso es diferente con distintos datos o con los mismos datos para diferentes objetivos

    • Es un proceso iterativo para descubrir información

Caso de Estudio: PYMES Europeas

  • Una consultora analiza 500 PYMES europeas fundadas entre 1980-2020 para:

    • Evaluar salud financiera y solvencia
    • Identificar patrones de productividad
    • Analizar riesgo crediticio
    • Ofrecer recomendaciones de inversión
  • Fuente de datos: pymes_europa.csv

  • Objetivo del AED:

    • Limpiar y preprocesar los datos
    • Entender los datos (distribuciones y relaciones entre variables)
    • Descubrir patrones (riesgo de las empresa, diferencias por sector y país)

Primera Aproximación

Contexto y reconomicimiento de los datos

  • Contexto: conocimiento previo de los datos (fuente, cómo están almacenados, etc.)

  • Cargar los datos

library(rio)
pymes <- import("data/pymes_europa.csv")
  • Reconocimiento inicial de las características: ¿todo como esperamos?

    • número de observaciones y de variables
    • tipo de cada variable
    • visualizar los datos
  • Consultar el “diccionario” de datos

    • Descripción de cada variable y unidades de medida
    • Tipo de variable esperado
diccionario <- read_csv("data/diccionario_pymes.csv")

Identificar Problemas de Calidad en los Datos

  1. Verificar que las variables la información y el tipo adecuado

    • Algunas variables deberían ser numéricas:
pymes <- pymes |>
  mutate( anio_fundacion = as.numeric(anio_fundacion),
          empleados = as.numeric(empleados),
          liquidez_ratio = as.numeric(liquidez_ratio)  )
  1. Detectar inconsistencias en texto, fechas, unidades, etc.

    • Ej.: en sector, “Tecnología”, “tecnologia”, “TECNOLOGÍA” son la misma categoría

    • este tipo de problemas se puede descubrir más adelante.

# Homogeneizar texto
pymes <- pymes |> 
  mutate( sector = str_to_lower(sector),
          sector = str_replace_all(sector, "tecnologia", "tecnología"),
          sector = str_replace_all(sector, "farmaceutico", "farmacéutico"),
          sector = str_replace_all(sector, "energia", "energía") )

Identificar Problemas (cont.)

  1. Las variables con información categórica deben ser factores
pymes <- pymes |>
  mutate(across(c(sector, pais, tipo_propiedad, tamano_ciudad), 
                ~parse_factor(.x))) |>
  mutate(rating_credito = factor(rating_credito, 
                           levels = c("AAA", "AA", "A", 
                                      "BBB", "BB", "B", 
                                      "CCC", "CC", "C", "D"),
                           ordered = TRUE)
  )
  1. Identificar Valores Faltantes (NAs)
pymes |> summary()
  • Podríamos decidir borrar o reemplazar los NAs, pero se suele preferir decidir al modelizar

Identificar Problemas (y 3)

  1. Detectar y eliminar filas duplicadas
sum(duplicated(pymes))
pymes <- pymes |> distinct()
  1. Variables que contienen información redundante

    • Ej., activos_total y total_recursos son pasivos + patrimonio_neto (igualdad contable)
pymes <- pymes |> select(-total_recursos)
  1. Renombrar variables (para mayor claridad), generar nuevas

  2. ¿Mantenemos solo algunas variables u observaciones?

  3. Otras…

  • NO ES UNA RECETA: más adelante podemos volver atrás, para rehacer o tomar decisiones

Análisis de Variación (“univariante”)

Patrones de variación en los datos

  • Queremos entender cómo cambian los valores de una variable entre distintas observaciones (p. e., ventas de diferentes empresas), es decir, su distribución

    • diferentes técnicas según el tipo de variable (numérica o categórica).
  • Aspectos a observar en la distribución

    • Inconsistencias: categorías erróneas (“unknown”), valores fuera de rango

    • Concentración de valores: ceros, números redondos o repeticiones excesivas → ¿errores o patrones reales?

    • Categorías: ¿tienen sentido? ¿agrupar de manera diferente? ¿reagrupar si hay pocas observaciones?

    • Continuas: dispersión o asimetría (usar log?); ¿discretizar (ej. grupos de edad)?

    • Valores inusuales (“atípicos” o “outliers”): no encajan en el patrón general

      • ¿cambian los resultados del análisis sin ellos? ¿Qué los ha causado?

Variables Categóricas

  • Describimos la distribución con frecuencias y proporciones: con summary(), table(), mode() o con summarize(), count()
table(pymes$sector)

pymes |> count(sector, sort = TRUE) |> mutate(prop = n / sum(n))
  • Este análisis puede detectar situaciones donde queremos agrupar categorías:

    • dos clases similares
    • clases con pocas observaciones (análisis más difícil: visualizaciones desequilibradas, resultados poco confiables)
library(forcats)
pymes <- pymes |>
  mutate(sector_agrupado = fct_lump_min(sector, min = 5, 
                                  other_level = "Otros"), 
         sector_agrupado = fct_collapse(sector, 
                                  Grupo1 = c("manufactura", "textil"),
                                  Grupo2 = c("servicios", "comercio")))

Variables Categóricas: Visualización

  • Para distribuciones discretas, la mejor visualización de la distribución de los datos es un histograma.
# frecuencias absolutas
ggplot(data = pymes) + geom_bar(aes(x = tipo_propiedad))  
# frecuencias relativas (proporciones)
ggplot(data = pymes)+ geom_bar(aes(x = tipo_propiedad, 
                                   y = after_stat(prop), group = 1))

# Variantes
pymes |> count(tipo_propiedad) |> ggplot() + 
  geom_bar(aes(x = tipo_propiedad, y = n), stat = "identity")

ggplot(data = pymes) + geom_bar(aes(x = tipo_propiedad)) +
  theme(axis.text.x = element_text(angle = 90))

# barra vertical
ggplot(data = pymes) + geom_bar(aes(x = "", fill = tipo_propiedad))
  • Observación: la mayor parte de las PYMEs son empresas familiares o S.L.

Variables Cuantitativas

  • Estadísticas descriptivas básicas con summary()
summary(pymes)
  • Información adicional con funciones para estadísticos (rango, varianza, cuartiles, asimetría)
summary(pymes$ingresos)
pymes |>
  summarise(
    media = mean(ingresos, na.rm = TRUE),
    mediana = median(ingresos, na.rm = TRUE),
    sd = sd(ingresos, na.rm = TRUE),
    min = min(ingresos, na.rm = TRUE),
    max = max(ingresos, na.rm = TRUE),
    q25 = quantile(ingresos, 0.25, na.rm = TRUE),
    q75 = quantile(ingresos, 0.75, na.rm = TRUE),
    NAs = sum(is.na(ingresos)))
  • Interés particular en amplia variabilidad, distribución asimétrica

Variables Cuantitativas: Visualización

  • Para distribuciones numéricas, la visualización de la distribución de los datos puede realizarse con un histograma, con la densidad o ambos.
ggplot(pymes) + geom_histogram(aes(x = empleados), bins = 30)

ggplot(pymes, aes(x=empleados)) + 
  geom_histogram(aes(y=after_stat(density)), bins = 30) + 
  geom_density()
  • Recordar: usar varios anchos de intervalo. Esto es discretizar la variable continua de formas distintas

  • Si observamos una distribución muy asimétrica, considerar escala logarítmica

ggplot(pymes, aes(x = ingresos)) + geom_histogram(bins = 30) 
ggplot(pymes, aes(x = ingresos)) + geom_histogram(bins = 30) + 
  scale_x_log10()

Variables Cuantitativas: Visualización con Boxplots

  • Los gráficos de caja pueden ser útiles sobre la dispersión, identificar outliers y comparar distribuciones
ggplot(pymes, aes(y = roe)) +
  geom_boxplot()

Herramientas Automáticas

  • datasummary_skim(): vista rápida de todas las variables (o algunas seleccionada), distinguiendo automáticamente por tipo

    • útil para uso personal, no necesariamente para incluir en informe final
library(modelsummary)
pymes |> datasummary_skim()
  • DataExplorer
library(DataExplorer)
plot_bar(pymes)        # para TODAS las variables categóricas
plot_histogram(pymes)  # para TODAS las variables numéricas

create_report(pymes)
  • dlookr ofrece heramientas para diagnóstico y exploración de datos

Análisis de Covariación (“multivariante”)

Análisis de Covariación

  • La covariación describe relaciones entre variables: tendencia a que los valores de una variable dependan de la otra

  • Estudiamos la distribución condicional de una variable \(\small{Y}\) dados los valores de otra \(\small{X}\)

    • Si \(\small{\Pr(Y|X=x_1) = \Pr(Y|X=x_0) = \Pr(Y) \Rightarrow}\) \(\small{Y}\) NO depende de \(\small{X}\)

      • p.e., el valor esperado de \(\small{Y}\) será el mismo para distintos valores de \(\small{X}\)
    • Si la probabilidad condicional de que \(\small{Y}\) tome valores altos (o bajos) depende de lo que sabemos de \(\small{X}\), se puede predecir (su valor esperado) a partir del valor de \(\small{X}\)

  • Punto de partida para formular modelos que explican patrones complejos de los datos

  • La forma de visualizar la posible existencia de relaciones depende del tipo de variables

Una variable numérica y una categórica

  • ¿Es diferente la distribución de Y (continua) por categorías de X?

    • Cuidado: podemos necesitar escala logarítmica

    • También ajustes como reordenar las categorías de un factor (forcats), rotar los ejes, etc.

  • Podemos usar histogramas o densidades, en un mismo gráfico o con facetas

g0 <- ggplot(pymes, aes(x = ingresos)) +  scale_x_log10()
g0 + geom_density(aes(color = tamano_ciudad))
g0 + geom_density() + facet_wrap(~sector)
  • Con muchos grupos o uno muy pequeño, es difícil notar diferencias
pymes <- pymes |> mutate(rating = fct_collapse(rating_credito, 
                        "Rating Alto" = c("AAA", "AA", "A", "BBB"),
                        "Rating Bajo" = c("BB", "B", "CCC", "CC", "C", "D")))
ggplot(pymes, aes(x = ingresos, color = rating)) +     # color = rating_credito
            geom_density() + scale_x_log10()
  • También con gráficos de caja (menos información pero más fácil de comparar)

    • ¿cómo se vería este gráfico si no hubiesemos homogeneizado sector?
ggplot(pymes, aes(x = sector, y = ingresos, fill = sector)) +
  geom_boxplot() + scale_y_log10()

Una variable numérica y una categórica (cont.)

  • También podemos calcular estadísticos concretos de la distribución de Y para distintos valores de X
pymes |> group_by(sector) |>
  summarise(media = mean(ingresos, na.rm = TRUE),
            mediana = median(ingresos, na.rm = TRUE),
            n = n(),
            sd = sd(ingresos, na.rm = TRUE))
pymes |> group_by(rating) |>
  summarise(media = mean(ingresos, na.rm = TRUE),
            mediana = median(ingresos, na.rm = TRUE),
            n = n(),
            sd = sd(ingresos, na.rm = TRUE))
  • NOTA: una regresión simple equivale a calcular la media de la variable continua por grupos

\[ \scriptsize E[Y|X]=\beta_0+\beta_1 X \Rightarrow \begin{cases} E[Y|X=0] &=\beta_0 \\ E[Y|X=1]&=\beta_0+\beta_1 \end{cases} \]

lm(data = pymes, ingresos ~ sector) |> summary()
lm(data = pymes, ingresos ~ rating) |> summary()
  • ¿Y mediante la correlación? NO tiene sentido cuando una variable es categórica
pymes |> select(ingresos, rating) |> cor()

Dos variables categóricas

  • Partimos de la distribución conjunta de frecuencias absolutas
pymes |> count(sector, tipo_propiedad) |>
  pivot_wider(names_from = tipo_propiedad, values_from = n)

pymes |> ggplot(aes(x=tipo_propiedad)) + geom_bar(aes(fill=sector), 
                                                  position="dodge")
  • Pero es más informativo la tabla o visualización de la distribución condicional de frecuencias relativas de una variable dada la otra

    • ¿Hay la misma proporción de los tipos de propiedad en distintos sectores?
datos <- pymes |> count(sector, tipo_propiedad) |> 
  group_by(sector) |> mutate(prop= n/sum(n)) |> select(-n) 

datos |> pivot_wider(names_from = sector, values_from = prop) 
datos |> ggplot() + geom_bar(aes(x=sector, y=prop, fill = tipo_propiedad), 
                      stat = "identity")

ggplot(pymes, aes(x = sector, fill = tipo_propiedad)) +
  geom_bar(position = "fill") +
  scale_y_continuous(labels = scales::percent)

Dos variables numéricas

  • La forma obvia de visualizar relaciones entre variables continuas es un gráfico de dispersión; añadir smoothers ayuda a apreciar un patrón en los puntos
ggplot(pymes, aes(x = activos_total, y = ingresos)) +
  geom_point() + geom_smooth(method = "lm", se = TRUE) +
  scale_x_log10() +  scale_y_log10() 
  • Con GGally obtenemos una primera visión de conjunto

    • PERO la automatización no permite ajustes (ej., escala logarítmica)
library(GGally)
#pymes |> select(where(is.numeric)) |> ggpairs()
pymes |> select(empleados:ingresos) |> ggpairs()
  • Otra posibilidad: discretizar una variable continua y usar las técnicas anteriores
pymes |> mutate(empleados_group = cut(empleados, 
                                      breaks=seq(0, 250, by=25))) |>
  ggplot() + geom_boxplot(aes(y = ingresos, x = empleados_group)) + 
             scale_y_log10()

Dos variables numéricas: correlación

  • Podemos estimar modelos de regresión con dos variables continuas
summary(lm(data = pymes, ingresos ~ activos_total) )
  • Y también correlaciones para dos (o múltiples) variables
pymes |>
  select(activos_total, ingresos, empleados, 
         beneficio_neto, roe, roa) |>
  cor(use = "complete.obs")
  • O visualizar las correlaciones
datos <- pymes |>
  select(activos_total, pasivos, patrimonio_neto, ingresos, 
         ebitda, beneficio_neto, liquidez_ratio, roe, roa,
         deuda_patrimonio, empleados)

datos |> correlate() |> plot()

library(corrplot)
datos |> cor() |> corrplot()
datos |> cor() |> corrplot.mixed()

Transformación: Motivación desde el AED

Crear Variables Derivadas

  1. Usar logaritmos para variables con distribución asimétrica
  1. Agrupación por tamaño: la distribución de empleados tiene un rango muy amplio
pymes |> ggplot() + geom_histogram(aes(x = empleados))
pymes <- pymes |>
  mutate(tamano_empresa = cut(empleados, breaks = c(0, 10, 50, 250, Inf),
                 labels = c("Micro", "Pequeña", "Mediana", "Grande"),
                 include.lowest = TRUE) )
  • Disyuntiva: Discretizar simplifica comunicación (Pequeña vs. Mediana) pero pierde información (tratamo igual a 11 y 49 empleados)
  1. Agrupar categorías de rating financiero: el análisis univariante mostraba algunas categorías con pocas observaciones y otras con comportamiento similar
  • Agrupar cuando:

    1. El análisis previo mostró que es necesario

    2. Tiene sentido de negocio

    3. Las categorías agrupadas tienen comportamiento similar

    4. Mejora la robustez sin perder información crítica

Herramientas de AED Automatizado y uso de IA

La Promesa de la Automatización

  • El AED manualmente consume tiempo. Se pueden automatizar partes del proceso, pero con precaución

  • Ventajas de herramientas automatizadas: Velocidad, Exahaustividad, Primera Exploración

  • Limitaciones críticas:

    • Ruido: muchos gráficos irrelantes

    • No entienden contexto de negocio

    • Interpretación superficial -> No sugieren acciones específicas

  • Herramientas de AED Automatizado

    • Herramientas interactivas: GWalkR, explore, Radiant (también online)

    • Informes completos automatizados con DataExplorer, dlookr, smartEDA, DataMaid

IA Generativa para análisis de datos

  • El usuario debe introducir un prompt efectivo (no solo “analiza”)
Analiza pymes_europa.csv. Variables clave: ingresos, roe, sector, país. 
Contexto: PYMES europeas 2020-2024. ROE típico: 8-12%. 
Pregunta: ¿Qué características tienen empresas con ROE > 15%?
Dame: limpieza, descriptivos, visualizaciones, análisis por sector
  • Fortalezas

    • Genera código estándar rápidamente y lo documenta

    • Sugiere análisis adicionales

    • Explica conceptos estadísticos

  • Limitaciones críticas:

    1. No conoce tu contexto de negocio

    2. Análisis superficial y sin identificar patrones específicos del dominio

    3. Riesgo de errores

      • Puede “inventar” patrones inexistentes
      • Confunde correlación con causalidad
      • Interpretaciones estadísticamente incorrectas
    4. Limitaciones técnicas: datos < 100-200MB, resultados dependen del prompt

Filosofía: Automatización Inteligente

  1. Exploración rápida (automática)

  2. Identificar áreas de interés

    • ¿Qué variables tienen problemas?
    • ¿Qué relaciones parecen interesantes?
    • ¿Qué no tiene sentido de negocio?
  3. Análisis manual profundo (lo que hicimos)

    • Limpieza contextualizada
    • Visualizaciones específicas
    • Interpretación experta
  1. SIEMPRE validación crítica de resultados de IA y documentar

    • Verifica el código (piensa y re-pregunta)

    • Valida las decisiones e interpretaciones (piensa y re-pregunta)

    • Cuestiona las recomendaciones (piensa y re-pregunta)

Advertencias Importantes

NO hagas esto:

❌ Usar reportes automatizados como análisis final

❌ Confiar ciegamente en interpretaciones de IA

❌ Incluir todas las visualizaciones automáticas

❌ Olvidar validar hallazgos automáticos

Desarrolla criterio para saber cuándo confiar en automatización

SÍ haz esto:

✓ Usar automatización como punto de partida

✓ Validar críticamente todo output automático

✓ Combinar velocidad de herramientas con expertise humano

✓ Mantener el contexto de negocio en el centro

Regla de oro: La automatización acelera, el expertise guía. Nunca al revés.