library(readxl)
library(dplyr)

Transformación y limpieza de datos

En proyectos reales de análisis de datos, es poco común trabajar con bases perfectamente limpias.

Con frecuencia es necesario revisar las variables, identificar inconsistencias y realizar pequeñas transformaciones antes de comenzar el análisis.

En esta sección exploraremos algunas herramientas de R que permiten realizar este tipo de tareas de forma eficiente.

Importamos el archivo power_dataset.xlsx:

df <- read_excel("power_dataset.xlsx")

head(df)
## # A tibble: 6 × 8
##   Date       Time     `Global active power` `Global reactive power` Voltage
##   <chr>      <chr>                    <dbl>                   <dbl>   <dbl>
## 1 18/12/2006 08:49:38                  1.99                   0.431    236.
## 2 18/12/2006 16:40:50                  2.02                   0.148    236.
## 3 17/12/2006 17:37:45                  2.65                   0.264    237.
## 4 17/12/2006 08:43:04                  2.28                   0.393    234.
## 5 18/12/2006 16:28:13                  3.71                   0.235    235.
## 6 20/12/2006 07:50:29                  4.39                   0.2      237.
## # ℹ 3 more variables: `Global intensity` <dbl>, `Appliance Category` <chr>,
## #   `Energy Consumption` <dbl>


El primer objetivo es mostrar un resumen estadístico que incluya promedio y desviación estándar para la variable de consumo de energía, clasificado por categoría de electrodoméstico.

# Incorrecto pero estamos tentados por las backticks
df %>% 
  group_by(df$`Appliance Category`) %>% 
  summarise(
    Promedio = round(mean(df$`Energy Consumption`),2),
    Desviación = round(sd(df$`Energy Consumption`),2))
## # A tibble: 6 × 3
##   `df$\`Appliance Category\`` Promedio Desviación
##   <chr>                          <dbl>      <dbl>
## 1 Kitchen Apliances               20.8       17.3
## 2 Kitchen Appliances              20.8       17.3
## 3 Laundry & Refrigeration         20.8       17.3
## 4 Laundry and Refrigeration       20.8       17.3
## 5 Water Heating & HVAC            20.8       17.3
## 6 Water Heating & HVCA            20.8       17.3
# Quitamos la mención de df dentro del promedio
df %>% 
  group_by(df$`Appliance Category`) %>% 
  summarise(
    Promedio = round(mean(`Energy Consumption`),2),
    Desviación = round(sd(`Energy Consumption`),2))
## # A tibble: 6 × 3
##   `df$\`Appliance Category\`` Promedio Desviación
##   <chr>                          <dbl>      <dbl>
## 1 Kitchen Apliances               17.4       11.7
## 2 Kitchen Appliances              20.9       15.7
## 3 Laundry & Refrigeration         20.9       20.2
## 4 Laundry and Refrigeration       20.6       16.5
## 5 Water Heating & HVAC            20.8       18.4
## 6 Water Heating & HVCA            20.7       15.0
# Observamos que sigue habiendo problemas...
# ¿Cuántas categorías distintas de electodomésticos hay en la base?
df %>%  
  group_by(`Appliance Category`) %>% # Usamos backticks con AltGr + ] + SPACE
  count(`Appliance Category`)
## # A tibble: 6 × 2
## # Groups:   Appliance Category [6]
##   `Appliance Category`          n
##   <chr>                     <int>
## 1 Kitchen Apliances            20
## 2 Kitchen Appliances         1654
## 3 Laundry & Refrigeration    1059
## 4 Laundry and Refrigeration   301
## 5 Water Heating & HVAC       1083
## 6 Water Heating & HVCA        903
# Alternativamente
table(df$`Appliance Category`)
## 
##         Kitchen Apliances        Kitchen Appliances   Laundry & Refrigeration 
##                        20                      1654                      1059 
## Laundry and Refrigeration      Water Heating & HVAC      Water Heating & HVCA 
##                       301                      1083                       903


# Cambio en registro: 
# Kitchen Apliances -> Kitchen Appliances

# Método 'manual'

