Estadística descriptiva sin sorpresas

r
estadistica
Por qué summary() se queda corto. Tendencia central (media, mediana, moda), dispersión (varianza, SD, IQR), cuartiles y herramientas mejores como skimr.

Por qué summary() se queda corto

R base tiene una función mágica: summary(). Le pasas un data frame y devuelve un mini-resumen por columna:

summary(mtcars)
#>       mpg             cyl             disp             hp
#>  Min.   :10.40   Min.   :4.000   Min.   : 71.1   Min.   : 52.0
#>  1st Qu.:15.43   1st Qu.:4.000   1st Qu.:120.8   1st Qu.: 96.5
#>  Median :19.20   Median :6.000   Median :196.3   Median :123.0
#>  Mean   :20.09   Mean   :6.188   Mean   :230.7   Mean   :146.7
#>  3rd Qu.:22.80   3rd Qu.:8.000   3rd Qu.:326.0   3rd Qu.:180.0
#>  Max.   :33.90   Max.   :8.000   Max.   :400.0   Max.   :335.0

Útil. Pero deja preguntas sin responder:

  • ¿Cuántos NA tiene cada columna?
  • ¿Cuál es la desviación estándar? ¿La asimetría?
  • ¿Cómo se ve la distribución de un vistazo?
  • ¿Qué pasa con variables categóricas? (summary() solo cuenta frecuencias si la columna es factor.)

Para análisis serio, hay que ir más allá. Veamos cómo.

Tendencia central: tres medidas, tres usos

Hay tres formas básicas de resumir “el centro” de una distribución:

  • Media (mean(x, na.rm = TRUE)), promedio aritmético. Sensible a outliers.
  • Mediana (median(x, na.rm = TRUE)), valor que divide a la mitad. Robusta a outliers.
  • Moda: valor más frecuente. R base no tiene función. Hay que hacerlo a mano.

¿Cuándo cada una?

Tipo de datos Medida central
Numérico simétrico sin outliers Media
Numérico con cola larga (precios, ingresos, tiempos) Mediana
Ordinal (rating 1-5) Mediana
Categórico nominal Moda

Ejemplo concreto: salarios de una empresa. La media puede ser 50.000 € pero la mediana 35.000 €. Si el CEO gana 5 M, la media miente sobre lo que cobra “el empleado típico”. Reporta mediana.

Una moda casera en R:

moda <- function(x) {
  ux <- unique(na.omit(x))
  ux[which.max(tabulate(match(x, ux)))]
}

moda(mtcars$cyl)   # 8

Dispersión: qué tan “extendido” está

Tres medidas habituales:

  • Varianza (var(x)), promedio de las desviaciones al cuadrado.
  • Desviación estándar (sd(x)), raíz cuadrada de la varianza, en unidades originales.
  • IQR (IQR(x)), rango intercuartílico: Q3 − Q1. Robusto a outliers.

La SD es la métrica por defecto en datos aproximadamente simétricos. El IQR es la métrica por defecto en distribuciones asimétricas o con outliers, viaja con la mediana.

mean(mtcars$mpg)     # 20.09
sd(mtcars$mpg)       # 6.03
median(mtcars$mpg)   # 19.2
IQR(mtcars$mpg)      # 7.375

Pareja sana: (mediana, IQR) o (media, SD). Mezclar media + IQR es raro y confunde.

Cuartiles y percentiles

quantile(mtcars$mpg)
#>     0%    25%    50%    75%   100%
#>  10.40  15.43  19.20  22.80  33.90

Cuartiles por defecto (0 %, 25 %, 50 %, 75 %, 100 %). Para otros percentiles:

quantile(mtcars$mpg, probs = c(0.1, 0.5, 0.9))
#>  10%   50%   90%
#> 14.34 19.20 30.09

Útil para reportes que dicen “el 90 % de las observaciones está por debajo de X”.

skimr: el resumen completo

install.packages("skimr")
library(skimr)

skim(mtcars)

Salida (resumida):

── Variable type: numeric ─────────────────────────────────
   skim_variable n_missing complete_rate    mean      sd    p0    p25    p50    p75   p100 hist
 1 mpg                   0             1   20.1     6.03   10.4  15.4   19.2   22.8   33.9 ▃▇▅▁▂
 2 cyl                   0             1    6.19    1.79    4     4      6      8      8   ▆▁▃▁▇
 3 disp                  0             1  231.    124.     71.1 121.   196.   326.   400   ▇▃▃▃▆

Información que summary() no daba:

  • n_missing: cuántos NA por columna.
  • complete_rate: proporción no-NA.
  • sd: desviación estándar.
  • hist: sparkline ASCII de la distribución. Detectas asimetría, bimodalidad y outliers de un vistazo.

skim() debería ser tu primer comando al recibir un dataset nuevo. Sustituye al reflejo de summary().

Resumen por grupos con dplyr

Cuando quieres ver descriptivas por grupo (lo más común en análisis):

library(dplyr)

mtcars |>
  group_by(cyl) |>
  summarise(
    n           = n(),
    mpg_media   = mean(mpg, na.rm = TRUE),
    mpg_sd      = sd(mpg, na.rm = TRUE),
    mpg_mediana = median(mpg, na.rm = TRUE),
    mpg_iqr     = IQR(mpg, na.rm = TRUE),
    .groups     = "drop"
  )

Es el patrón split-apply-combine del tutorial de group_by aplicado a estadística descriptiva.

Trampas habituales

  • Reportar media en datos con outliers o sesgo. La media de ingresos es casi siempre inflada por unos pocos altos. La mediana suele ser la respuesta correcta a “¿cuánto gana el empleado típico?”.
  • Olvidar na.rm = TRUE. Si tu columna tiene un solo NA, mean(x) devuelve NA. Todas las funciones (mean, sd, median, etc.) aceptan na.rm = TRUE. Decisión consciente, no automática.
  • Reportar SD con mediana. La SD asume simetría. La mediana se usa porque no la hay. Reportar ambas juntas confunde. Usa mediana + IQR o media + SD, no las mezcles.
  • summary() sin pensar. Útil para inspección rápida, pero no es un análisis descriptivo completo. Acostúmbrate a skim() para datasets nuevos.

En la siguiente entrega

Has aprendido a resumir datos. La siguiente pieza es entender qué asume un test estadístico sobre la distribución de tus datos. La mayoría de tests clásicos asumen normalidad. Aprender cuándo se cumple y qué hacer cuando no es el siguiente paso. Lo siguiente.