# Ejemplo simple: formato wide
datos_wide_ejemplo <- data.table(
estudiante = c("Ana", "Juan", "María"),
matematicas = c(85, 90, 78),
ciencias = c(88, 85, 92),
historia = c(82, 88, 89)
)
print("Formato WIDE (típico de Excel):")
#> [1] "Formato WIDE (típico de Excel):"
print(datos_wide_ejemplo)
#> estudiante matematicas ciencias historia
#> <char> <num> <num> <num>
#> 1: Ana 85 88 82
#> 2: Juan 90 85 88
#> 3: María 78 92 89
# Convertir a formato long
datos_long_ejemplo <- melt(datos_wide_ejemplo,
id.vars = "estudiante",
variable.name = "materia",
value.name = "calificacion")
print("\nFormato LONG (ideal para análisis):")
#> [1] "\nFormato LONG (ideal para análisis):"
print(datos_long_ejemplo)
#> estudiante materia calificacion
#> <char> <fctr> <num>
#> 1: Ana matematicas 85
#> 2: Juan matematicas 90
#> 3: María matematicas 78
#> 4: Ana ciencias 88
#> 5: Juan ciencias 85
#> 6: María ciencias 92
#> 7: Ana historia 82
#> 8: Juan historia 88
#> 9: María historia 898 Remodelación de Datos: melt() y dcast()
8.1 Conceptos Fundamentales: Wide vs Long
Antes de explorar melt() y dcast(), es crucial entender cuándo y por qué transformar estructuras de datos:
- Formato Wide (ancho): Cada variable tiene su propia columna. Fácil de leer pero difícil de analizar estadísticamente.
- Formato Long (largo): Las observaciones se “apilan” en filas. Ideal para análisis estadístico y visualización.
8.2 melt(): De Ancho a Largo
8.2.1 1. melt() Básico
# Transformar datos de ventas trimestrales
ventas_long <- melt(ventas_trimestral_wide,
id.vars = c("producto", "categoria", "precio_unitario"),
variable.name = "periodo_metrica",
value.name = "valor")
print("Datos transformados a formato largo:")
#> [1] "Datos transformados a formato largo:"
print(head(ventas_long, 12))
#> producto categoria precio_unitario periodo_metrica valor
#> <char> <char> <num> <fctr> <num>
#> 1: Laptop Computadoras 1200 Q1_2023_unidades 150
#> 2: Desktop Computadoras 800 Q1_2023_unidades 80
#> 3: Tablet Computadoras 400 Q1_2023_unidades 200
#> 4: Smartphone Móviles 600 Q1_2023_unidades 300
#> 5: Monitor Periféricos 300 Q1_2023_unidades 120
#> ---
#> 8: Impresora Oficina 200 Q1_2023_unidades 60
#> 9: Router Redes 150 Q1_2023_unidades 90
#> 10: Cámara Multimedia 250 Q1_2023_unidades 70
#> 11: Laptop Computadoras 1200 Q1_2023_revenue 180000
#> 12: Desktop Computadoras 800 Q1_2023_revenue 64000
# Ver la estructura completa
cat("Dimensiones originales:", dim(ventas_trimestral_wide), "\n")
#> Dimensiones originales: 10 13
cat("Dimensiones después de melt:", dim(ventas_long), "\n")
#> Dimensiones después de melt: 100 58.2.2 2. Separar Variables Complejas
# Separar periodo y métrica de la variable compuesta
ventas_long_separada <- ventas_long[, `:=`(
periodo = sub("_unidades|_revenue", "", periodo_metrica),
metrica = ifelse(grepl("unidades", periodo_metrica), "unidades", "revenue")
)]
print("Datos con variables separadas:")
#> [1] "Datos con variables separadas:"
print(head(ventas_long_separada, 12))
#> producto categoria precio_unitario periodo_metrica valor periodo
#> <char> <char> <num> <fctr> <num> <char>
#> 1: Laptop Computadoras 1200 Q1_2023_unidades 150 Q1_2023
#> 2: Desktop Computadoras 800 Q1_2023_unidades 80 Q1_2023
#> 3: Tablet Computadoras 400 Q1_2023_unidades 200 Q1_2023
#> 4: Smartphone Móviles 600 Q1_2023_unidades 300 Q1_2023
#> 5: Monitor Periféricos 300 Q1_2023_unidades 120 Q1_2023
#> ---
#> 8: Impresora Oficina 200 Q1_2023_unidades 60 Q1_2023
#> 9: Router Redes 150 Q1_2023_unidades 90 Q1_2023
#> 10: Cámara Multimedia 250 Q1_2023_unidades 70 Q1_2023
#> 11: Laptop Computadoras 1200 Q1_2023_revenue 180000 Q1_2023
#> 12: Desktop Computadoras 800 Q1_2023_revenue 64000 Q1_2023
#> metrica
#> <char>
#> 1: unidades
#> 2: unidades
#> 3: unidades
#> 4: unidades
#> 5: unidades
#> ---
#> 8: unidades
#> 9: unidades
#> 10: unidades
#> 11: revenue
#> 12: revenue
# Limpiar columna temporal
ventas_long_separada[, periodo_metrica := NULL]8.2.3 3. melt() con Patrones
# Melt usando patrones para múltiples tipos de variables
empleados_long <- melt(empleados_metricas,
id.vars = c("empleado_id", "nombre", "departamento", "nivel", "salario_base"),
measure = patterns(
bonus = "^bonus_",
evaluacion = "^evaluacion_",
proyectos = "^proyectos_"
),
variable.name = "trimestre",
value.name = c("bonus", "evaluacion", "proyectos"))
# Limpiar nombres de trimestre
empleados_long[, trimestre := paste0("Q", trimestre)]
print("Empleados con múltiples métricas en formato largo:")
#> [1] "Empleados con múltiples métricas en formato largo:"
print(head(empleados_long, 12))
#> empleado_id nombre departamento nivel salario_base trimestre bonus
#> <char> <char> <char> <char> <num> <char> <num>
#> 1: EMP_001 Empleado_A Ventas Junior 72700 Q1 378
#> 2: EMP_002 Empleado_B Ventas Senior 49400 Q1 896
#> 3: EMP_003 Empleado_C Ventas Lead 65600 Q1 5443
#> 4: EMP_004 Empleado_D Ventas Manager 66400 Q1 6800
#> 5: EMP_005 Empleado_E Ventas Junior 55600 Q1 6379
#> ---
#> 8: EMP_008 Empleado_H IT Manager 48600 Q1 6525
#> 9: EMP_009 Empleado_I IT Junior 74400 Q1 4
#> 10: EMP_010 Empleado_J IT Senior 40400 Q1 169
#> 11: EMP_011 Empleado_K Marketing Lead 75600 Q1 3529
#> 12: EMP_012 Empleado_L Marketing Manager 77900 Q1 2322
#> evaluacion proyectos
#> <num> <int>
#> 1: 3.6 7
#> 2: 4.3 1
#> 3: 3.4 6
#> 4: 4.1 5
#> 5: 3.5 2
#> ---
#> 8: 3.1 2
#> 9: 3.5 1
#> 10: 4.6 3
#> 11: 3.2 1
#> 12: 4.2 18.2.4 4. melt() Avanzado para Sensores
# Melt complejo para datos de sensores múltiples
sensores_long <- melt(sensores_multiples,
id.vars = c("timestamp", "ubicacion"),
variable.name = "sensor_completo",
value.name = "medicion")[, `:=`(
# Extraer tipo de sensor y número
tipo_sensor = sub("_[0-9]+$", "", sensor_completo),
numero_sensor = as.numeric(sub(".*_", "", sensor_completo))
)][, sensor_completo := NULL] # Limpiar columna temporal
print("Sensores en formato largo:")
#> [1] "Sensores en formato largo:"
print(head(sensores_long, 15))
#> timestamp ubicacion medicion tipo_sensor numero_sensor
#> <POSc> <char> <num> <char> <num>
#> 1: 2024-01-01 00:00:00 Planta_Norte 19.9 sensor_temp 1
#> 2: 2024-01-01 06:00:00 Planta_Norte 19.9 sensor_temp 1
#> 3: 2024-01-01 12:00:00 Planta_Norte 24.5 sensor_temp 1
#> 4: 2024-01-01 18:00:00 Planta_Norte 23.4 sensor_temp 1
#> 5: 2024-01-02 00:00:00 Planta_Norte 24.1 sensor_temp 1
#> ---
#> 11: 2024-01-03 12:00:00 Planta_Norte 23.5 sensor_temp 1
#> 12: 2024-01-03 18:00:00 Planta_Norte 23.6 sensor_temp 1
#> 13: 2024-01-04 00:00:00 Planta_Norte 21.1 sensor_temp 1
#> 14: 2024-01-04 06:00:00 Planta_Norte 21.3 sensor_temp 1
#> 15: 2024-01-04 12:00:00 Planta_Norte 20.3 sensor_temp 1
# Estadísticas por tipo de sensor
stats_sensores <- sensores_long[, .(
mediciones = .N,
promedio = round(mean(medicion, na.rm = TRUE), 2),
minimo = round(min(medicion, na.rm = TRUE), 2),
maximo = round(max(medicion, na.rm = TRUE), 2),
desviacion = round(sd(medicion, na.rm = TRUE), 2)
), by = .(tipo_sensor, ubicacion)]
print("\nEstadísticas por tipo de sensor y ubicación:")
#> [1] "\nEstadísticas por tipo de sensor y ubicación:"
print(stats_sensores)
#> tipo_sensor ubicacion mediciones promedio minimo maximo desviacion
#> <char> <char> <int> <num> <num> <num> <num>
#> 1: sensor_temp Planta_Norte 56 21.08 13.5 27.1 3.38
#> 2: sensor_temp Planta_Sur 56 21.15 13.1 26.4 3.31
#> 3: sensor_temp Planta_Centro 56 21.24 14.5 26.9 3.21
#> 4: sensor_humedad Planta_Norte 56 64.10 42.9 79.8 8.65
#> 5: sensor_humedad Planta_Sur 56 64.20 42.6 77.6 10.36
#> 6: sensor_humedad Planta_Centro 56 58.45 40.7 78.6 9.29
#> 7: sensor_presion Planta_Norte 56 1021.35 999.9 1044.3 11.25
#> 8: sensor_presion Planta_Sur 56 1007.93 987.8 1033.3 12.23
#> 9: sensor_presion Planta_Centro 56 1020.05 997.3 1040.6 11.558.3 dcast(): De Largo a Ancho
8.3.1 1. dcast() Básico
# Reconstruir formato ancho desde formato largo
ventas_reconstruida <- dcast(ventas_long_separada,
producto + categoria + precio_unitario ~ periodo + metrica,
value.var = "valor")
print("Datos reconstruidos a formato ancho:")
#> [1] "Datos reconstruidos a formato ancho:"
print(head(ventas_reconstruida))
#> Key: <producto, categoria, precio_unitario>
#> producto categoria precio_unitario Q1_2023_revenue Q1_2023_unidades
#> <char> <char> <num> <num> <num>
#> 1: Cámara Multimedia 250 17500 70
#> 2: Desktop Computadoras 800 64000 80
#> 3: Impresora Oficina 200 12000 60
#> 4: Laptop Computadoras 1200 180000 150
#> 5: Monitor Periféricos 300 36000 120
#> 6: Mouse Periféricos 25 11250 450
#> Q1_2024_revenue Q1_2024_unidades Q2_2023_revenue Q2_2023_unidades
#> <num> <num> <num> <num>
#> 1: 20000 80 18750 75
#> 2: 68000 85 60000 75
#> 3: 15000 75 11000 55
#> 4: 228000 190 216000 180
#> 5: 51000 170 42000 140
#> 6: 13750 550 12000 480
#> Q3_2023_revenue Q3_2023_unidades Q4_2023_revenue Q4_2023_unidades
#> <num> <num> <num> <num>
#> 1: 21250 85 22500 90
#> 2: 72000 90 76000 95
#> 3: 14000 70 16000 80
#> 4: 240000 200 264000 220
#> 5: 48000 160 54000 180
#> 6: 12500 500 15000 600
# Verificar que coincide con datos originales
cat("¿Reconstrucción exitosa?",
nrow(ventas_reconstruida) == nrow(ventas_trimestral_wide) &&
ncol(ventas_reconstruida) >= ncol(ventas_trimestral_wide) - 2, "\n")
#> ¿Reconstrucción exitosa? TRUE8.3.2 2. dcast() con Agregación
# Crear tabla resumen: promedio por producto y año
ventas_resumen_anual <- ventas_long_separada[,
año := ifelse(grepl("2023", periodo), "2023", "2024")
][, .(
valor_promedio = round(mean(valor), 0)
), by = .(producto, categoria, año, metrica)]
# Convertir a formato ancho con agregación
resumen_wide <- dcast(ventas_resumen_anual,
producto + categoria ~ año + metrica,
value.var = "valor_promedio")
print("Resumen anual en formato ancho:")
#> [1] "Resumen anual en formato ancho:"
print(resumen_wide)
#> Key: <producto, categoria>
#> producto categoria 2023_revenue 2023_unidades 2024_revenue
#> <char> <char> <num> <num> <num>
#> 1: Cámara Multimedia 20000 80 20000
#> 2: Desktop Computadoras 68000 85 68000
#> 3: Impresora Oficina 13250 66 15000
#> 4: Laptop Computadoras 225000 188 228000
#> 5: Monitor Periféricos 45000 150 51000
#> 6: Mouse Periféricos 12688 508 13750
#> 7: Router Redes 14438 96 15750
#> 8: Smartphone Móviles 205500 342 228000
#> 9: Tablet Computadoras 76000 190 80000
#> 10: Teclado Periféricos 21250 425 22500
#> 2024_unidades
#> <num>
#> 1: 80
#> 2: 85
#> 3: 75
#> 4: 190
#> 5: 170
#> 6: 550
#> 7: 105
#> 8: 380
#> 9: 200
#> 10: 4508.3.3 3. dcast() con Funciones de Agregación Personalizadas
# Análisis completo de empleados por departamento
empleados_analisis <- dcast(empleados_long,
departamento + nivel ~ .,
value.var = c("bonus", "evaluacion", "proyectos"),
fun.aggregate = list(
mean = mean,
max = max,
min = min
))
print("Análisis agregado de empleados:")
#> [1] "Análisis agregado de empleados:"
print(empleados_analisis)
#> Key: <departamento, nivel>
#> departamento nivel bonus_mean evaluacion_mean proyectos_mean bonus_max
#> <char> <char> <num> <num> <num> <num>
#> 1: IT Junior 2997.50 4.200 3.250 6544
#> 2: IT Lead 8586.00 3.750 7.750 14636
#> 3: IT Manager 5640.25 3.950 4.750 8626
#> 4: IT Senior 5855.50 4.350 4.125 12881
#> 5: Marketing Junior 5739.75 4.150 4.250 9434
#> ---
#> 12: RRHH Senior 4420.25 3.275 6.500 7729
#> 13: Ventas Junior 5225.00 4.200 5.875 10347
#> 14: Ventas Lead 5906.00 4.000 6.250 7472
#> 15: Ventas Manager 4664.00 3.750 5.250 7673
#> 16: Ventas Senior 4700.00 3.975 2.750 9105
#> evaluacion_max proyectos_max bonus_min evaluacion_min proyectos_min
#> <num> <int> <num> <num> <int>
#> 1: 4.9 7 4 3.5 1
#> 2: 4.2 12 4157 3.3 5
#> 3: 4.7 9 2792 3.1 1
#> 4: 5.0 8 169 3.2 1
#> 5: 4.8 9 1410 3.2 2
#> ---
#> 12: 3.4 12 431 3.1 1
#> 13: 4.9 12 378 3.5 2
#> 14: 4.7 9 4470 3.3 3
#> 15: 4.1 11 264 3.4 1
#> 16: 4.6 5 896 3.3 18.3.4 4. Tablas de Contingencia con dcast()
# Transformar encuesta a formato largo primero
encuesta_long <- melt(encuesta_satisfaccion,
id.vars = c("respuesta_id", "cliente_id", "fecha_encuesta", "edad", "genero", "region"),
measure.vars = patterns("^satisfaccion_", "gasto_mensual"),
variable.name = "aspecto_satisfaccion",
value.name = c("puntuacion", "gasto_mensual"))
# Limpiar nombres de aspectos
encuesta_long[, aspecto := sub("satisfaccion_", "", aspecto_satisfaccion)]
# Crear tabla de contingencia: región vs aspecto (promedio de satisfacción)
tabla_contingencia <- dcast(encuesta_long,
region ~ aspecto,
value.var = "puntuacion",
fun.aggregate = mean)
print("Tabla de contingencia: Satisfacción promedio por región y aspecto:")
#> [1] "Tabla de contingencia: Satisfacción promedio por región y aspecto:"
print(tabla_contingencia)
#> Key: <region>
#> region 1 2 3 4
#> <char> <num> <num> <num> <num>
#> 1: Este 3.791667 3.875000 3.750000 3.375000
#> 2: Norte 3.700000 3.500000 3.150000 3.150000
#> 3: Oeste 3.750000 3.718750 3.062500 3.375000
#> 4: Sur 4.083333 3.791667 3.208333 3.416667
# Matriz de correlación usando dcast
# Primero, crear datos en formato adecuado
matriz_correlacion_data <- encuesta_long[, .(
satisfaccion_promedio = mean(puntuacion)
), by = .(cliente_id, aspecto)]
matriz_correlacion_wide <- dcast(matriz_correlacion_data,
cliente_id ~ aspecto,
value.var = "satisfaccion_promedio")
# Calcular correlaciones
cor_matrix <- cor(matriz_correlacion_wide[, -"cliente_id"], use = "complete.obs")
print("\nMatriz de correlaciones entre aspectos:")
#> [1] "\nMatriz de correlaciones entre aspectos:"
print(round(cor_matrix, 3))
#> 1 2 3 4
#> 1 1.000 0.153 0.228 -0.311
#> 2 0.153 1.000 -0.127 -0.090
#> 3 0.228 -0.127 1.000 -0.175
#> 4 -0.311 -0.090 -0.175 1.0008.4 Casos de Uso Avanzados
8.4.1 1. Reportes Ejecutivos Dinámicos
# Crear reporte ejecutivo completo combinando melt/dcast
reporte_ejecutivo <- ventas_long_separada[
# Calcular métricas adicionales
, `:=`(
año = ifelse(grepl("2023", periodo), "2023", "2024"),
trimestre_num = as.numeric(substr(periodo, 2, 2))
)
][
# Agrupar y calcular KPIs
, .(
valor_total = sum(valor),
productos_activos = uniqueN(producto)
), by = .(categoria, año, metrica)
]
# Crear formato ancho para reporte
reporte_ejecutivo <- dcast(reporte_ejecutivo, categoria ~ año + metrica, value.var = "valor_total")
print("Reporte Ejecutivo de Ventas:")
#> [1] "Reporte Ejecutivo de Ventas:"
print(reporte_ejecutivo)
#> Key: <categoria>
#> categoria 2023_revenue 2023_unidades 2024_revenue 2024_unidades
#> <char> <num> <num> <num> <num>
#> 1: Computadoras 1476000 1850 376000 475
#> 2: Multimedia 80000 320 20000 80
#> 3: Móviles 822000 1370 228000 380
#> 4: Oficina 53000 265 15000 75
#> 5: Periféricos 315750 4330 87250 1170
#> 6: Redes 57750 385 15750 105
# Calcular crecimiento año sobre año
reporte_con_crecimiento <- copy(reporte_ejecutivo)[, `:=`(
crecimiento_revenue = round((get("2024_revenue") - get("2023_revenue")) / get("2023_revenue") * 100, 1),
crecimiento_unidades = round((get("2024_unidades") - get("2023_unidades")) / get("2023_unidades") * 100, 1)
)]
print("\nReporte con análisis de crecimiento:")
#> [1] "\nReporte con análisis de crecimiento:"
print(reporte_con_crecimiento[, .(categoria,
revenue_2023 = `2023_revenue`,
revenue_2024 = `2024_revenue`,
crecimiento_revenue,
crecimiento_unidades)])
#> Key: <categoria>
#> categoria revenue_2023 revenue_2024 crecimiento_revenue
#> <char> <num> <num> <num>
#> 1: Computadoras 1476000 376000 -74.5
#> 2: Multimedia 80000 20000 -75.0
#> 3: Móviles 822000 228000 -72.3
#> 4: Oficina 53000 15000 -71.7
#> 5: Periféricos 315750 87250 -72.4
#> 6: Redes 57750 15750 -72.7
#> crecimiento_unidades
#> <num>
#> 1: -74.3
#> 2: -75.0
#> 3: -72.3
#> 4: -71.7
#> 5: -73.0
#> 6: -72.78.4.2 2. Dashboard de Sensores en Tiempo Real
# Crear dashboard de estado actual de sensores
estado_actual_sensores <- sensores_long[
# Obtener última lectura por sensor
, .SD[.N], by = .(ubicacion, tipo_sensor, numero_sensor)
][
# Clasificar estado según rangos normales
, estado := fcase(
tipo_sensor == "sensor_temp" & (medicion < 15 | medicion > 30), "ALERTA",
tipo_sensor == "sensor_humedad" & (medicion < 30 | medicion > 80), "ALERTA",
tipo_sensor == "sensor_presion" & (medicion < 1000 | medicion > 1030), "ALERTA",
default = "NORMAL"
)
]
# Dashboard en formato ancho
dashboard_wide <- dcast(estado_actual_sensores,
ubicacion ~ tipo_sensor + numero_sensor,
value.var = "medicion")
print("Dashboard de Sensores (valores actuales):")
#> [1] "Dashboard de Sensores (valores actuales):"
print(dashboard_wide)
#> Key: <ubicacion>
#> ubicacion sensor_humedad_1 sensor_humedad_2 sensor_presion_1
#> <char> <num> <num> <num>
#> 1: Planta_Centro 60.5 74.1 1006.4
#> 2: Planta_Norte 42.9 58.0 1012.8
#> 3: Planta_Sur 71.0 60.6 1015.7
#> sensor_presion_2 sensor_temp_1 sensor_temp_2
#> <num> <num> <num>
#> 1: 998.2 19.9 25.7
#> 2: 999.9 18.4 25.8
#> 3: 1029.3 19.4 26.0
# Tabla de alertas
alertas_sensores <- estado_actual_sensores[estado == "ALERTA"]
if(nrow(alertas_sensores) > 0) {
print("\n🚨 ALERTAS ACTIVAS:")
print(alertas_sensores[, .(ubicacion, tipo_sensor, numero_sensor, medicion, timestamp)])
} else {
cat("\n✅ Todos los sensores operan en rangos normales\n")
}
#> [1] "\n🚨 ALERTAS ACTIVAS:"
#> ubicacion tipo_sensor numero_sensor medicion timestamp
#> <char> <char> <num> <num> <POSc>
#> 1: Planta_Norte sensor_presion 2 999.9 2024-01-07 18:00:00
#> 2: Planta_Centro sensor_presion 2 998.2 2024-01-07 18:00:008.4.3 3. Análisis Multivariable de Encuestas
# Análisis completo de satisfacción por segmentos
analisis_satisfaccion <- encuesta_long[
# Agregar segmentación demográfica
, `:=`(
grupo_edad = cut(edad, breaks = c(0, 30, 45, 60, 100),
labels = c("Joven", "Adulto", "Maduro", "Senior")),
gasto_categoria = cut(gasto_mensual, breaks = c(0, 100, 300, 500, Inf),
labels = c("Bajo", "Medio", "Alto", "Premium"))
)
][
# Calcular satisfacción promedio por segmento y aspecto
, .(
satisfaccion_promedio = round(mean(puntuacion), 2),
respuestas = .N
), by = .(region, grupo_edad, gasto_categoria, aspecto)
]
# Crear matriz de satisfacción: aspecto vs segmento
matriz_satisfaccion <- dcast(analisis_satisfaccion,
region + grupo_edad + gasto_categoria ~ aspecto,
value.var = "satisfaccion_promedio",
fun.aggregate = mean)
print("Matriz de satisfacción por segmento:")
#> [1] "Matriz de satisfacción por segmento:"
print(head(matriz_satisfaccion, 10))
#> Key: <region, grupo_edad, gasto_categoria>
#> region grupo_edad gasto_categoria 1 2 3 4
#> <char> <fctr> <fctr> <num> <num> <num> <num>
#> 1: Este Joven <NA> NaN 4.00 4.00 3.50
#> 2: Este Joven Bajo 4.00 NaN NaN NaN
#> 3: Este Joven Medio 2.50 NaN NaN NaN
#> 4: Este Joven Alto 4.00 NaN NaN NaN
#> 5: Este Joven Premium 2.00 NaN NaN NaN
#> 6: Este Adulto <NA> NaN 3.57 3.14 3.00
#> 7: Este Adulto Bajo 3.00 NaN NaN NaN
#> 8: Este Adulto Medio 4.67 NaN NaN NaN
#> 9: Este Adulto Alto 4.67 NaN NaN NaN
#> 10: Este Maduro <NA> NaN 4.17 3.83 3.33
# Verificar qué columnas se crearon después del dcast
print("Columnas en matriz_satisfaccion:")
#> [1] "Columnas en matriz_satisfaccion:"
print(names(matriz_satisfaccion))
#> [1] "region" "grupo_edad" "gasto_categoria" "1"
#> [5] "2" "3" "4"
# Identificar segmentos críticos (satisfacción baja en múltiples aspectos)
# Primero verificar qué columnas de aspectos existen
columnas_aspectos <- names(matriz_satisfaccion)[!names(matriz_satisfaccion) %in% c("region", "grupo_edad", "gasto_categoria")]
print(paste("Columnas de aspectos encontradas:", paste(columnas_aspectos, collapse = ", ")))
#> [1] "Columnas de aspectos encontradas: 1, 2, 3, 4"
if(length(columnas_aspectos) >= 4) {
# Si tenemos las 4 columnas esperadas (entrega, precio, producto, servicio)
segmentos_criticos <- matriz_satisfaccion[
# Calcular score de satisfacción general usando las columnas que existen
, satisfaccion_general := round(rowMeans(.SD, na.rm = TRUE), 2), .SDcols = columnas_aspectos
][
satisfaccion_general < 3.5 # Umbral crítico
][order(satisfaccion_general)]
} else {
# Si no tenemos todas las columnas, usar un enfoque más simple
cat("No se encontraron todas las columnas de aspectos esperadas. Usando análisis simplificado.\n")
segmentos_criticos <- matriz_satisfaccion[1:0] # Tabla vacía para evitar errores
}
if(nrow(segmentos_criticos) > 0) {
print("\n⚠️ SEGMENTOS CRÍTICOS (satisfacción < 3.5):")
print(segmentos_criticos[, .(region, grupo_edad, gasto_categoria, satisfaccion_general)])
} else {
cat("\n✅ No hay segmentos con satisfacción crítica\n")
}
#> [1] "\n⚠️ SEGMENTOS CRÍTICOS (satisfacción < 3.5):"
#> region grupo_edad gasto_categoria satisfaccion_general
#> <char> <fctr> <fctr> <num>
#> 1: Este Joven Premium 2.00
#> 2: Este Maduro Premium 2.00
#> 3: Este Joven Medio 2.50
#> 4: Norte Maduro Premium 2.50
#> 5: Este Adulto Bajo 3.00
#> ---
#> 21: Oeste Adulto <NA> 3.34
#> 22: Norte Adulto <NA> 3.39
#> 23: Sur Maduro <NA> 3.39
#> 24: Sur Adulto <NA> 3.46
#> 25: Oeste Senior <NA> 3.488.5 Ejercicios Prácticos
💡 Solución del Ejercicio 13
# 1. Pipeline completo de reshape para análisis de empleados
# Paso 1: melt() para formato largo
empleados_melted <- melt(empleados_metricas,
id.vars = c("empleado_id", "nombre", "departamento", "nivel", "salario_base"),
measure = patterns(
bonus = "^bonus_Q",
evaluacion = "^evaluacion_Q",
proyectos = "^proyectos_Q"
),
variable.name = "trimestre_num",
value.name = c("bonus", "evaluacion", "proyectos"))
# 2. Agregar variables derivadas
empleados_enriquecido <- empleados_melted[, `:=`(
trimestre = paste0("Q", trimestre_num),
compensacion_total = salario_base / 4 + bonus, # Salario trimestral + bonus
productividad = round(proyectos / (evaluacion + 0.1), 2), # Proyectos por punto de evaluación
categoria_performance = fcase(
evaluacion >= 4.5, "Excelente",
evaluacion >= 4.0, "Muy Bueno",
evaluacion >= 3.5, "Bueno",
evaluacion >= 3.0, "Satisfactorio",
default = "Necesita Mejora"
),
rango_bonus = fcase(
bonus >= 10000, "Alto",
bonus >= 5000, "Medio",
bonus > 0, "Bajo",
default = "Sin Bonus"
)
)]
# 3. Crear múltiples vistas con dcast()
# Vista 1: Performance promedio por departamento y nivel
performance_depto <- dcast(empleados_enriquecido,
departamento + nivel ~ .,
value.var = c("evaluacion", "productividad", "compensacion_total"),
fun.aggregate = list(mean = mean, max = max))
cat("📊 VISTA 1: PERFORMANCE POR DEPARTAMENTO Y NIVEL\n")
#> 📊 VISTA 1: PERFORMANCE POR DEPARTAMENTO Y NIVEL
print(performance_depto[order(departamento, nivel)])
#> Key: <departamento, nivel>
#> departamento nivel evaluacion_mean productividad_mean
#> <char> <char> <num> <num>
#> 1: IT Junior 4.200 0.71000
#> 2: IT Lead 3.750 2.04750
#> 3: IT Manager 3.950 1.15250
#> 4: IT Senior 4.350 1.01125
#> 5: Marketing Junior 4.150 1.06250
#> ---
#> 12: RRHH Senior 3.275 1.93250
#> 13: Ventas Junior 4.200 1.41250
#> 14: Ventas Lead 4.000 1.61750
#> 15: Ventas Manager 3.750 1.36250
#> 16: Ventas Senior 3.975 0.73000
#> compensacion_total_mean evaluacion_max productividad_max
#> <num> <num> <num>
#> 1: 21597.50 4.9 1.40
#> 2: 22011.00 4.2 3.43
#> 3: 17790.25 4.7 1.89
#> 4: 19230.50 5.0 2.42
#> 5: 22414.75 4.8 2.20
#> ---
#> 12: 22870.25 3.4 3.53
#> 13: 21262.50 4.9 3.33
#> 14: 22306.00 4.7 2.65
#> 15: 21264.00 4.1 2.97
#> 16: 17050.00 4.6 1.47
#> compensacion_total_max
#> <num>
#> 1: 25144
#> 2: 28061
#> 3: 20776
#> 4: 26428
#> 5: 26109
#> ---
#> 12: 26179
#> 13: 26904
#> 14: 23872
#> 15: 24273
#> 16: 21455
# Vista 2: Evolución trimestral por empleado (transpuesta)
evolucion_empleados <- dcast(empleados_enriquecido[departamento == "IT"], # Solo IT para ejemplo
nombre ~ trimestre,
value.var = "evaluacion",
fun.aggregate = mean)
cat("\n📈 VISTA 2: EVOLUCIÓN DE EVALUACIONES - DEPARTAMENTO IT\n")
#>
#> 📈 VISTA 2: EVOLUCIÓN DE EVALUACIONES - DEPARTAMENTO IT
print(evolucion_empleados)
#> Key: <nombre>
#> nombre Q1 Q2 Q3 Q4
#> <char> <num> <num> <num> <num>
#> 1: Empleado_F 4.6 3.2 4.9 4.7
#> 2: Empleado_G 3.3 4.2 4.1 3.4
#> 3: Empleado_H 3.1 4.7 3.6 4.4
#> 4: Empleado_I 3.5 4.9 4.1 4.3
#> 5: Empleado_J 4.6 5.0 3.8 4.0
# Vista 3: Matriz de categorías (contingencia)
matriz_categorias <- dcast(empleados_enriquecido,
departamento ~ categoria_performance,
value.var = "empleado_id",
fun.aggregate = function(x) length(unique(x)))
cat("\n🎯 VISTA 3: MATRIZ DE CATEGORÍAS DE PERFORMANCE\n")
#>
#> 🎯 VISTA 3: MATRIZ DE CATEGORÍAS DE PERFORMANCE
print(matriz_categorias)
#> Key: <departamento>
#> departamento Bueno Excelente Muy Bueno Satisfactorio
#> <char> <int> <int> <int> <int>
#> 1: IT 3 4 4 3
#> 2: Marketing 3 5 5 4
#> 3: RRHH 2 3 4 4
#> 4: Ventas 4 4 3 3
# 4. Reporte ejecutivo combinando técnicas
cat("\n📋 REPORTE EJECUTIVO DE RRHH\n")
#>
#> 📋 REPORTE EJECUTIVO DE RRHH
# KPIs generales
kpis_generales <- empleados_enriquecido[, .(
empleados_unicos = uniqueN(empleado_id),
evaluacion_promedio = round(mean(evaluacion), 2),
bonus_promedio = round(mean(bonus), 0),
proyectos_promedio = round(mean(proyectos), 1),
compensacion_promedio = round(mean(compensacion_total), 0)
)]
cat("KPIs GENERALES:\n")
#> KPIs GENERALES:
cat("• Empleados analizados:", kpis_generales$empleados_unicos, "\n")
#> • Empleados analizados: 20
cat("• Evaluación promedio:", kpis_generales$evaluacion_promedio, "/5.0\n")
#> • Evaluación promedio: 4.02 /5.0
cat("• Bonus promedio trimestral:", scales::dollar(kpis_generales$bonus_promedio), "\n")
#> • Bonus promedio trimestral: $5,465
cat("• Proyectos promedio por trimestre:", kpis_generales$proyectos_promedio, "\n")
#> • Proyectos promedio por trimestre: 5.1
cat("• Compensación total promedio:", scales::dollar(kpis_generales$compensacion_promedio), "\n\n")
#> • Compensación total promedio: $20,502
# Top performers
top_performers <- empleados_enriquecido[, .(
evaluacion_promedio = round(mean(evaluacion), 2),
productividad_promedio = round(mean(productividad), 2),
bonus_total = sum(bonus)
), by = .(empleado_id, nombre, departamento)][
order(-evaluacion_promedio, -productividad_promedio)
][1:5]
cat("🏆 TOP 5 PERFORMERS:\n")
#> 🏆 TOP 5 PERFORMERS:
for(i in 1:nrow(top_performers)) {
emp <- top_performers[i]
cat(sprintf("%d. %s (%s) - Eval: %.1f, Productividad: %.1f, Bonus Total: %s\n",
i, emp$nombre, emp$departamento, emp$evaluacion_promedio,
emp$productividad_promedio, scales::dollar(emp$bonus_total)))
}
#> 1. Empleado_P (RRHH) - Eval: 4.4, Productividad: 1.3, Bonus Total: $23,858
#> 2. Empleado_J (IT) - Eval: 4.3, Productividad: 1.2, Bonus Total: $23,002
#> 3. Empleado_F (IT) - Eval: 4.3, Productividad: 0.9, Bonus Total: $23,842
#> 4. Empleado_O (Marketing) - Eval: 4.2, Productividad: 1.0, Bonus Total: $27,901
#> 5. Empleado_E (Ventas) - Eval: 4.2, Productividad: 0.8, Bonus Total: $28,921
# Análisis por departamento
analisis_depto <- empleados_enriquecido[, .(
empleados = uniqueN(empleado_id),
evaluacion_promedio = round(mean(evaluacion), 2),
empleados_excelentes = sum(categoria_performance == "Excelente"),
tasa_excelencia = round(mean(categoria_performance == "Excelente") * 100, 1),
presupuesto_bonus = sum(bonus)
), by = departamento][order(-tasa_excelencia)]
cat("\n🏢 ANÁLISIS POR DEPARTAMENTO:\n")
#>
#> 🏢 ANÁLISIS POR DEPARTAMENTO:
print(analisis_depto)
#> departamento empleados evaluacion_promedio empleados_excelentes
#> <char> <int> <num> <int>
#> 1: Ventas 5 4.03 7
#> 2: IT 5 4.12 7
#> 3: Marketing 5 4.02 7
#> 4: RRHH 5 3.92 3
#> tasa_excelencia presupuesto_bonus
#> <num> <num>
#> 1: 35 102880
#> 2: 35 115739
#> 3: 35 115452
#> 4: 15 103162
# Recomendaciones automáticas
cat("\n💡 RECOMENDACIONES AUTOMÁTICAS:\n")
#>
#> 💡 RECOMENDACIONES AUTOMÁTICAS:
# Departamento con mejor performance
mejor_depto <- analisis_depto[1, departamento]
peor_depto <- analisis_depto[.N, departamento]
cat("• Mejores prácticas de", mejor_depto, "podrían replicarse en otros departamentos\n")
#> • Mejores prácticas de Ventas podrían replicarse en otros departamentos
cat("• Departamento", peor_depto, "requiere plan de mejora en evaluaciones\n")
#> • Departamento RRHH requiere plan de mejora en evaluaciones
# Empleados que necesitan atención
empleados_atencion <- empleados_enriquecido[
categoria_performance %in% c("Necesita Mejora", "Satisfactorio"),
uniqueN(empleado_id),
by = departamento
][V1 > 0]
if(nrow(empleados_atencion) > 0) {
cat("• Revisar planes de desarrollo individual en:", paste(empleados_atencion$departamento, collapse = ", "), "\n")
}
#> • Revisar planes de desarrollo individual en: Ventas, IT, Marketing, RRHH
# # Crear tabla interactiva del reporte (comentado para PDF)
# DT::datatable(
# performance_depto,
# caption = "Dashboard Ejecutivo de Performance - RRHH",
# options = list(pageLength = 10, scrollX = TRUE)
# ) %>%
# DT::formatRound(c("evaluacion_mean", "productividad_mean"), digits = 2) %>%
# DT::formatCurrency("compensacion_total_mean", currency = "$")
💡 Solución del Ejercicio 14
# 1. Análisis temporal completo con reshape
# Preparar datos base con información temporal
sensores_temporal <- sensores_long[, `:=`(
fecha = as.Date(timestamp),
hora = hour(timestamp),
dia_semana = wday(timestamp, label = TRUE)
)]
# 2. Crear ventanas móviles por tipo de sensor
sensores_con_ventanas <- sensores_temporal[order(ubicacion, tipo_sensor, numero_sensor, timestamp)][, `:=`(
# Ventanas móviles de 24 horas (4 mediciones = 24 horas con datos cada 6h)
media_24h = frollmean(medicion, 4, na.rm = TRUE),
media_48h = frollmean(medicion, 8, na.rm = TRUE),
desv_24h = frollapply(medicion, 4, sd, na.rm = TRUE),
# Cambios temporales
cambio_6h = abs(medicion - shift(medicion, 1)),
cambio_24h = abs(medicion - shift(medicion, 4)),
# Tendencia
tendencia_24h = frollapply(medicion, 4, function(x) {
if(length(x) < 4) return(0)
lm(x ~ seq_along(x))$coefficients[2]
})
), by = .(ubicacion, tipo_sensor, numero_sensor)]
# 3. Detección de anomalías por tipo de sensor
sensores_con_anomalias <- sensores_con_ventanas[, `:=`(
# Rangos normales específicos por tipo
limite_inferior = fcase(
tipo_sensor == "sensor_temp", 15,
tipo_sensor == "sensor_humedad", 30,
tipo_sensor == "sensor_presion", 1000,
default = -Inf
),
limite_superior = fcase(
tipo_sensor == "sensor_temp", 30,
tipo_sensor == "sensor_humedad", 80,
tipo_sensor == "sensor_presion", 1030,
default = Inf
)
)][, `:=`(
# Detectar anomalías
anomalia_rango = medicion < limite_inferior | medicion > limite_superior,
anomalia_cambio_subito = !is.na(cambio_6h) & cambio_6h > fcase(
tipo_sensor == "sensor_temp", 5,
tipo_sensor == "sensor_humedad", 15,
tipo_sensor == "sensor_presion", 20,
default = Inf
),
anomalia_desviacion = !is.na(media_24h) & !is.na(desv_24h) &
abs(medicion - media_24h) > 2 * desv_24h,
anomalia_tendencia = !is.na(tendencia_24h) & abs(tendencia_24h) > fcase(
tipo_sensor == "sensor_temp", 1,
tipo_sensor == "sensor_humedad", 3,
tipo_sensor == "sensor_presion", 5,
default = Inf
)
)][, `:=`(
# Score compuesto de anomalía
score_anomalia = (as.numeric(anomalia_rango) * 4) +
(as.numeric(anomalia_cambio_subito) * 3) +
(as.numeric(anomalia_desviacion) * 2) +
(as.numeric(anomalia_tendencia) * 1),
# Clasificación de severidad
severidad_anomalia = fcase(
(anomalia_rango) * 4 + (anomalia_cambio_subito) * 3 + (anomalia_desviacion) * 2 + (anomalia_tendencia) * 1 >= 6, "CRÍTICA",
(anomalia_rango) * 4 + (anomalia_cambio_subito) * 3 + (anomalia_desviacion) * 2 + (anomalia_tendencia) * 1 >= 4, "ALTA",
(anomalia_rango) * 4 + (anomalia_cambio_subito) * 3 + (anomalia_desviacion) * 2 + (anomalia_tendencia) * 1 >= 2, "MEDIA",
(anomalia_rango) * 4 + (anomalia_cambio_subito) * 3 + (anomalia_desviacion) * 2 + (anomalia_tendencia) * 1 >= 1, "BAJA",
default = "NORMAL"
)
)]
# 4. Generar reporte ejecutivo de alertas
cat("🚨 REPORTE DE ALERTAS - SISTEMA DE SENSORES 🚨\n")
#> 🚨 REPORTE DE ALERTAS - SISTEMA DE SENSORES 🚨
cat(rep("=", 60), "\n\n")
#> = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Resumen general usando reshape
resumen_alertas <- sensores_con_anomalias[severidad_anomalia != "NORMAL", .N,
by = .(ubicacion, tipo_sensor, severidad_anomalia)]
if(nrow(resumen_alertas) > 0) {
# Crear matriz de alertas con dcast
matriz_alertas <- dcast(resumen_alertas,
ubicacion + tipo_sensor ~ severidad_anomalia,
value.var = "N",
fill = 0)
cat("📊 MATRIZ DE ALERTAS ACTIVAS:\n")
print(matriz_alertas)
} else {
cat("✅ No hay alertas activas en el sistema\n")
}
#> 📊 MATRIZ DE ALERTAS ACTIVAS:
#> Key: <ubicacion, tipo_sensor>
#> ubicacion tipo_sensor ALTA BAJA CRÍTICA
#> <char> <char> <int> <int> <int>
#> 1: Planta_Centro sensor_humedad 0 3 0
#> 2: Planta_Centro sensor_presion 16 3 0
#> 3: Planta_Centro sensor_temp 2 15 0
#> 4: Planta_Norte sensor_humedad 0 3 0
#> 5: Planta_Norte sensor_presion 12 1 1
#> 6: Planta_Norte sensor_temp 2 10 0
#> 7: Planta_Sur sensor_humedad 0 3 0
#> 8: Planta_Sur sensor_presion 16 0 0
#> 9: Planta_Sur sensor_temp 2 7 0
# Estado actual por sensor (usando reshape)
estado_actual <- sensores_con_anomalias[, .SD[.N],
by = .(ubicacion, tipo_sensor, numero_sensor)][, .(
ubicacion, tipo_sensor, numero_sensor, timestamp, medicion,
severidad_anomalia, score_anomalia
)]
# Convertir a formato wide para dashboard
dashboard_estado <- dcast(estado_actual,
ubicacion ~ tipo_sensor + numero_sensor,
value.var = "medicion")
cat("\n🏢 ESTADO ACTUAL POR UBICACIÓN:\n")
#>
#> 🏢 ESTADO ACTUAL POR UBICACIÓN:
print(dashboard_estado)
#> Key: <ubicacion>
#> ubicacion sensor_humedad_1 sensor_humedad_2 sensor_presion_1
#> <char> <num> <num> <num>
#> 1: Planta_Centro 60.5 74.1 1006.4
#> 2: Planta_Norte 42.9 58.0 1012.8
#> 3: Planta_Sur 71.0 60.6 1015.7
#> sensor_presion_2 sensor_temp_1 sensor_temp_2
#> <num> <num> <num>
#> 1: 998.2 19.9 25.7
#> 2: 999.9 18.4 25.8
#> 3: 1029.3 19.4 26.0
# Top alertas críticas
alertas_criticas <- sensores_con_anomalias[
severidad_anomalia %in% c("CRÍTICA", "ALTA")
][order(-score_anomalia, -timestamp)][1:min(10, .N)]
if(nrow(alertas_criticas) > 0) {
cat("\n🔥 TOP ALERTAS CRÍTICAS:\n")
print(alertas_criticas[, .(
Ubicación = ubicacion,
Sensor = paste(tipo_sensor, numero_sensor),
Timestamp = timestamp,
Medición = medicion,
Severidad = severidad_anomalia,
Score = score_anomalia
)])
}
#>
#> 🔥 TOP ALERTAS CRÍTICAS:
#> Ubicación Sensor Timestamp Medición Severidad Score
#> <char> <char> <POSc> <num> <char> <num>
#> 1: Planta_Norte sensor_presion 1 2024-01-04 00:00:00 1044.3 CRÍTICA 7
#> 2: Planta_Centro sensor_temp 1 2024-01-06 00:00:00 14.5 ALTA 5
#> 3: Planta_Norte sensor_temp 1 2024-01-06 00:00:00 13.5 ALTA 5
#> 4: Planta_Norte sensor_presion 1 2024-01-03 00:00:00 1035.1 ALTA 5
#> 5: Planta_Norte sensor_presion 1 2024-01-02 18:00:00 1038.1 ALTA 5
#> 6: Planta_Centro sensor_presion 2 2024-01-07 18:00:00 998.2 ALTA 4
#> 7: Planta_Norte sensor_presion 2 2024-01-07 18:00:00 999.9 ALTA 4
#> 8: Planta_Sur sensor_presion 2 2024-01-07 12:00:00 1031.5 ALTA 4
#> 9: Planta_Centro sensor_presion 2 2024-01-07 00:00:00 997.3 ALTA 4
#> 10: Planta_Sur sensor_presion 2 2024-01-07 00:00:00 1033.3 ALTA 4
# Análisis de tendencias por tipo usando melt/dcast
tendencias_tipo <- sensores_con_anomalias[!is.na(tendencia_24h), .(
tendencia_promedio = round(mean(tendencia_24h, na.rm = TRUE), 4),
medicion_promedio = round(mean(medicion, na.rm = TRUE), 2),
anomalias_total = sum(severidad_anomalia != "NORMAL")
), by = .(ubicacion, tipo_sensor)]
# Formato wide para comparación
tendencias_wide <- dcast(tendencias_tipo,
ubicacion ~ tipo_sensor,
value.var = "tendencia_promedio")
cat("\n📈 TENDENCIAS PROMEDIO POR UBICACIÓN (24h):\n")
#>
#> 📈 TENDENCIAS PROMEDIO POR UBICACIÓN (24h):
print(tendencias_wide)
#> Key: <ubicacion>
#> ubicacion sensor_humedad sensor_presion sensor_temp
#> <char> <num> <num> <num>
#> 1: Planta_Centro -0.0194 -0.7440 -0.0484
#> 2: Planta_Norte -0.7072 -0.4502 -0.0448
#> 3: Planta_Sur 0.5846 0.5512 -0.0688
# Recomendaciones automáticas
cat("\n💡 RECOMENDACIONES AUTOMÁTICAS:\n")
#>
#> 💡 RECOMENDACIONES AUTOMÁTICAS:
# Sensores con más anomalías
sensores_problematicos <- sensores_con_anomalias[, .(
anomalias = sum(severidad_anomalia != "NORMAL")
), by = .(ubicacion, tipo_sensor, numero_sensor)][anomalias > 0][order(-anomalias)]
if(nrow(sensores_problematicos) > 0) {
top_problematico <- sensores_problematicos[1]
cat("• Revisar sensor", paste(top_problematico$tipo_sensor, top_problematico$numero_sensor),
"en", top_problematico$ubicacion, "con", top_problematico$anomalias, "anomalías\n")
}
#> • Revisar sensor sensor_presion 1 en Planta_Centro con 15 anomalías
# Ubicación con más problemas
ubicacion_problemas <- sensores_con_anomalias[, .(
anomalias_total = sum(severidad_anomalia != "NORMAL")
), by = ubicacion][order(-anomalias_total)]
if(nrow(ubicacion_problemas) > 0 && ubicacion_problemas[1, anomalias_total] > 0) {
cat("• Priorizar mantenimiento en", ubicacion_problemas[1, ubicacion],
"con", ubicacion_problemas[1, anomalias_total], "anomalías totales\n")
}
#> • Priorizar mantenimiento en Planta_Centro con 39 anomalías totales
cat("• Siguiente revisión recomendada: en 6 horas\n")
#> • Siguiente revisión recomendada: en 6 horas
# # Tabla interactiva de alertas críticas (comentado para PDF)
# if(nrow(alertas_criticas) > 0) {
# DT::datatable(
# alertas_criticas[, .(ubicacion, tipo_sensor, numero_sensor, timestamp,
# medicion, severidad_anomalia, score_anomalia)],
# caption = "Alertas Críticas del Sistema de Sensores",
# options = list(pageLength = 10, scrollX = TRUE)
# ) %>%
# DT::formatStyle(
# "severidad_anomalia",
# backgroundColor = DT::styleEqual(
# c("CRÍTICA", "ALTA", "MEDIA", "BAJA"),
# c("red", "orange", "yellow", "lightblue")
# )
# ) %>%
# DT::formatRound("medicion", digits = 2)
# }8.6 Mejores Prácticas para Reshape
8.6.1 1. Cuándo Usar Cada Técnica
# ✅ Usar melt() cuando:
# - Necesitas análisis estadístico o visualización con ggplot2
# - Quieres aplicar funciones por grupos de variables
# - Los datos vienen de Excel/reportes en formato ancho
datos_para_analisis <- melt(datos_wide, id.vars = "identificador")
# ✅ Usar dcast() cuando:
# - Necesitas crear reportes ejecutivos o dashboards
# - Quieres matrices de correlación o contingencia
# - Necesitas format de "tabla dinámica" para presentación
reporte_ejecutivo <- dcast(datos_long, fila ~ columna, value.var = "valor")
# ✅ Combinar ambos para:
# - Pipelines de transformación complejos
# - Análisis que requieren múltiples vistas de los mismos datos
pipeline_completo <- datos %>% melt(...) %>%
enriquecer(...) %>% dcast(...)8.6.2 2. Performance y Memoria
# ✅ HACER: Especificar measure.vars explícitamente
melt(dt, measure.vars = c("col1", "col2", "col3")) # Más rápido
# ❌ EVITAR: Melt sin especificar columnas
melt(dt) # Puede incluir columnas no deseadas
# ✅ HACER: Usar patterns() para múltiples tipos de variables
melt(dt, measure = patterns("^bonus_", "^eval_"))
# ✅ HACER: Limpiar datos después de reshape
datos_melted[, columna_temp := NULL] # Eliminar columnas temporales8.6.3 3. Manejo de Valores Faltantes
# ✅ Control de NAs en dcast
dcast(dt, row ~ col, value.var = "val", fill = 0) # Llenar con 0
dcast(dt, row ~ col, value.var = "val", drop = FALSE) # Mantener combinaciones vacías
# ✅ Manejo de NAs después de melt
datos_melted[!is.na(value)] # Filtrar NAs
datos_melted[, value := nafill(value, fill = 0)] # Llenar NAs
🎯 Puntos Clave de Este Capítulo
melt()convierte datos anchos a largos - esencial para análisis estadístico y visualizacióndcast()convierte datos largos a anchos - perfecto para reportes y dashboards ejecutivos
- Patrones complejos con
patterns()permiten reshape de múltiples tipos de variables simultáneamente - Funciones de agregación en
dcast()crean resúmenes poderosos durante el reshape - Combinar ambas técnicas permite pipelines de transformación muy sofisticados
- Performance: Especificar columnas explícitamente mejora velocidad y memoria