indice_kitchen <- which(df$`Appliance Category` == "Kitchen Apliances")
indice_kitchen
##  [1]  739 1066 1519 1974 2088 2481 2557 2867 2929 2932 3403 3410 3751 3785 3842
## [16] 4126 4136 4145 4285 4365
length(indice_kitchen)
## [1] 20
df$`Appliance Category`[indice_kitchen]
##  [1] "Kitchen Apliances" "Kitchen Apliances" "Kitchen Apliances"
##  [4] "Kitchen Apliances" "Kitchen Apliances" "Kitchen Apliances"
##  [7] "Kitchen Apliances" "Kitchen Apliances" "Kitchen Apliances"
## [10] "Kitchen Apliances" "Kitchen Apliances" "Kitchen Apliances"
## [13] "Kitchen Apliances" "Kitchen Apliances" "Kitchen Apliances"
## [16] "Kitchen Apliances" "Kitchen Apliances" "Kitchen Apliances"
## [19] "Kitchen Apliances" "Kitchen Apliances"
df <- df %>% 
  mutate(`Appliance Category` = {
    x <- `Appliance Category`
    x[x == "Kitchen Apliances"] <- "Kitchen Appliances"
    x
  })

which(df$`Appliance Category` == "Kitchen Apliances")
## integer(0)
table(df$`Appliance Category`)
## 
##        Kitchen Appliances   Laundry & Refrigeration Laundry and Refrigeration 
##                      1674                      1059                       301 
##      Water Heating & HVAC      Water Heating & HVCA 
##                      1083                       903


# Cambio en registro: 
# Laundry and Refrigeration -> Laundry & Refrigeration 

# Método replace(vector, condicion, valor_nuevo)

df <- df %>% 
  mutate(`Appliance Category` = replace( 
    `Appliance Category`,
    `Appliance Category` == "Laundry and Refrigeration",
    "Laundry & Refrigeration"
  ))

which(df$`Appliance Category` == "Laundry and Refrigeration")
## integer(0)
table(df$`Appliance Category`)
## 
##      Kitchen Appliances Laundry & Refrigeration    Water Heating & HVAC 
##                    1674                    1360                    1083 
##    Water Heating & HVCA 
##                     903


# Cambio en registro: 
# Water Heating & HVCA -> Water Heating & HVAC 
# [Heating, Ventilation, and Air Conditioning]

# Método recode(variable, valor_viejo = valor_nuevo)

df <- df %>% 
  mutate(`Appliance Category` = recode( 
    `Appliance Category`,
    "Water Heating & HVCA" = "Water Heating & HVAC"
  ))

which(df$`Appliance Category` == "Water Heating & HVCA")
## integer(0)
table(df$`Appliance Category`)
## 
##      Kitchen Appliances Laundry & Refrigeration    Water Heating & HVAC 
##                    1674                    1360                    1986


resumen <- df %>% 
  group_by(df$`Appliance Category`) %>% 
  summarise(
    Promedio = round(mean(`Energy Consumption`),2),
    Desviación = round(sd(`Energy Consumption`),2)) %>% 
  print()
## # A tibble: 3 × 3
##   `df$\`Appliance Category\`` Promedio Desviación
##   <chr>                          <dbl>      <dbl>
## 1 Kitchen Appliances              20.9       15.7
## 2 Laundry & Refrigeration         20.9       19.5
## 3 Water Heating & HVAC            20.7       16.9


Una vez que comprendemos la estructura de la base de datos y corregimos inconsistencias en las categorías, nos enfocamos en crear una nueva variable llamada “Data Quality Flag” que clasifique cada registro según la calidad de los datos:

  • Si el consumo de energía (Energy Consumption) es mayor a 120, clasifica el registro como “Outlier”.

  • Si el voltaje (Voltage) es un valor faltante (NA), clasifica el registro como “Missing Voltage”.

  • Si la potencia activa global (Global active power) es menor que 0, clasifica el registro como “Negative Power”.

  • Si ninguna de las condiciones anteriores se cumple, clasifica el registro como “OK”.

ifelse(condición, valor_si_TRUE, valor_si_FALSE)

# ifelse
df$`Data Quality Flag` <- ifelse(
  df$`Energy Consumption` > 120,
  "Outlier",
  "OK"
)

