Apunte 34 — Simulación de escenarios e incertidumbre

Analítica de Personas · Semestre otoño 2026 · Semana 15 · Prof. René Gempp

1. Por qué no existe "el pronóstico correcto"

El Apunte 33 construyó un modelo que proyecta la dotación año a año. Ese modelo produce números con decimales: 867,3 activos al cabo de dos años; 813,1 contrataciones necesarias. La precisión numérica es una trampa. El modelo es tan bueno como sus supuestos, y los supuestos no se validan contra el futuro porque el futuro no existe todavía.

La respuesta correcta a la incertidumbre no es buscar el pronóstico más preciso, sino mapear el rango de futuros plausibles (Schoemaker, 1995). Lo que nos interesa de un plan de dotación no es que acierte el número exacto — ninguno lo hace — sino que ayude a Carolina a tomar una decisión robusta: una que sea buena a través de los distintos escenarios posibles, no solo óptima para uno.

Del diagnóstico al plan. Un diagnóstico describe lo que fue. Una proyección declara de qué depende lo que será. Un plan de dotación honesto dice: "si la rotación se mantiene en los niveles históricos, necesitaremos ~812 contrataciones; si mejora un 25% gracias al programa de retención, ~734; si empeora, ~886. Te recomendamos comprometer en firme ~734 y ajustar el resto a medida que tengamos datos." Eso es actuar bajo incertidumbre con integridad profesional.

2. Programación funcional con purrr

Simular múltiples escenarios implica correr la misma proyección muchas veces con supuestos distintos. La herramienta del tidyverse para esto es purrr: un paquete de programación funcional que reemplaza los bucles for con funciones limpias y pipelines legibles.

La idea central: map()

map(lista, función) aplica función a cada elemento de lista y devuelve una list con los resultados. El flujo de trabajo típico:

# 1. Definir la grilla de supuestos (un tibble, una fila por escenario)
escenarios <- tibble(
  escenario     = c("Optimista", "Base", "Pesimista"),
  factor_salida = c(0.75,       1.00, 1.25)
)

# 2. map() sobre la columna de supuestos
#    \(f) es la sintaxis moderna de función anónima (R 4.1+)
resultado <- escenarios |>
  mutate(
    P_esc = map(factor_salida, \(f) ajustar_rotacion(P5, f)),
    proy  = map(P_esc, \(Pe) proyectar_dotacion(Pe, n0, horizonte = 2))
  )

# 3. unnest() despliega la list-column a formato largo
simulacion <- resultado |>
  select(escenario, proy) |>
  unnest(proy)
List-columns. Una list-column es una columna de un tibble que contiene listas (o tibbles anidados) en vez de valores atómicos. map() crea list-columns; unnest() las despliega a formato largo. Este patrón — definir grilla → map()unnest() — es la forma idiomática de simulación en el tidyverse.

La familia completa de map()

FunciónDevuelveCuándo usarla
map()listResultado es un objeto complejo (tibble, modelo, gráfico)
map_dbl()vector doubleResultado es un número (el total de activos, la brecha)
map_chr()vector characterResultado es un string
map_dfr()data frame apiladoResultado es un tibble; map_dfr los apila por filas
pmap()listVarios argumentos a la vez (un escenario tiene varias dimensiones)

La función auxiliar: ajustar_rotacion()

Esta función toma la matriz P y un factor, escala la probabilidad de salida de cada nivel y reajusta las demás para que la fila siga sumando 1:

ajustar_rotacion <- function(P5, factor_salida) {
  P2 <- P5
  for (i in 1:4) {
    sal   <- P2[i, "Salida"] * factor_salida
    otras <- P2[i, 1:4]
    if (sum(otras) > 0) otras <- otras / sum(otras) * (1 - sal)
    P2[i, 1:4]      <- otras
    P2[i, "Salida"] <- sal
  }
  P2
}
# factor = 0.75: rotación baja un 25% (programa de retención exitoso)
# factor = 1.00: base (sin cambios respecto al histórico)
# factor = 1.25: rotación sube un 25% (condiciones adversas)

3. Los resultados de la simulación

Una vez que unnest() despliega la simulación, agrupamos por escenario y año para obtener el total de activos proyectados:

totales <- simulacion |>
  group_by(escenario, anio) |>
  summarise(activos = sum(headcount), .groups = "drop")

