Analítica de Personas · Semestre otoño 2026 · Semana 6 · Prof. René Gempp
Hasta ahora, en esta semana, hemos hecho lo siguiente:
psych::alpha() (Apunte 14).psych::fa() (Apunte 15).Ahora viene la pregunta del VP de Personas: ¿qué dimensión está moviendo más al engagement global? Esa es la pregunta que decide dónde poner presupuesto.
La herramienta natural es la regresión múltiple con engagement global como VD y las cinco dimensiones como predictores:
modelo <- lm(engagement_global ~ liderazgo + desarrollo +
reconocimiento + carga + proposito,
data = datos)
summary(modelo)
broom::tidy(modelo, conf.int = TRUE)
lm() que aprendimos en la auditoría salarial. La diferencia: la VD (engagement_global) es continua y no se transforma con logaritmo. Si necesitas refrescar la lectura del summary() y de broom::tidy(), vuelve al Apunte 11.
Lee con cuidado el coeficiente de "liderazgo" en el output:
term estimate std.error statistic p.value conf.low conf.high 1 (Intercept) 0.971 0.273 3.560 0.000391 0.435 1.508 2 liderazgo 0.756 0.063 11.984 0.000000 0.632 0.880 3 desarrollo 0.685 0.067 10.193 0.000000 0.553 0.817 4 reconocimiento 0.429 0.064 6.713 0.000000 0.304 0.555 5 carga -0.396 0.054 -7.290 0.000000 -0.503 -0.290 6 proposito 0.325 0.062 5.265 0.000000 0.204 0.447
Lectura ingenua: "liderazgo es el driver más fuerte porque tiene el coeficiente más grande". Esa lectura es engañosa, por dos razones:
Necesitamos otra métrica: la importancia relativa.
La importancia relativa busca responder: "si tuviera que repartir el R² total entre los predictores, ¿qué porcentaje le tocaría a cada uno?". Hay varios métodos para calcularla; el más usado y mejor comportado es el método LMG (Lindeman, Merenda & Gold, 1980), implementado en el paquete relaimpo.
La intuición del LMG: cuando los predictores están correlacionados, el R² que aporta cada uno depende del orden en que entran al modelo. Por ejemplo:
El método LMG resuelve este problema promediando la contribución de cada predictor sobre todos los órdenes posibles de entrada. El resultado es un número por cada predictor, y la suma de los números es el R² total del modelo. Esos números sí se pueden comparar honestamente como "qué tanto importa cada driver".
relaimpo::calc.relimp(): cálculo en R# Instalar (una sola vez)
install.packages("relaimpo")
library(relaimpo)
# Calcular importancia relativa con el método LMG
importancia <- relaimpo::calc.relimp(modelo,
type = "lmg",
rela = TRUE)
importancia
Response variable: engagement_global
Total response variance: 4.21
Analysis based on 936 observations
5 Regressors:
liderazgo desarrollo reconocimiento carga proposito
Proportion of variance explained by model: 64.6%
Metrics are normalized to sum to 100%.
Relative importance metrics:
lmg
liderazgo 0.302
desarrollo 0.246
reconocimiento 0.165
carga 0.154
proposito 0.133
Lectura:
rela = TRUE, los cinco números de la columna lmg suman 1 (o 100 %). Cada uno es la fracción del R² explicado que se atribuye a ese driver.El gráfico es un complemento natural:
plot(importancia)
...que produce un gráfico de barras de las contribuciones LMG, ordenadas o no según prefieras.
Tres advertencias importantes que conviene tener presentes:
El employee Net Promoter Score (eNPS) es la versión organizacional del NPS de Reichheld (2003). La pregunta canónica es:
"En una escala de 0 a 10, ¿qué tan probable es que recomiendes a InnovaCo como un buen lugar para trabajar?"
Las respuestas se categorizan en tres grupos:
| Rango | Categoría |
|---|---|
| 9 – 10 | Promotor |
| 7 – 8 | Pasivo |
| 0 – 6 | Detractor |
Y la fórmula del eNPS es simplemente:
eNPS = % Promotores − % Detractores
El score teórico va de −100 (todos detractores) a +100 (todos promotores).
datos <- datos |>
mutate(categoria_enps = case_when(
enps >= 9 ~ "Promotor",
enps >= 7 ~ "Pasivo",
TRUE ~ "Detractor"
))
# eNPS global
datos |>
count(categoria_enps) |>
mutate(pct = n / sum(n) * 100) |>
summarise(eNPS = pct[categoria_enps == "Promotor"] -
pct[categoria_enps == "Detractor"])
# eNPS segmentado por departamento
datos |>
group_by(departamento) |>
summarise(
promotor = mean(categoria_enps == "Promotor") * 100,
detractor= mean(categoria_enps == "Detractor") * 100,
eNPS = promotor - detractor
) |>
arrange(eNPS)
geom_tile()Cuando quieres mostrar una métrica (engagement promedio) cruzada entre dos categorías (departamento × dimensión), un heatmap permite ver patrones de un vistazo. La función ggplot2 es geom_tile():
# Datos en formato largo
heatmap_data <- datos |>
group_by(departamento) |>
summarise(across(c(liderazgo, desarrollo, reconocimiento,
carga, proposito), mean)) |>
pivot_longer(-departamento, names_to = "dimension",
values_to = "puntaje")
ggplot(heatmap_data, aes(dimension, departamento, fill = puntaje)) +
geom_tile(color = "white", linewidth = 0.6) +
geom_text(aes(label = round(puntaje, 1)),
color = "white", size = 3.5) +
scale_fill_gradient2(
midpoint = 3, low = "#B85042", mid = "#E7E8D1", high = "#0D9488",
limits = c(1, 5)
) +
labs(title = "Engagement promedio por departamento y dimensión",
subtitle = "Encuesta InnovaCo 2026 · n = 936",
x = NULL, y = NULL, fill = "Promedio (1–5)") +
theme_minimal()
scale_fill_gradient2() con midpoint anclado
Para datos centrados en un valor con significado (e.g., el "neutral" de una escala Likert 1–5 está en 3), usa scale_fill_gradient2(), no scale_fill_gradient(). La paleta divergente (un color para "bajo", otro para "alto", pasando por un color neutral en el punto medio) hace inmediatamente legible qué celdas están "en zona roja" y cuáles "en zona verde". Si usaras una paleta secuencial, todo se vería en un mismo gradiente y perderías la información ordinal del punto medio.
Las barras divergentes (diverging stacked bar charts) son la forma más honesta de visualizar una distribución de respuestas Likert. La idea: poner el "neutral" en el centro y dejar crecer las respuestas positivas a la derecha y las negativas a la izquierda. Inventadas por Heiberger y Robbins (2014) y popularizadas en R por los paquetes HH y likert.
El patrón en ggplot2 puro:
# Calcular % en cada categoría de respuesta para un ítem
item_div <- datos |>
count(item_rec_01) |>
mutate(
pct = n / sum(n) * 100,
label = factor(item_rec_01,
levels = 1:5,
labels = c("Muy en desacuerdo", "En desacuerdo",
"Neutral", "De acuerdo", "Muy de acuerdo")),
pct_signed = case_when(
item_rec_01 %in% 1:2 ~ -pct,
item_rec_01 == 3 ~ -pct/2,
item_rec_01 %in% 4:5 ~ pct
)
)
ggplot(item_div, aes(x = pct_signed, y = "item", fill = label)) +
geom_col() +
geom_vline(xintercept = 0, color = "black") +
scale_fill_manual(values = c(
"Muy en desacuerdo" = "#B85042",
"En desacuerdo" = "#D88471",
"Neutral" = "#E7E8D1",
"De acuerdo" = "#7DAEA0",
"Muy de acuerdo" = "#0D9488"
)) +
labs(title = "'Mi jefatura reconoce mi trabajo cuando lo hago bien'",
x = "% de respondientes", y = NULL) +
theme_minimal()
likert
Si vas a hacer muchas visualizaciones de este tipo, vale la pena explorar el paquete likert, que automatiza todo el trabajo de barras divergentes con una sintaxis específica para ítems Likert. install.packages("likert") y luego likert::likert(items_df) |> plot().
Mostrar una caída de engagement de un año al otro requiere una visualización que contextualice la magnitud, no solo que la dramatice. Dos opciones efectivas:
geom_segment() + geom_point()comparacion <- datos |>
group_by(departamento) |>
summarise(engagement_2026 = mean(engagement_global)) |>
left_join(hist_2025 |>
select(departamento,
engagement_2025 = engagement_global_2025),
by = "departamento")
ggplot(comparacion, aes(y = reorder(departamento, engagement_2026))) +
geom_segment(aes(x = engagement_2025, xend = engagement_2026,
yend = departamento),
linewidth = 1.5, color = "grey60",
arrow = arrow(length = unit(0.18, "cm"),
type = "closed")) +
geom_point(aes(x = engagement_2025), color = "#A7BEAE", size = 4) +
geom_point(aes(x = engagement_2026), color = "#B85042", size = 4) +
labs(title = "Engagement global por departamento: 2025 vs. 2026",
x = "Engagement promedio (escala 0–10)", y = NULL) +
theme_minimal()
geom_dumbbell() del paquete ggaltlibrary(ggalt)
ggplot(comparacion, aes(y = reorder(departamento, engagement_2026),
x = engagement_2025, xend = engagement_2026)) +
geom_dumbbell(colour_x = "#A7BEAE",
colour_xend = "#B85042",
size_x = 4, size_xend = 4)
| Función | Paquete | Para qué sirve |
|---|---|---|
relaimpo::calc.relimp() | relaimpo | Importancia relativa por método LMG |
case_when() | dplyr | Categorización de respondientes para eNPS |
geom_tile() | ggplot2 | Heatmaps de métrica × dos categorías |
scale_fill_gradient2() | ggplot2 | Paleta divergente con punto medio anclado |
geom_segment() / geom_dumbbell() | ggplot2 / ggalt | Comparación entre dos puntos en el tiempo |
likert::likert() | likert | Atajo para barras divergentes Likert (opcional) |