head(df)
## # A tibble: 6 × 9
##   Date       Time     `Global active power` `Global reactive power` Voltage
##   <chr>      <chr>                    <dbl>                   <dbl>   <dbl>
## 1 18/12/2006 08:49:38                  1.99                   0.431    236.
## 2 18/12/2006 16:40:50                  2.02                   0.148    236.
## 3 17/12/2006 17:37:45                  2.65                   0.264    237.
## 4 17/12/2006 08:43:04                  2.28                   0.393    234.
## 5 18/12/2006 16:28:13                  3.71                   0.235    235.
## 6 20/12/2006 07:50:29                  4.39                   0.2      237.
## # ℹ 4 more variables: `Global intensity` <dbl>, `Appliance Category` <chr>,
## #   `Energy Consumption` <dbl>, `Data Quality Flag` <chr>
table(df$`Data Quality Flag`)
## 
##      OK Outlier 
##    5000      20


# ifelse anidado

df$`Data Quality Flag` <- ifelse(
  df$`Energy Consumption` > 120,
  "Outlier",
  ifelse(
    is.na(df$Voltage),
    "Missing Voltage",
    ifelse(
      df$`Global active power` < 0,
      "Negative Power",
      "OK"
    )
  )
)

# difícil de leer
# muchos paréntesis
# difícil agregar más reglas

table(df$`Data Quality Flag`)
## 
## Missing Voltage  Negative Power              OK         Outlier 
##              20              10            4970              20
# case_when: condición ~ resultado

df <- df %>%
  mutate(
    `Data Quality Flag` = case_when(
      `Energy Consumption` > 120 ~ "Outlier",
      is.na(Voltage) ~ "Missing Voltage",
      `Global active power` < 0 ~ "Negative Power",
      TRUE ~ "OK"
    )
  )


Observamos que existen distintas maneras de implementar reglas condicionales en R.

Aunque ifelse() es útil para casos simples, cuando el número de condiciones aumenta el código puede volverse difícil de leer y mantener.

La función case_when() del paquete dplyr permite expresar estas reglas de una forma más clara y escalable.

A continuación se muestra una comparativa entre los enfoques utilizados:

Método Legibilidad Escalabilidad
ifelse simple Alta Baja
ifelse anidado Media Mala
case_when() Muy alta Muy buena

Histogramas

Hemos trabajado previamente con histogramas para representar conjuntos de datos. Sin embargo, hasta ahora no nos hemos detenido a analizar en detalle sus características ni su interpretación.

En esta sección profundizaremos en el uso del histograma como herramienta para explorar la distribución de los datos.

Para un conjunto de datos continuos, un histograma es esencialmente una estimación gráfica de la densidad correspondientes a la distribución de los datos \(X_1,X_2,…,X_n\).

Las funciones de densidad tienden a tener formas reconocibles en muchos casos. Por lo tanto, una estimación gráfica de una densidad debería proporcionar una buena pista sobre las distribuciones que podrían considerarse como modelos para los datos.

library(ggplot2)   # Creación de gráficos
library(patchwork) # Combinar múltiples gráficos de ggplot2
library(e1071)     # Funciones estadísticas como skewness()
library(moments)   # Cálculo de asimetría y curtosis
# Los histogramas son una herramienta fundamental para explorar la distribución de una variable numérica. Este tipo de gráfico agrupa los valores en intervalos (bins) y muestra cuántas observaciones caen dentro de cada rango. De esta forma, permite visualizar rápidamente la forma de la distribución de los datos, identificando aspectos como la concentración de valores, la dispersión, la presencia de asimetría o posibles valores atípicos.

# Analizar la distribución mediante histogramas es importante porque muchos métodos estadísticos y modelos asumen ciertos comportamientos de los datos, como la normalidad o la simetría de la distribución. Antes de aplicar análisis más avanzados, observar un histograma ayuda a entender si los datos siguen un patrón esperado o si requieren transformaciones o tratamientos adicionales.

# Además, los histogramas son especialmente útiles en contextos de simulación, como en el método de Monte Carlo method. En este tipo de métodos se generan miles o millones de valores aleatorios para aproximar el comportamiento de un sistema o estimar probabilidades. Los histogramas permiten visualizar la distribución de los resultados simulados y verificar si la simulación reproduce el comportamiento teórico esperado, por ejemplo, si los resultados tienden a aproximarse a una distribución normal o a la forma prevista por el modelo.

Generamos valores aleatorios que siguen una distribución \(Gamma(\alpha = 2, \beta=1)\)


Para construir un histograma, se divide el rango de valores cubiertos por los datos en k intervalos disjuntos adyacentes \([b_0,b_1),[b_1,b_2 ),…, [b_{k-1},b_k)\). Todos los intervalos deben tener el mismo ancho, denotado como \(∆b=b_j-b_{j-1}\), lo que podría requerir descartar algunos valores \(X_i\) extremadamente grandes o pequeños para evitar que el histograma tenga un aspecto poco manejable.