# Brecha total por escenario a 2 años (demanda = 1.680)
totales |>
  filter(anio == 2) |>
  mutate(brecha = round(1680 - activos))
#> escenario   activos  brecha
#> Optimista      946     734   ← si el programa de retención funciona
#> Base           868     812   ← manteniendo tasas históricas
#> Pesimista      794     886   ← si la rotación sube

El rango es amplio: entre 734 y 886 contrataciones según cómo evolucione la rotación. Una diferencia de 152 personas — no es un detalle.

4. Visualizar la incertidumbre: geom_ribbon() + geom_line()

La forma honesta de comunicar la proyección es mostrar la banda de incertidumbre (optimista–pesimista) junto a la trayectoria base. geom_ribbon() dibuja el área entre dos curvas; geom_line() superpone la línea central.

# 1. Calcular el piso (Pesimista) y techo (Optimista) por año
banda <- totales |>
  group_by(anio) |>
  summarise(ymin = min(activos), ymax = max(activos), .groups = "drop")

linea_base <- totales |> filter(escenario == "Base")

# 2. Graficar
ggplot() +
  geom_ribbon(data = banda, aes(x = anio, ymin = ymin, ymax = ymax),
              fill = "#B85042", alpha = 0.18) +
  geom_line(data = linea_base, aes(x = anio, y = activos),
            color = "#1B2A4A", linewidth = 1.3) +
  geom_point(data = linea_base, aes(x = anio, y = activos),
             color = "#1B2A4A", size = 2.5) +
  geom_hline(yintercept = 1680, linetype = "dashed", color = "#E8B96E") +
  annotate("text", x = 0.1, y = 1705, hjust = 0,
           label = "Demanda objetivo (+40%) = 1.680", color = "#E8B96E") +
  scale_x_continuous(breaks = 0:2) +
  scale_y_continuous(labels = scales::comma) +
  labs(title = "Plan de dotación: rango de escenarios",
       subtitle = "Banda = optimista–pesimista (rotación ±25%) · línea = base",
       x = "Año", y = "Dotación activa proyectada (sin contratar)") +
  theme_minimal()
Anatomía de geom_ribbon(). Sus argumentos esenciales son ymin y ymax: los límites inferior y superior de la banda. El argumento alpha controla la transparencia (0 = invisible, 1 = opaco). Se combina con geom_line() para superponer la trayectoria central: primero el ribbon (fondo), luego la línea (frente).

El resultado muestra que la brecha es la distancia vertical entre la línea punteada (demanda) y la banda sombreada. El mensaje visual es inmediato: la organización necesita contratar masivamente para llegar a la meta, y el rango de incertidumbre es considerable.

5. Tablas ejecutivas con gt

Para la presentación al directorio, la visualización se complementa con una tabla que muestra las cifras por escenario:

# Construir la tabla de brechas por escenario
plan_esc <- totales |>
  filter(anio == 2) |>
  transmute(escenario,
            oferta_interna = round(activos),
            demanda        = 1680,
            contratar      = round(1680 - activos))

# Formatear con gt
plan_esc |>
  gt() |>
  tab_header(title    = "Plan de contratación a 2 años — InnovaCo SpA",
             subtitle = "Objetivo +40% · tres escenarios de rotación") |>
  fmt_number(columns = c(oferta_interna, demanda, contratar), decimals = 0) |>
  cols_label(escenario = "Escenario", oferta_interna = "Oferta interna",
             demanda = "Demanda", contratar = "A contratar (neto)")

El resultado es una tabla limpia con los tres escenarios: Optimista (~734), Base (~812), Pesimista (~886).

6. La decisión robusta bajo incertidumbre

Una decisión robusta es buena a través del rango de futuros plausibles, no solo óptima para el escenario más probable. Para el plan de dotación de InnovaCo, la recomendación operativa emerge directamente de los tres escenarios:

  1. Comprometer en firme el piso del rango: ~734 contrataciones son necesarias incluso en el mejor escenario (si la rotación baja un 25%). Esas se contratan sin condición.
  2. Escalonar el resto según la rotación observada: las ~78–152 restantes (Base minus Optimista; Pesimista minus Base) se contratan trimestralmente según si la rotación efectiva está bajando o subiendo.
  3. Concentrar en la base: el 95% de la brecha está en Operativo y Profesional. No forzar contratación directa de Jefaturas o Gerencias en el corto plazo: el pipeline interno cubre parcialmente esos niveles; la urgencia es en la base.