Reglas para calcular el número de bins

Se han sugerido varias reglas prácticas para elegir el número de intervalos \(k\) por ejemplo:


Al mismo tiempo, se recomienda probar varios valores diferentes de \(∆b\) y elegir el que produzca un histograma “suave”. Esto es claramente una cuestión subjetiva y representa el principal problema al usar histogramas.

Histogramas con base a la longitud del intervalo

Si \(∆b\) es demasiado pequeño, el histograma tendrá una forma irregular debido a que las varianzas de los \(h_j\) serán grandes. Si \(∆b\) es demasiado grande, el histograma tendrá una forma “cuadrada” y se enmascarará la verdadera forma de la densidad subyacente, ya que los datos estarán sobreagrupados.



Asimetría

La asimetría (o sesgo) es una medida que indica qué tan simétrica es la distribución de los datos con respecto a su media.

Formalmente, decimos que una distribución de frecuencias es simétrica si la media \(\bar{x}\) es igual a la mediana \(\bar{M_e}\). Esto significa que, en cualquier otro caso, la distribución es asimétrica.

Cuando \(\bar{M_e} < \bar{x}\) decimos que la distribución es Asimétrica a la derecha, Asimétrica de cola derecha o que tiene Asimetría positiva.

Cuando \(\bar{M_e} > \bar{x}\) decimos que la distribución es Asimétrica a la izquierda, Asimétrica de cola izquierda o que tiene Asimetría Negativa.

Trabajemos con una distribución normal para ejemplificar la asimetría de una distribución.


Trabajamos con una distribución Exponencial para ejemplificar la asimetría a la derecha.


Trabajamos con una distribución Weibull con ciertos parámetros para ejemplificar la asimétrica a la izquierda


El coeficiente de asimetría también conocido como el coeficiente de asimetría de Fisher-Pearson, se basa en el tercer momento central:

\[ \mu_3 = E[(X-\mu)^3] \] Se utiliza \(\sigma^3\) para obtener la asimetría estandarizada.

\[ \frac{\mathbb{E}[(X - \mu)^3]}{\sigma^3} = \mathbb{E} \left[ \left( \frac{X - \mu}{\sigma} \right)^3 \right] = \frac{\mu_3}{\sigma^3} \]

  1. Si \(asimetría = 0\) entonces la distribución es simétrica.
  2. Si \(asimetría > 0\) entonces la distribución es asimétrica a la derecha.
  3. Si \(asimetría < 0\) entonces la distribución es asimétrica a la izquierda.



Curtosis

La curtosis es una medida estadística que se utiliza para evaluar la forma de la distribución de un conjunto de datos y, específicamente, para medir cuán “afilada” o “achatada” es la distribución en comparación con una distribución normal. Indica la concentración de valores alrededor de la media y la presencia de valores extremos en la distribución de datos.

Distribución leptocúrtica: El coeficiente de curtosis es positivo para una distribución leptocúrtica. Presenta un elevado grado de concentración alrededor de los valores centrales de la variable.Las colas de la distribución son más pesadas, lo que significa que los valores extremos son más probables.

Distribución mesocúrtica: Presenta un grado de concentración medio alrededor de los valores centrales de la variable (el mismo que presenta una distribución normal). Los valores extremos o atípicos son menos comunes en este tipo de distribución.La curtosis se aproxima a cero para una distribución mesocúrtica.

Distribución platicúrtica: Presenta un reducido grado de concentración alrededor de los valores centrales de la variable y sus valores están más dispersos. Tienen una forma achatada con un pico más bajo en el centro y colas menos pronunciadas. El coeficiente de curtosis es negativo para una distribución platicúrtica.

El cuarto momento central se usa para medir la curtosis, que refleja si las colas de una distribución son más pesadas o ligeras que una distribución normal.

\[ \mu_4 = E[(X-\mu)^4] \] Se utiliza \(\sigma^4\) para obtener la curtosis estandarizada.

\[ \frac{\mathbb{E}[(X - \mu)^4]}{\sigma^4} = \mathbb{E} \left[ \left( \frac{X - \mu}{\sigma} \right)^4 \right] = \frac{\mu_4}{\sigma^4} \]