La analogía de la cadena de suministro (Cappelli, 2008). Un fabricante que enfrenta demanda incierta no pide todo el inventario de una vez: pide el mínimo garantizado y hace pedidos adicionales a medida que llega información. La misma lógica aplica al talento: contratar el piso del rango con certeza y ajustar trimestralmente según la rotación observada. La diferencia con el inventario es que las personas no se devuelven fácilmente si se excede la dotación.

7. Los supuestos que van en el reporte

Declarar los supuestos no es una debilidad; es la marca de un análista competente. El directorio de InnovaCo necesita saber de qué depende el plan para poder cuestionar los supuestos con los que está en desacuerdo.

#SupuestoSi es incorrecto…
1Ausencia de memoria: la probabilidad de salir solo depende del nivel actual, no de la antigüedad.El modelo subestima la rotación de nuevos ingresos (mayor hazard en los primeros años).
2Estacionariedad: las tasas de 2021–2025 seguirán vigentes en 2026–2027.Si hay un ciclo económico adverso o el programa de retención funciona, las tasas cambian.
3Homogeneidad: todos los de un nivel tienen la misma probabilidad de salir o promover.El modelo no distingue alto desempeño de bajo desempeño.
4Gerencia corregida: usamos 92%/8% como supuesto experto, no la estimación empírica de 22 casos.Si la tasa real de salida de gerencia es mayor, la proyección sobreestima la estabilidad en el nivel alto.
5Demanda uniforme: +40% proporcional en todos los niveles.Si las jefaturas no necesitan crecer tanto, la brecha real es menor y el plan de desarrollo interno más urgente.
6Mercado externo: asumimos que los candidatos están disponibles al precio histórico.Un mercado laboral apretado para ingenieros (el perfil de Profesional en InnovaCo) puede hacer la contratación más cara o imposible.
Buena práctica. Cuando presentes el plan al directorio, incluye esta tabla (simplificada) en el memo ejecutivo. Que el directorio te cuestione los supuestos es bueno: significa que está evaluando el plan críticamente. Que lo apruebe sin cuestionarlos es una señal de alerta.

8. Ética de la planificación de dotación

El modelo de Markov que construimos está orientado al crecimiento: proyectar cuántos contratar. Pero la misma herramienta puede usarse para proyectar reducciones. Si la demanda cayera un 20% en vez de crecer un 40%, el análisis diría que sobran ~400 personas. El modelo no distingue entre crecimiento y ajuste — produce números en ambos casos con la misma indiferencia matemática.

Esto plantea preguntas que el analista de personas necesita poder sostener:

La frontera entre planificación y vigilancia. Un modelo de flujo de personal puede volverse una herramienta de control si se combina con datos individuales de desempeño, red social o comunicaciones. Proyectar cuántos operativos necesitaremos es planificación legítima. Identificar cuáles operativos serán más fáciles de "dejar ir" mediante su perfil predictivo de riesgo es una práctica distinta, con consecuencias éticas y legales propias. Esta semilla germina en la Clase 17.

9. Glosario

TérminoSignificado
purrr::map()Aplica una función a cada elemento de una lista; devuelve una lista.
List-columnColumna de un tibble que contiene listas o tibbles anidados.
unnest()Despliega una list-column a formato largo (filas individuales).
geom_ribbon()Dibuja un área sombreada entre dos curvas (ymin y ymax).
EscenarioUna combinación de supuestos sobre el futuro (rotación, crecimiento).
Decisión robustaDecisión que es buena a través del rango de escenarios, no solo óptima para uno.
Planificación por escenariosMapear futuros plausibles en lugar de buscar el pronóstico "correcto".

Referencias

Cappelli, P. (2008). Talent management for the twenty-first century. Harvard Business Review, 86(3), 74–81.

Schoemaker, P. J. H. (1995). Scenario planning: A tool for strategic thinking. Sloan Management Review, 36(2), 25–40.

Sterman, J. D. (2000). Business dynamics: Systems thinking and modeling for a complex world. McGraw-Hill.

Wickham, H., & Grolemund, G. (2017). R for data science. O'Reilly. Disponible en r4ds.had.co.nz.