# Librerías
library(readxl)    # lectura de Excel
library(dplyr)     # manipulación de datos
library(tidyr)     # formato largo para gráficos
library(ggplot2)   # gráficos
library(scales)    # formato de ejes en pesos
library(knitr)     # tablas
library(car)       # VIF, Durbin-Watson
library(lmtest)    # Breusch-Pagan, RESET

# Paleta sobria
azul  <- "#1F3A5F"
gris  <- "#8C8C8C"
tema  <- theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold", color = azul),
        panel.grid.minor = element_blank())

# Formato de pesos en millones
millones <- label_number(scale = 1e-6, suffix = " M", accuracy = 0.1)

A. Introducción

Una empresa necesita estimar el ingreso mensual de sus empleados para apoyar decisiones de Recursos Humanos: definir bandas salariales por cargo, preparar ofertas para nuevos ingresos y detectar salarios que se alejan de lo esperado para un perfil determinado.

Se utiliza la Base 3, con información de 1.470 empleados. La variable dependiente es el ingreso mensual en pesos colombianos y se mantiene durante todo el caso. La pregunta que guía el análisis es si un modelo de regresión lineal construido con el cargo y la trayectoria laboral del empleado predice el ingreso con la precisión suficiente para que Recursos Humanos lo use en esas decisiones.

Los resultados se leen como asociaciones estadísticas. La base es un corte transversal de una sola empresa y no permite afirmar que una variable cause un mayor ingreso.

# Carga de la base
base <- read_excel("base3_ingreso.xlsx", sheet = "Base")

# Nombres sin tildes ni eñes para evitar problemas de codificación
base <- base %>%
  rename(Ingreso     = Ingreso_Mensual,
         Experiencia = `Años_Experiencia`)

# Variables categóricas como factor, con su categoría de referencia
base <- base %>%
  mutate(Cargo  = relevel(factor(Cargo),  ref = "Tecnico_Laboratorio"),
         Genero = relevel(factor(Genero), ref = "F"))

dim(base)
## [1] 1470   24

B. Punto 1

1. Planteamiento de variables y sustentación de la elección

Problema y variable dependiente

  • Usuario: área de Recursos Humanos.
  • Decisión: cuánto debería ganar una persona según el cargo que ocupa y su trayectoria.
  • Variable dependiente (Y): Ingreso, ingreso mensual en pesos colombianos.

Variables seleccionadas

Variable Tipo Uso en el caso Justificación
Experiencia Cuantitativa (años) Regresión simple y múltiple Mayor correlación con el ingreso (r = 0,77). La experiencia total acumulada es el criterio habitual para ubicar a una persona dentro de la escala salarial.
Antiguedad Cuantitativa (años en la empresa) Regresión simple y múltiple Correlación de 0,51 con el ingreso. Recoge la trayectoria dentro de la empresa, distinta de la experiencia total (r = 0,63 entre ambas, una correlación moderada que permite usarlas juntas).
Genero Categórica binaria (referencia: F) Regresión simple y control en la múltiple Es la binaria pertinente para el problema: permite evaluar si existe una diferencia salarial entre hombres y mujeres con el mismo cargo y la misma trayectoria, que es la interpretación responsable que pide el caso. Se usa como control, no como criterio para fijar salarios.
Cargo Categórica, 9 niveles (referencia: Técnico de Laboratorio) Regresión múltiple Define la escala salarial: las medianas van de 2,6 M (Representante de Ventas) a 17,5 M (Gerente). Se elige Técnico de Laboratorio como referencia por ser uno de los cargos más numerosos y de menor salario, lo que facilita leer cada coeficiente como la prima del cargo.

Variables descartadas

Antes de definir el modelo se revisaron las demás variables de la base. Se descartan por una de tres razones: no tienen relación con el ingreso, su información ya está contenida en otra variable o no son pertinentes para predecir.

Variable Evidencia Razón para descartarla
Edad r = 0,50 con el ingreso, pero r = 0,68 con la experiencia Redundante con la experiencia (ver prueba F).
Educacion r = 0,10 con el ingreso Relación débil y no significativa (ver prueba F).
Horas_Extra R² = 0,000; p = 0,82 No se relaciona con el ingreso.
Rendimiento_Laboral Solo toma los valores 3 y 4; p = 0,11 con cargo y experiencia No se relaciona con el nivel del salario, sino con el aumento (r = 0,77 con Porcentaje_aumento_salarial). Al no haber casos con calificación 1 o 2, el modelo no podría usarse para bajo rendimiento.
Porcentaje_aumento_salarial r = −0,03 con el ingreso Es una tasa de cambio, no un nivel. Sin el salario anterior no aporta información sobre el ingreso actual.
Rotación Diferencia significativa, R² = 0,03 Es una consecuencia, no un determinante: probablemente quienes ganan menos se van más. Además, no se conoce de antemano para un nuevo empleado.
Satisfacción, equilibrio vida-trabajo, distancia a casa, capacitaciones |r| < 0,04 Sin relación con el ingreso.
Departamento, Campo_Educación, Estado_Civil – El área de trabajo ya está contenida en el cargo, y el estado civil no es un criterio pertinente para fijar salarios.

Prueba F conjunta: exclusión de edad y educación

Para justificar la exclusión de Edad y Educacion se compara un modelo completo con un modelo restringido que no las incluye. La hipótesis nula es que ambos coeficientes son cero de forma simultánea:

\[H_0: \beta_{Edad} = \beta_{Educacion} = 0 \qquad H_1: \text{al menos uno es distinto de cero}\]

# Modelo completo: con edad y educación
m_completo <- lm(Ingreso ~ Experiencia + Antiguedad + Edad + Educacion +
                   Cargo + Genero, data = base)

# Modelo restringido: sin edad ni educación
m_restringido <- lm(Ingreso ~ Experiencia + Antiguedad + Cargo + Genero,
                    data = base)

# Prueba F conjunta
prueba_f <- anova(m_restringido, m_completo)
prueba_f
Res.Df RSS Df Sum of Sq F Pr(>F)
1458 3.994840e+15 NA NA NA NA
1456 3.988984e+15 2 5.855454e+12 1.068636 0.3437461
# Comparación de ajuste
data.frame(
  Modelo      = c("Completo", "Sin edad ni educación"),
  R2_ajustado = round(c(summary(m_completo)$adj.r.squared,
                        summary(m_restringido)$adj.r.squared), 4),
  AIC         = round(c(AIC(m_completo), AIC(m_restringido)), 0)
)
Modelo R2_ajustado AIC
Completo 0.8764 46287
Sin edad ni educación 0.8764 46285

Interpretación. La prueba da F = 1,07 con p = 0,344, por lo que no se rechaza la hipótesis nula: edad y educación no aportan información, ni por separado ni en conjunto, una vez se conocen la experiencia, la antigüedad, el cargo y el género. Al quitarlas, el R² ajustado se mantiene en 0,876 y el AIC baja de 46.287 a 46.285, lo que indica que el modelo restringido es preferible.

La edad sí tiene relación con el ingreso por sí sola (r = 0,50), pero esa relación pasa por la experiencia: las personas mayores tienen más años trabajados. Cuando la experiencia está en el modelo, la edad ya no agrega nada. Al retirarla, además, la antigüedad pasa de p = 0,05 a p = 0,026, porque deja de repartir su efecto con una variable correlacionada.

Nota metodológica. Esta selección se hace con toda la base como paso exploratorio. En la validación cruzada del punto 8 y del Punto 2, la selección de variables se repite dentro de cada partición de entrenamiento.

2. Análisis exploratorio de las variables del análisis

Calidad de los datos

# Variables del análisis
vars <- c("Ingreso", "Experiencia", "Antiguedad", "Cargo", "Genero")
datos <- base %>% select(all_of(vars))

# Valores faltantes por variable
colSums(is.na(datos))
##     Ingreso Experiencia  Antiguedad       Cargo      Genero 
##           0           0           0           0           0
# Filas duplicadas en la base completa
sum(duplicated(base))
## [1] 0
# Consistencia: la antigüedad en la empresa no puede superar la experiencia total
sum(base$Antiguedad > base$Experiencia)
## [1] 0

La base no tiene valores faltantes ni filas duplicadas. Ningún empleado tiene más años en la empresa que años de experiencia total, de modo que no hay inconsistencias entre ambas variables.

Variables cuantitativas

# Estadísticas descriptivas
resumen <- datos %>%
  select(Ingreso, Experiencia, Antiguedad) %>%
  pivot_longer(everything(), names_to = "Variable", values_to = "Valor") %>%
  group_by(Variable) %>%
  summarise(Media    = mean(Valor),
            Mediana  = median(Valor),
            Desv_Est = sd(Valor),
            Minimo   = min(Valor),
            Maximo   = max(Valor),
            CV       = sd(Valor) / mean(Valor))

kable(resumen, digits = 2, format.args = list(big.mark = "."))
Variable Media Mediana Desv_Est Minimo Maximo CV
Antiguedad 7.01 5 6.13 0 40 0.87
Experiencia 11.28 10 7.78 0 40 0.69
Ingreso 6.502.931.29 4.919.000 4.707.956.78 1.009.000 19.999.000 0.72
# Distribución del ingreso
ggplot(datos, aes(x = Ingreso)) +
  geom_histogram(bins = 40, fill = azul, color = "white") +
  geom_vline(aes(xintercept = mean(Ingreso)), color = gris, linetype = "dashed") +
  geom_vline(aes(xintercept = median(Ingreso)), color = gris) +
  scale_x_continuous(labels = millones) +
  labs(title = "Distribución del ingreso mensual",
       subtitle = "Línea continua: mediana · Línea punteada: media",
       x = "Ingreso mensual", y = "Empleados") +
  tema

Ingreso. La mediana es 4,9 M y la media 6,5 M. La media queda por encima porque la distribución tiene una cola larga a la derecha (asimetría de 1,37): la mayoría gana entre 2,9 M y 8,4 M, y un grupo más pequeño supera los 15 M. Esta forma anticipa que una transformación logarítmica puede ser útil en el Punto 2.

# Distribución de experiencia y antigüedad
datos %>%
  select(Experiencia, Antiguedad) %>%
  pivot_longer(everything(), names_to = "Variable", values_to = "Años") %>%
  ggplot(aes(x = Años)) +
  geom_histogram(binwidth = 2, fill = azul, color = "white") +
  facet_wrap(~ Variable, scales = "free_y") +
  labs(title = "Distribución de la experiencia y la antigüedad",
       x = "Años", y = "Empleados") +
  tema

Experiencia y antigüedad. La experiencia tiene una mediana de 10 años (rango de 0 a 40) y la antigüedad de 5 años (rango de 0 a 40). Ambas son asimétricas a la derecha: la mayoría de los empleados lleva pocos años en la empresa y unos pocos superan los 20.

Valores atípicos

# Criterio del rango intercuartílico: valores por encima de Q3 + 1,5·RIC
limite_sup <- function(x) quantile(x, 0.75) + 1.5 * IQR(x)

data.frame(
  Variable = c("Ingreso", "Experiencia", "Antiguedad"),
  Limite   = c(limite_sup(datos$Ingreso),
               limite_sup(datos$Experiencia),
               limite_sup(datos$Antiguedad)),
  Atipicos = c(sum(datos$Ingreso     > limite_sup(datos$Ingreso)),
               sum(datos$Experiencia > limite_sup(datos$Experiencia)),
               sum(datos$Antiguedad  > limite_sup(datos$Antiguedad)))
)
Variable Limite Atipicos
Ingreso 16581000.0 114
Experiencia 28.5 63
Antiguedad 18.0 104
# ¿A qué cargos pertenecen los ingresos atípicos?
datos %>%
  filter(Ingreso > limite_sup(Ingreso)) %>%
  count(Cargo)
Cargo n
Director_Investigación 40
Gerente 74

Con el criterio del rango intercuartílico se identifican 114 ingresos atípicos (por encima de 16,6 M), 63 en experiencia (más de 28,5 años) y 104 en antigüedad (más de 18 años).

Tratamiento: se conservan todos. Los 114 ingresos atípicos corresponden en su totalidad a Gerentes (74) y Directores de Investigación (40), los dos cargos mejor pagados de la empresa. No son errores de registro, sino la parte alta de la escala salarial. Eliminarlos haría que el modelo no pudiera predecir el salario de esos cargos. Lo mismo ocurre con la experiencia y la antigüedad: valores de 30 o 40 años son plausibles para empleados de mayor edad. Su posible influencia sobre los coeficientes se evalúa en el Punto 2 con la distancia de Cook.

Variables categóricas

# Distribución del cargo
datos %>%
  count(Cargo) %>%
  mutate(Porcentaje = round(100 * n / sum(n), 1)) %>%
  arrange(desc(n))
Cargo n Porcentaje
Ejecutivo_Ventas 326 22.2
Investigador_Cientifico 292 19.9
Tecnico_Laboratorio 259 17.6
Director_Manofactura 145 9.9
Representante_Salud 131 8.9
Gerente 102 6.9
Representante_Ventas 83 5.6
Director_Investigación 80 5.4
Recursos_Humanos 52 3.5
# Distribución del género
datos %>%
  count(Genero) %>%
  mutate(Porcentaje = round(100 * n / sum(n), 1))
Genero n Porcentaje
F 588 40
M 882 60

Los cargos más frecuentes son Ejecutivo de Ventas (22 %), Investigador Científico (20 %) y Técnico de Laboratorio (18 %). Recursos Humanos es el menos frecuente (52 empleados, 3,5 %), por lo que sus estimaciones serán menos precisas. En género, el 60 % son hombres y el 40 % mujeres; ambos grupos tienen tamaño suficiente para compararlos.

3. Análisis de correlaciones y dispersión

Matriz de correlaciones

# Correlación de Pearson entre el ingreso y las variables cuantitativas
# (se incluyen edad y educación como referencia de la exclusión)
round(cor(base[, c("Ingreso", "Experiencia", "Antiguedad", "Edad", "Educacion")]), 3)
##             Ingreso Experiencia Antiguedad  Edad Educacion
## Ingreso       1.000       0.773      0.514 0.498     0.095
## Experiencia   0.773       1.000      0.628 0.680     0.148
## Antiguedad    0.514       0.628      1.000 0.311     0.069
## Edad          0.498       0.680      0.311 1.000     0.208
## Educacion     0.095       0.148      0.069 0.208     1.000

La experiencia tiene la correlación más alta con el ingreso (0,77), seguida de la antigüedad (0,51). Entre los predictores, la correlación experiencia–antigüedad es moderada (0,63), por lo que ambas pueden convivir en un mismo modelo. La edad, en cambio, está más relacionada con la experiencia (0,68) que con el ingreso (0,50), lo que confirma lo encontrado en la prueba F.

Dispersión: ingreso frente a experiencia y antigüedad

# Diagramas de dispersión con recta de regresión
datos %>%
  select(Ingreso, Experiencia, Antiguedad) %>%
  pivot_longer(-Ingreso, names_to = "Variable", values_to = "Años") %>%
  ggplot(aes(x = Años, y = Ingreso)) +
  geom_point(alpha = 0.3, color = azul) +
  geom_smooth(method = "lm", se = FALSE, color = gris, linewidth = 1) +
  facet_wrap(~ Variable, scales = "free_x") +
  scale_y_continuous(labels = millones) +
  labs(title = "Ingreso mensual frente a experiencia y antigüedad",
       x = "Años", y = "Ingreso mensual") +
  tema

Experiencia. La relación es positiva y clara: a más años de experiencia, mayor ingreso. La nube se abre a medida que aumenta la experiencia. Con pocos años los salarios están concentrados, y con más de 15 años hay personas que ganan 5 M y otras que ganan 19 M. Esa diferencia se debe al cargo, y es una señal de que la varianza no será constante (heterocedasticidad), lo que se evalúa en el Punto 2.

Antigüedad. La relación también es positiva pero más débil y dispersa. Muchos empleados con pocos años en la empresa ya tienen ingresos altos, lo que sugiere que el salario depende más de la experiencia que traen que del tiempo que llevan en la empresa.

Comparación entre grupos: cargo

# Ingreso por cargo, ordenado por la mediana
ggplot(datos, aes(x = reorder(Cargo, Ingreso, FUN = median), y = Ingreso)) +
  geom_boxplot(fill = azul, alpha = 0.7, color = "grey30", outlier.color = gris) +
  coord_flip() +
  scale_y_continuous(labels = millones) +
  labs(title = "Ingreso mensual por cargo",
       x = NULL, y = "Ingreso mensual") +
  tema

# Medianas por cargo
datos %>%
  group_by(Cargo) %>%
  summarise(n = n(), Mediana = median(Ingreso), Minimo = min(Ingreso),
            Maximo = max(Ingreso)) %>%
  arrange(Mediana)
Cargo n Mediana Minimo Maximo
Representante_Ventas 83 2579000 1052000 6632000
Tecnico_Laboratorio 259 2886000 1102000 7403000
Investigador_Cientifico 292 2887500 1009000 9724000
Recursos_Humanos 52 3093000 1555000 10725000
Ejecutivo_Ventas 326 6231000 4001000 13872000
Director_Manofactura 145 6447000 4011000 13973000
Representante_Salud 131 6811000 4000000 13966000
Director_Investigación 80 16510000 11031000 19973000
Gerente 102 17454500 11244000 19999000

El cargo separa a los empleados en tres niveles salariales:

  • Nivel bajo (mediana de 2,6 a 3,1 M): Representante de Ventas, Técnico de Laboratorio, Investigador Científico y Recursos Humanos.
  • Nivel medio (6,2 a 6,8 M): Ejecutivo de Ventas, Director de Manufactura y Representante de Salud.
  • Nivel alto (16,5 a 17,5 M): Director de Investigación y Gerente.

Las cajas casi no se superponen entre niveles, lo que indica que el cargo es el principal determinante del ingreso. Dentro de cada cargo sigue habiendo variación (un Gerente gana entre 11,2 y 20 M), que es la que se espera explicar con la experiencia y la antigüedad.

Comparación entre grupos: género

# Ingreso por género
ggplot(datos, aes(x = Genero, y = Ingreso)) +
  geom_boxplot(fill = c(gris, azul), alpha = 0.7, color = "grey30") +
  scale_y_continuous(labels = millones) +
  labs(title = "Ingreso mensual por género", x = NULL, y = "Ingreso mensual") +
  tema

datos %>%
  group_by(Genero) %>%
  summarise(n = n(), Media = mean(Ingreso), Mediana = median(Ingreso))
Genero n Media Mediana
F 588 6686566 5081500
M 882 6380508 4837500

Sin controlar por otras variables, las mujeres tienen un ingreso mediano ligeramente mayor (5,08 M frente a 4,84 M). Las distribuciones son muy parecidas, por lo que se espera que el género explique poco por sí solo. La pregunta relevante, que se aborda en el Punto 2, es si existe alguna diferencia entre hombres y mujeres que ocupan el mismo cargo y tienen la misma trayectoria.

4. Modelación: regresiones lineales simples

Se proponen tres modelos simples, cada uno con un predictor diferente:

  • Modelo 1: Ingreso ~ Experiencia. Es la variable con mayor correlación con el ingreso.
  • Modelo 2: Ingreso ~ Antiguedad. Recoge la trayectoria dentro de la empresa.
  • Modelo 3: Ingreso ~ Genero. Es la variable binaria pertinente, con categoría de referencia F.

El cargo no se usa como regresión simple porque tiene nueve categorías. Se incorpora en los modelos múltiples del Punto 2.

# Modelo 1: experiencia
m1 <- lm(Ingreso ~ Experiencia, data = base)
summary(m1)
## 
## Call:
## lm(formula = Ingreso ~ Experiencia, data = base)
## 
## Residuals:
##       Min        1Q    Median        3Q       Max 
## -11271297  -1750781    -87495   1398604  11539481 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  1227935     137299   8.944   <2e-16 ***
## Experiencia   467658      10021  46.669   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 2988000 on 1468 degrees of freedom
## Multiple R-squared:  0.5974, Adjusted R-squared:  0.5971 
## F-statistic:  2178 on 1 and 1468 DF,  p-value: < 2.2e-16
# Modelo 2: antigüedad
m2 <- lm(Ingreso ~ Antiguedad, data = base)
summary(m2)
## 
## Call:
## lm(formula = Ingreso ~ Antiguedad, data = base)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -9504365 -2498983 -1188091  1392960 15484522 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  3733273     160091   23.32   <2e-16 ***
## Antiguedad    395205      17201   22.98   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 4039000 on 1468 degrees of freedom
## Multiple R-squared:  0.2645, Adjusted R-squared:  0.264 
## F-statistic: 527.9 on 1 and 1468 DF,  p-value: < 2.2e-16
# Modelo 3: género (referencia: F)
m3 <- lm(Ingreso ~ Genero, data = base)
summary(m3)
## 
## Call:
## lm(formula = Ingreso ~ Genero, data = base)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -5557566 -3562258 -1562537  1872198 13618492 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  6686566     194120  34.445   <2e-16 ***
## GeneroM      -306058     250608  -1.221    0.222    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 4707000 on 1468 degrees of freedom
## Multiple R-squared:  0.001015,   Adjusted R-squared:  0.0003345 
## F-statistic: 1.491 on 1 and 1468 DF,  p-value: 0.2222
# Tabla resumen de los tres modelos simples
resumen_modelo <- function(modelo, nombre) {
  s <- summary(modelo)
  data.frame(Modelo      = nombre,
             Intercepto  = coef(modelo)[1],
             Pendiente   = coef(modelo)[2],
             p_valor     = s$coefficients[2, 4],
             R2          = s$r.squared,
             Error_est   = s$sigma)
}

tabla_simples <- rbind(resumen_modelo(m1, "M1: Experiencia"),
                       resumen_modelo(m2, "M2: Antigüedad"),
                       resumen_modelo(m3, "M3: Género (M)"))
rownames(tabla_simples) <- NULL

kable(tabla_simples, digits = c(0, 0, 0, 4, 3, 0),
      format.args = list(big.mark = "."),
      caption = "Resumen de los modelos de regresión simple")
Resumen de los modelos de regresión simple
Modelo Intercepto Pendiente p_valor R2 Error_est
M1: Experiencia 1.227.935 467.658 0.0000 0.597 2.988.386
M2: Antigüedad 3.733.273 395.205 0.0000 0.264 4.039.010
M3: Género (M) 6.686.566 -306.058 0.2222 0.001 4.707.169

La experiencia es, por amplio margen, el predictor simple con mayor capacidad explicativa. La antigüedad es significativa pero explica bastante menos, y el género no muestra una diferencia significativa por sí solo. La interpretación detallada se desarrolla en el punto 5 y la comparación predictiva en el punto 8.

# Rectas ajustadas de los modelos cuantitativos
datos %>%
  select(Ingreso, Experiencia, Antiguedad) %>%
  pivot_longer(-Ingreso, names_to = "Variable", values_to = "Años") %>%
  mutate(Variable = if_else(Variable == "Experiencia",
                            "M1: Experiencia", "M2: Antigüedad")) %>%
  ggplot(aes(x = Años, y = Ingreso)) +
  geom_point(alpha = 0.25, color = gris) +
  geom_smooth(method = "lm", se = TRUE, color = azul, fill = azul, alpha = 0.2) +
  facet_wrap(~ Variable, scales = "free_x") +
  scale_y_continuous(labels = millones) +
  labs(title = "Rectas ajustadas de los modelos simples",
       x = "Años", y = "Ingreso mensual") +
  tema

5. Interpretación de los modelos

# Intervalos de confianza del 95 % para los coeficientes
confint(m1)
##                2.5 %  97.5 %
## (Intercept) 958611.9 1497259
## Experiencia 448001.8  487315
confint(m2)
##                 2.5 %    97.5 %
## (Intercept) 3419241.7 4047304.6
## Antiguedad   361463.7  428945.4
confint(m3)
##               2.5 %    97.5 %
## (Intercept) 6305783 7067349.3
## GeneroM     -797647  185530.3

Coeficientes

  • Modelo 1 (experiencia). El intercepto (1,23 M) es el ingreso estimado para una persona sin experiencia. Cada año adicional de experiencia se asocia, en promedio, con 467.658 pesos más de ingreso mensual (IC 95 %: 448 mil a 487 mil). Una persona con 10 años de experiencia tendría un ingreso estimado de 1,23 M + 10 × 0,47 M ≈ 5,9 M.
  • Modelo 2 (antigüedad). Cada año adicional en la empresa se asocia con 395.205 pesos más al mes (IC 95 %: 361 mil a 429 mil). El intercepto (3,73 M) es el ingreso estimado de alguien que acaba de ingresar. Es más alto que en el Modelo 1 porque los nuevos empleados ya traen experiencia de otras empresas.
  • Modelo 3 (género). El intercepto (6,69 M) es el ingreso promedio de las mujeres, que son la categoría de referencia. Los hombres ganan en promedio 306.058 pesos menos (IC 95 %: −798 mil a +186 mil). El intervalo incluye el cero, así que la diferencia no es concluyente.

Significancia estadística

En los modelos 1 y 2 el p-valor de la pendiente es menor que 0,001, por lo que se rechaza la hipótesis de que la pendiente es cero: experiencia y antigüedad tienen una relación estadísticamente significativa con el ingreso. En el modelo 3 el p-valor es 0,222, por lo que no hay evidencia de una diferencia de ingreso entre hombres y mujeres cuando no se considera ninguna otra variable.

R²

  • Modelo 1: la experiencia explica el 59,7 % de la variabilidad del ingreso. Es un poder explicativo alto para un solo predictor.
  • Modelo 2: la antigüedad explica el 26,4 %. Comparte parte de su información con la experiencia (r = 0,63), pero por sí sola explica bastante menos.
  • Modelo 3: el género explica el 0,1 %. En la práctica no explica nada.

Asociación estadística frente a relación causal

Los coeficientes miden asociaciones, no efectos causales. Que cada año de experiencia se asocie con 468 mil pesos más no significa que sumar un año de experiencia a una persona le suba el salario en esa cantidad. Hay tres razones:

  1. Variables omitidas. Las personas con más experiencia ocupan cargos de mayor nivel. Parte de los 468 mil pesos corresponde al cargo y no a la experiencia en sí. En el Punto 2, cuando se controla por cargo, el efecto de la experiencia baja a unos 189 mil pesos por año.
  2. El caso de la edad. La edad tiene una correlación de 0,50 con el ingreso, pero cumplir años no aumenta el salario. La relación existe porque las personas mayores acumulan más experiencia, y desaparece cuando la experiencia está en el modelo (prueba F del punto 1).
  3. El caso del género. El Modelo 3 muestra que las mujeres ganan un poco más, pero eso no indica que el género determine el salario en ninguna dirección. Para evaluar una posible brecha hay que comparar personas con el mismo cargo y la misma trayectoria, lo que se hace en el Punto 2.

Además, la base es un corte transversal de una sola empresa: observa a distintas personas en un mismo momento, no a la misma persona a lo largo del tiempo.

6. Validación de supuestos

Los supuestos se evalúan sobre el Modelo 1 (experiencia), que es el mejor modelo simple. El diagnóstico de los modelos múltiples se hace en el Punto 2.

# Gráficos de diagnóstico del Modelo 1
par(mfrow = c(2, 2))
plot(m1, col = adjustcolor(azul, 0.4), pch = 16)

par(mfrow = c(1, 1))
# Linealidad: prueba RESET de Ramsey (H0: la forma funcional lineal es adecuada)
resettest(m1)
## 
##  RESET test
## 
## data:  m1
## RESET = 19.966, df1 = 2, df2 = 1466, p-value = 2.786e-09
# Varianza constante: prueba de Breusch-Pagan (H0: homocedasticidad)
bptest(m1)
## 
##  studentized Breusch-Pagan test
## 
## data:  m1
## BP = 286.05, df = 1, p-value < 2.2e-16
# Normalidad de los errores: prueba de Shapiro-Wilk (H0: los errores son normales)
shapiro.test(residuals(m1))
## 
##  Shapiro-Wilk normality test
## 
## data:  residuals(m1)
## W = 0.9819, p-value = 1.227e-12
# Independencia: prueba de Durbin-Watson (H0: no hay autocorrelación)
# (el p-valor se obtiene por remuestreo y puede variar ligeramente entre ejecuciones)
durbinWatsonTest(m1)
##  lag Autocorrelation D-W Statistic p-value
##    1     0.003275237      1.993359   0.904
##  Alternative hypothesis: rho != 0
# Observaciones influyentes: distancia de Cook
cook <- cooks.distance(m1)
c(Mayores_4_n = sum(cook > 4 / nrow(base)), Maxima = round(max(cook), 4))
## Mayores_4_n      Maxima 
##    139.0000      0.0587
Supuesto Prueba Resultado Conclusión
Linealidad RESET p < 0,001 No se cumple del todo: hay curvatura.
Varianza constante Breusch-Pagan BP = 286, p < 0,001 No se cumple: heterocedasticidad.
Normalidad Shapiro-Wilk W = 0,982, p < 0,001 No se cumple estrictamente.
Independencia Durbin-Watson DW = 1,99, p > 0,05 Se cumple.
Influyentes Distancia de Cook Máxima = 0,059 Ninguna observación es influyente por sí sola.

Linealidad. La prueba RESET rechaza la forma lineal. En el gráfico de residuos frente a valores ajustados, la línea de tendencia no es completamente plana: queda por debajo de cero en los valores ajustados bajos y sube por encima alrededor de 10 M, donde empiezan a aparecer los salarios de directores y gerentes. La relación no es exactamente una recta porque los saltos de cargo (de técnico a director, por ejemplo) no ocurren de forma gradual con la experiencia.

Varianza constante. Es el problema más importante. Los residuos forman un embudo: con poca experiencia los errores son pequeños y con mucha experiencia son grandes. Es lo que ya se veía en el diagrama de dispersión: con más de 15 años de experiencia conviven personas que ganan 5 M y otras que ganan 19 M. Consecuencia: los coeficientes siguen siendo insesgados, pero los errores estándar y los intervalos de predicción no son confiables. El intervalo es demasiado ancho para perfiles con poca experiencia y demasiado estrecho para perfiles con mucha.

Normalidad. Con 1.470 observaciones, Shapiro-Wilk detecta desviaciones pequeñas. El gráfico Q-Q muestra que los residuos se alejan de la diagonal en las colas. Por el tamaño de la muestra, las pruebas t y F siguen siendo aproximadamente válidas (teorema del límite central), pero los intervalos de predicción, que sí dependen de la normalidad, pueden ser imprecisos.

Independencia. El estadístico de Durbin-Watson es cercano a 2 y no hay autocorrelación. Es lo esperado en un corte transversal en el que cada fila es un empleado distinto.

Observaciones influyentes. Hay 139 observaciones por encima del umbral 4/n, pero la distancia de Cook máxima es 0,059, muy lejos del valor de 1 que indicaría una observación capaz de cambiar los resultados por sí sola. Esto confirma la decisión de conservar los atípicos.

7. Transformaciones

La heterocedasticidad y la asimetría del ingreso justifican probar una transformación logarítmica de la respuesta. Con el logaritmo, la experiencia pasa a tener un efecto porcentual en lugar de un efecto fijo en pesos, lo que es coherente con la forma de embudo: un año adicional suma más pesos a quien ya gana más.

# Modelo 1 con logaritmo del ingreso
m1_log <- lm(log(Ingreso) ~ Experiencia, data = base)
summary(m1_log)
## 
## Call:
## lm(formula = log(Ingreso) ~ Experiencia, data = base)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1.50265 -0.31612  0.01573  0.32857  1.29804 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 14.746880   0.020519  718.68   <2e-16 ***
## Experiencia  0.063246   0.001498   42.23   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.4466 on 1468 degrees of freedom
## Multiple R-squared:  0.5485, Adjusted R-squared:  0.5482 
## F-statistic:  1784 on 1 and 1468 DF,  p-value: < 2.2e-16
# Supuestos del modelo transformado
bptest(m1_log)
## 
##  studentized Breusch-Pagan test
## 
## data:  m1_log
## BP = 21.548, df = 1, p-value = 3.451e-06
shapiro.test(residuals(m1_log))
## 
##  Shapiro-Wilk normality test
## 
## data:  residuals(m1_log)
## W = 0.99594, p-value = 0.0005788
# Residuos frente a ajustados: modelo original y transformado
diag <- rbind(
  data.frame(Modelo = "Original", Ajustado = fitted(m1),
             Residuo = rstandard(m1)),
  data.frame(Modelo = "Logaritmo", Ajustado = fitted(m1_log),
             Residuo = rstandard(m1_log)))
diag$Modelo <- factor(diag$Modelo, levels = c("Original", "Logaritmo"))

ggplot(diag, aes(x = Ajustado, y = Residuo)) +
  geom_point(alpha = 0.3, color = azul) +
  geom_hline(yintercept = 0, color = gris, linetype = "dashed") +
  facet_wrap(~ Modelo, scales = "free_x") +
  labs(title = "Residuos estandarizados frente a valores ajustados",
       x = "Valor ajustado", y = "Residuo estandarizado") +
  tema

Interpretación del modelo transformado. Cada año adicional de experiencia se asocia con un ingreso 6,5 % mayor en promedio (exp(0,0632) − 1 = 0,065). El intercepto corresponde a un ingreso de exp(14,75) ≈ 2,5 M para alguien sin experiencia.

Efecto sobre los supuestos. El estadístico de Breusch-Pagan baja de 286 a 21,5 y el de Shapiro-Wilk sube de 0,982 a 0,996. Ambas pruebas siguen rechazando, pero los problemas son mucho menores y el embudo de residuos prácticamente desaparece.

El R² del modelo transformado (0,549) no se compara con el del original (0,597), porque miden la variabilidad de respuestas distintas. La comparación correcta se hace con el MAE en pesos, en el punto 8.

8. Predicción hipotética y métricas de evaluación

Validación cruzada y MAE

Los cuatro modelos y la referencia se evalúan con las mismas cinco particiones. En cada partición se ajusta el modelo con el 80 % de los datos y se mide el error absoluto medio (MAE) sobre el 20 % restante. Para el modelo con logaritmo, la predicción se devuelve a pesos con la función exponencial antes de calcular el error.

# Particiones fijas para todos los modelos
set.seed(123)
k <- 5
particion <- sample(rep(1:k, length.out = nrow(base)))

# Función de validación cruzada: devuelve el MAE de cada partición en pesos
mae_cv <- function(formula, logaritmo = FALSE) {
  sapply(1:k, function(i) {
    entrenamiento <- base[particion != i, ]
    prueba        <- base[particion == i, ]
    modelo        <- lm(formula, data = entrenamiento)
    prediccion    <- predict(modelo, newdata = prueba)
    if (logaritmo) prediccion <- exp(prediccion)   # volver a pesos
    mean(abs(prueba$Ingreso - prediccion))
  })
}

# Referencia: predecir la mediana del entrenamiento
mae_mediana <- sapply(1:k, function(i) {
  mediana <- median(base$Ingreso[particion != i])
  mean(abs(base$Ingreso[particion == i] - mediana))
})

# MAE por partición y promedio
resultados <- rbind(
  "Referencia: mediana"   = mae_mediana,
  "M1: Experiencia"       = mae_cv(Ingreso ~ Experiencia),
  "M2: Antigüedad"        = mae_cv(Ingreso ~ Antiguedad),
  "M3: Género"            = mae_cv(Ingreso ~ Genero),
  "M1 log: Experiencia"   = mae_cv(log(Ingreso) ~ Experiencia, logaritmo = TRUE)
)

tabla_cv <- data.frame(Modelo     = rownames(resultados),
                       MAE        = rowMeans(resultados),
                       Desv_Est   = apply(resultados, 1, sd),
                       Mejora_vs_mediana = 1 - rowMeans(resultados) / mean(mae_mediana))
rownames(tabla_cv) <- NULL

kable(tabla_cv, digits = c(0, 0, 0, 3), format.args = list(big.mark = "."),
      caption = "MAE promedio en validación cruzada de 5 particiones (pesos)")
MAE promedio en validación cruzada de 5 particiones (pesos)
Modelo MAE Desv_Est Mejora_vs_mediana
Referencia: mediana 3.347.858 207.527 0.000
M1: Experiencia 2.218.163 140.867 0.337
M2: Antigüedad 2.963.646 135.804 0.115
M3: Género 3.644.874 173.204 -0.089
M1 log: Experiencia 2.300.671 192.244 0.313
# Comparación visual del MAE
ggplot(tabla_cv, aes(x = reorder(Modelo, -MAE), y = MAE)) +
  geom_col(fill = ifelse(tabla_cv$Modelo == "M1: Experiencia", azul, gris)) +
  geom_text(aes(label = millones(MAE)), hjust = -0.1, size = 3.5) +
  coord_flip() +
  scale_y_continuous(labels = millones, expand = expansion(mult = c(0, 0.15))) +
  labs(title = "Error absoluto medio en validación cruzada",
       x = NULL, y = "MAE") +
  tema

Resultados.

  • Referencia (mediana): sin usar ninguna variable, el error promedio es de 3,35 M. Es el umbral mínimo que cualquier modelo debe superar.
  • M1 (experiencia): MAE de 2,22 M, un 34 % menor que la referencia. Es el mejor modelo y su error es estable entre particiones (desviación de 141 mil pesos).
  • M2 (antigüedad): MAE de 2,96 M, solo un 11 % mejor que la referencia.
  • M3 (género): MAE de 3,64 M, peor que predecir la mediana. Predice casi el mismo valor para todos (el promedio de cada grupo), y el promedio queda lejos de la mayoría de los salarios por la asimetría.
  • M1 con logaritmo: MAE de 2,30 M, algo peor que el modelo original. Al devolver la predicción a pesos con la exponencial se obtiene un valor cercano a la mediana condicional, que queda por debajo de los salarios altos.

Elección del mejor modelo simple: Modelo 1 (experiencia), en su versión original.

  • Precisión: tiene el menor MAE de los cuatro modelos.
  • Coherencia: el signo positivo es el esperado y el efecto es estable (intervalo de confianza estrecho).
  • Interpretabilidad: el coeficiente se explica en pesos (468 mil por año), lo que es más fácil de comunicar a Recursos Humanos que un efecto porcentual.

La versión logarítmica predice un poco peor en promedio, pero cumple mejor los supuestos. Por eso se usa para construir los intervalos de predicción.

Predicción para dos perfiles hipotéticos

Se plantean dos casos que Recursos Humanos podría enfrentar:

  • Escenario A: un candidato con 5 años de experiencia.
  • Escenario B: un candidato con 20 años de experiencia.
# Perfiles hipotéticos
escenarios <- data.frame(Experiencia = c(5, 20))

# Modelo original: predicción e intervalo de predicción del 95 %
pred_lineal <- predict(m1, newdata = escenarios, interval = "prediction", level = 0.95)

# Modelo logarítmico: predicción e intervalo devueltos a pesos
pred_log <- exp(predict(m1_log, newdata = escenarios, interval = "prediction", level = 0.95))

data.frame(
  Escenario   = c("A: 5 años", "B: 20 años"),
  Modelo      = rep(c("Original", "Logaritmo"), each = 2),
  Prediccion  = c(pred_lineal[, "fit"], pred_log[, "fit"]),
  Limite_inf  = c(pred_lineal[, "lwr"], pred_log[, "lwr"]),
  Limite_sup  = c(pred_lineal[, "upr"], pred_log[, "upr"])
) %>%
  kable(digits = 0, format.args = list(big.mark = "."),
        caption = "Predicciones e intervalos de predicción del 95 % (pesos)")
Predicciones e intervalos de predicción del 95 % (pesos)
Escenario Modelo Prediccion Limite_inf Limite_sup
A: 5 años Original 3.566.227 -2.299.027 9.431.481
B: 20 años Original 10.581.104 4.714.644 16.447.563
A: 5 años Logaritmo 3.481.976 1.449.241 8.365.867
B: 20 años Logaritmo 8.991.612 3.741.744 21.607.325

Interpretación.

  • Escenario A. Ambos modelos predicen alrededor de 3,5 M. El intervalo del modelo original llega a −2,3 M, lo que es imposible para un salario. Esto ocurre porque el modelo supone que el error tiene el mismo tamaño para todos, cuando en realidad es menor con poca experiencia. El modelo logarítmico da un intervalo coherente (1,45 a 8,37 M), aunque sigue siendo amplio.
  • Escenario B. El modelo original predice 10,6 M y el logarítmico 9,0 M. El intervalo del modelo logarítmico es asimétrico y más ancho hacia arriba (hasta 21,6 M), lo que refleja que con 20 años de experiencia la persona puede ser técnico o gerente.

Qué significa el error en la práctica. Un MAE de 2,2 M equivale al 45 % del ingreso mediano de la empresa (4,9 M). Para fijar una oferta salarial concreta, este error es demasiado grande: los intervalos van de 1,5 a 8,4 M para el mismo perfil. El modelo simple sirve como una primera aproximación del rango salarial según la experiencia, pero no es suficiente para decidir un salario. La amplitud de los intervalos se debe a que no considera el cargo, que es el principal determinante del ingreso. Por eso, en el Punto 2 se incorpora el cargo en un modelo múltiple.

C. Punto 2

1. Propuesta de modelos múltiples

Se proponen dos modelos que responden a dos momentos distintos de una decisión de Recursos Humanos:

Modelo Predictores Uso Categórica y referencia
MM1: perfil sin cargo Experiencia + Antigüedad + Género Estimar el ingreso de una persona antes de definir el cargo, solo con su trayectoria. Género (referencia: F)
MM2: perfil con cargo Experiencia + Antigüedad + Cargo + Género Estimar el ingreso de una persona para un cargo determinado. Cargo (referencia: Técnico de Laboratorio) y Género (referencia: F)

Justificación de los predictores.

  • Experiencia es el mejor predictor simple (R² = 0,597) y entra en ambos modelos.
  • Antigüedad recoge la trayectoria dentro de la empresa. Su correlación con la experiencia es moderada (0,63), así que pueden estar juntas sin que una anule a la otra.
  • Cargo es el principal determinante del ingreso según el análisis exploratorio. Las medianas van de 2,6 M a 17,5 M y las cajas casi no se superponen entre niveles.
  • Género se incluye como control. No sirve para predecir, pero permite responder si existe una diferencia salarial entre hombres y mujeres con el mismo cargo y la misma trayectoria.
  • Edad y educación quedan fuera por la prueba F del Punto 1 (p = 0,344).

Comparar MM1 con MM2 muestra cuánto vale conocer el cargo para predecir el salario.

# MM1: perfil sin cargo
mm1 <- lm(Ingreso ~ Experiencia + Antiguedad + Genero, data = base)
summary(mm1)
## 
## Call:
## lm(formula = Ingreso ~ Experiencia + Antiguedad + Genero, data = base)
## 
## Residuals:
##       Min        1Q    Median        3Q       Max 
## -10789449  -1760924    -60161   1363240  11390209 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  1148907     171792   6.688 3.21e-11 ***
## Experiencia   449700      12873  34.932  < 2e-16 ***
## Antiguedad     36562      16339   2.238   0.0254 *  
## GeneroM        42264     159109   0.266   0.7906    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 2985000 on 1466 degrees of freedom
## Multiple R-squared:  0.5988, Adjusted R-squared:  0.5979 
## F-statistic: 729.2 on 3 and 1466 DF,  p-value: < 2.2e-16
# MM2: perfil con cargo (referencias: Técnico de Laboratorio y F)
mm2 <- lm(Ingreso ~ Experiencia + Antiguedad + Cargo + Genero, data = base)
summary(mm2)
## 
## Call:
## lm(formula = Ingreso ~ Experiencia + Antiguedad + Cargo + Genero, 
##     data = base)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -5206138 -1093729   -82388   992650  5487063 
## 
## Coefficients:
##                               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)                  1.577e+06  1.328e+05  11.872  < 2e-16 ***
## Experiencia                  1.888e+05  8.561e+03  22.058  < 2e-16 ***
## Antiguedad                   2.031e+04  9.111e+03   2.229 0.025937 *  
## CargoDirector_Investigación  1.010e+07  2.349e+05  42.968  < 2e-16 ***
## CargoDirector_Manofactura    3.065e+06  1.764e+05  17.377  < 2e-16 ***
## CargoEjecutivo_Ventas        2.999e+06  1.405e+05  21.352  < 2e-16 ***
## CargoGerente                 1.059e+07  2.308e+05  45.867  < 2e-16 ***
## CargoInvestigador_Cientifico 1.413e+02  1.414e+05   0.001 0.999203    
## CargoRecursos_Humanos        8.913e+05  2.516e+05   3.543 0.000408 ***
## CargoRepresentante_Salud     3.023e+06  1.838e+05  16.450  < 2e-16 ***
## CargoRepresentante_Ventas    1.640e+04  2.104e+05   0.078 0.937877    
## GeneroM                      1.680e+05  8.863e+04   1.896 0.058184 .  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 1655000 on 1458 degrees of freedom
## Multiple R-squared:  0.8773, Adjusted R-squared:  0.8764 
## F-statistic: 947.8 on 11 and 1458 DF,  p-value: < 2.2e-16

2. Interpretación y selección paso a paso

Coeficientes, R² ajustado y significancia

# Coeficientes de ambos modelos en una sola tabla
coeficientes <- function(modelo, nombre) {
  s <- summary(modelo)$coefficients
  data.frame(Modelo = nombre, Variable = rownames(s),
             Coeficiente = s[, 1], p_valor = s[, 4], row.names = NULL)
}

rbind(coeficientes(mm1, "MM1"), coeficientes(mm2, "MM2")) %>%
  kable(digits = c(0, 0, 0, 4), format.args = list(big.mark = "."),
        caption = "Coeficientes de los modelos múltiples (pesos)")
Coeficientes de los modelos múltiples (pesos)
Modelo Variable Coeficiente p_valor
MM1 (Intercept) 1.148.907 0.0000
MM1 Experiencia 449.700 0.0000
MM1 Antiguedad 36.562 0.0254
MM1 GeneroM 42.264 0.7906
MM2 (Intercept) 1.576.512 0.0000
MM2 Experiencia 188.839 0.0000
MM2 Antiguedad 20.312 0.0259
MM2 CargoDirector_Investigación 10.095.004 0.0000
MM2 CargoDirector_Manofactura 3.065.127 0.0000
MM2 CargoEjecutivo_Ventas 2.999.153 0.0000
MM2 CargoGerente 10.585.619 0.0000
MM2 CargoInvestigador_Cientifico 141 0.9992
MM2 CargoRecursos_Humanos 891.314 0.0004
MM2 CargoRepresentante_Salud 3.022.825 0.0000
MM2 CargoRepresentante_Ventas 16.399 0.9379
MM2 GeneroM 168.030 0.0582
# Ajuste global
data.frame(Modelo      = c("MM1: sin cargo", "MM2: con cargo"),
           R2          = c(summary(mm1)$r.squared, summary(mm2)$r.squared),
           R2_ajustado = c(summary(mm1)$adj.r.squared, summary(mm2)$adj.r.squared),
           AIC         = c(AIC(mm1), AIC(mm2))) %>%
  kable(digits = c(0, 3, 3, 0), format.args = list(big.mark = "."))
Modelo R2 R2_ajustado AIC
MM1: sin cargo 0.599 0.598 48.011
MM2: con cargo 0.877 0.876 46.285

MM1 (sin cargo). El R² ajustado es 0,598, prácticamente el mismo que el de la regresión simple con experiencia (0,597).

  • Experiencia: cada año adicional se asocia con 449.700 pesos más de ingreso, manteniendo constantes la antigüedad y el género (p < 0,001).
  • Antigüedad: cada año en la empresa suma 36.562 pesos (p = 0,025). Es significativa, pero su aporte es pequeño frente al de la experiencia.
  • Género: los hombres ganan 42.264 pesos más que las mujeres con la misma trayectoria (p = 0,79). No hay diferencia.

Sin el cargo, agregar antigüedad y género no mejora lo que ya explicaba la experiencia.

MM2 (con cargo). El R² ajustado sube a 0,876: el modelo explica el 88 % de la variabilidad del ingreso. El AIC baja de 48.011 a 46.285.

  • Experiencia: el efecto baja de 449.700 a 188.839 pesos por año (p < 0,001). Más de la mitad de lo que el MM1 atribuía a la experiencia era en realidad el cargo, porque las personas con más experiencia ocupan cargos mejor pagados.
  • Antigüedad: 20.312 pesos por año en la empresa (p = 0,026).
  • Cargo, comparado con un Técnico de Laboratorio con la misma experiencia, antigüedad y género:
    • Gerente: +10,59 M. Director de Investigación: +10,10 M.
    • Director de Manufactura (+3,07 M), Representante de Salud (+3,02 M) y Ejecutivo de Ventas (+3,00 M).
    • Recursos Humanos: +0,89 M.
    • Representante de Ventas (p = 0,94) e Investigador Científico (p = 0,99) no se diferencian del técnico.
  • Género: los hombres ganan 168.030 pesos más que las mujeres con el mismo cargo y la misma trayectoria, pero no es significativo al 5 % (p = 0,058). No hay evidencia suficiente de una brecha salarial en esta empresa. Este resultado es una asociación: no descarta que existan diferencias en el acceso a los cargos mejor pagados, que el modelo toma como dadas.
# Significancia conjunta del cargo: MM2 frente a MM1
anova(mm1, mm2)
Res.Df RSS Df Sum of Sq F Pr(>F)
1466 1.306465e+16 NA NA NA NA
1458 3.994840e+15 8 9.06981e+15 413.777 0

Algunos niveles del cargo no son significativos por separado, pero el cargo como variable sí lo es (F = 414, p < 0,001). Con una variable de varias categorías, la significancia se evalúa en conjunto.

Selección paso a paso con AIC

La selección se aplica a los dos modelos y, además, a un modelo ampliado que incluye edad y educación, para verificar si el AIC confirma la exclusión decidida con la prueba F.

# Selección paso a paso en ambas direcciones
mm1_step <- step(mm1, direction = "both", trace = 0)
mm2_step <- step(mm2, direction = "both", trace = 0)

# Selección desde el modelo ampliado (con edad y educación)
ampliado      <- lm(Ingreso ~ Experiencia + Antiguedad + Edad + Educacion + Cargo + Genero,
                    data = base)
ampliado_step <- step(ampliado, direction = "both", trace = 1)
## Start:  AIC=42113.06
## Ingreso ~ Experiencia + Antiguedad + Edad + Educacion + Cargo + 
##     Genero
## 
##               Df  Sum of Sq        RSS   AIC
## - Edad         1 2.0814e+12 3.9911e+15 42112
## - Educacion    1 2.9639e+12 3.9919e+15 42112
## <none>                      3.9890e+15 42113
## - Genero       1 9.6308e+12 3.9986e+15 42115
## - Antiguedad   1 1.0558e+13 3.9995e+15 42115
## - Experiencia  1 9.2038e+14 4.9094e+15 42416
## - Cargo        8 9.0371e+15 1.3026e+16 43837
## 
## Step:  AIC=42111.83
## Ingreso ~ Experiencia + Antiguedad + Educacion + Cargo + Genero
## 
##               Df  Sum of Sq        RSS   AIC
## - Educacion    1 3.7741e+12 3.9948e+15 42111
## <none>                      3.9911e+15 42112
## + Edad         1 2.0814e+12 3.9890e+15 42113
## - Genero       1 9.7006e+12 4.0008e+15 42113
## - Antiguedad   1 1.3108e+13 4.0042e+15 42115
## - Experiencia  1 1.3306e+15 5.3217e+15 42533
## - Cargo        8 9.0622e+15 1.3053e+16 43838
## 
## Step:  AIC=42111.22
## Ingreso ~ Experiencia + Antiguedad + Cargo + Genero
## 
##               Df  Sum of Sq        RSS   AIC
## <none>                      3.9948e+15 42111
## + Educacion    1 3.7741e+12 3.9911e+15 42112
## + Edad         1 2.8916e+12 3.9919e+15 42112
## - Genero       1 9.8475e+12 4.0047e+15 42113
## - Antiguedad   1 1.3619e+13 4.0085e+15 42114
## - Experiencia  1 1.3332e+15 5.3280e+15 42533
## - Cargo        8 9.0698e+15 1.3065e+16 43837
# Fórmulas finales
formula(mm1_step)
## Ingreso ~ Experiencia + Antiguedad
formula(mm2_step)
## Ingreso ~ Experiencia + Antiguedad + Cargo + Genero
formula(ampliado_step)
## Ingreso ~ Experiencia + Antiguedad + Cargo + Genero

Qué cambia.

  • MM1: el stepwise elimina el género, que no aporta información cuando no se conoce el cargo. El modelo final es Ingreso ~ Experiencia + Antiguedad.
  • MM2: el stepwise no elimina ninguna variable. Quitar el género sube el AIC en 2 puntos y quitar la antigüedad en 3. Son aportes pequeños, pero mejoran el balance entre ajuste y complejidad.
  • Modelo ampliado: el stepwise elimina primero la edad y después la educación, y llega exactamente al MM2. Esto confirma por otra vía la decisión tomada con la prueba F.

El stepwise también se repite dentro de cada partición de la validación cruzada (punto 5), como recomienda el enunciado. Desde el modelo ampliado llegó al MM2 en cuatro de las cinco particiones. En la restante cambió el género por la educación, lo que indica que ambas variables están en el límite.

3. Evaluación de supuestos

El diagnóstico se hace sobre el MM2, que es el modelo con mejor desempeño.

# Gráficos de diagnóstico del MM2
par(mfrow = c(2, 2))
plot(mm2, col = adjustcolor(azul, 0.4), pch = 16)

par(mfrow = c(1, 1))
# Linealidad
resettest(mm2)
## 
##  RESET test
## 
## data:  mm2
## RESET = 34.966, df1 = 2, df2 = 1456, p-value = 1.472e-15
# Varianza constante
bptest(mm2)
## 
##  studentized Breusch-Pagan test
## 
## data:  mm2
## BP = 279.57, df = 11, p-value < 2.2e-16
# Normalidad de los errores
shapiro.test(residuals(mm2))
## 
##  Shapiro-Wilk normality test
## 
## data:  residuals(mm2)
## W = 0.99416, p-value = 1.577e-05
# Multicolinealidad (GVIF para variables con varias categorías)
vif(mm2)
##                 GVIF Df GVIF^(1/(2*Df))
## Experiencia 2.378815  1        1.542341
## Antiguedad  1.670365  1        1.292426
## Cargo       1.808657  8        1.037731
## Genero      1.011522  1        1.005744
# Independencia
durbinWatsonTest(mm2)
##  lag Autocorrelation D-W Statistic p-value
##    1     -0.01098858      2.021443   0.716
##  Alternative hypothesis: rho != 0
# Observaciones influyentes
cook_mm2 <- cooks.distance(mm2)
c(Mayores_4_n = sum(cook_mm2 > 4 / nrow(base)), Maxima = round(max(cook_mm2), 4))
## Mayores_4_n      Maxima 
##    113.0000      0.0197
# Dispersión de los residuos por cargo: origen de la heterocedasticidad
data.frame(Cargo = base$Cargo, Residuo = residuals(mm2)) %>%
  ggplot(aes(x = reorder(Cargo, Residuo, FUN = sd), y = Residuo)) +
  geom_boxplot(fill = azul, alpha = 0.7, color = "grey30", outlier.color = gris) +
  geom_hline(yintercept = 0, color = gris, linetype = "dashed") +
  coord_flip() +
  scale_y_continuous(labels = millones) +
  labs(title = "Residuos del MM2 por cargo",
       subtitle = "Ordenados de menor a mayor dispersión",
       x = NULL, y = "Residuo (ingreso observado − estimado)") +
  tema

Supuesto Prueba Resultado Conclusión
Linealidad RESET p < 0,001 No se cumple del todo.
Varianza constante Breusch-Pagan BP = 279,6, p < 0,001 No se cumple: heterocedasticidad por cargo.
Normalidad Shapiro-Wilk W = 0,994, p < 0,001 Desviación leve.
Multicolinealidad GVIF Máximo 2,38 (experiencia) Sin problema.
Independencia Durbin-Watson DW = 2,02, p > 0,05 Se cumple.
Influyentes Distancia de Cook Máxima = 0,020 Ninguna observación influyente.

Linealidad. La prueba RESET sigue rechazando, aunque la curvatura del gráfico de residuos es mucho menor que en el modelo simple: el cargo absorbió los saltos de nivel salarial. Lo que queda indica que el efecto de la experiencia no es exactamente igual en todos los años ni en todos los cargos.

Varianza constante. Es el problema principal, y el gráfico por cargo muestra de dónde viene. En los cargos de menor salario (Representante de Ventas, Investigador Científico, Técnico de Laboratorio) la desviación de los residuos es de 1,0 a 1,2 M. En los demás cargos es de 1,9 a 2,2 M. Consecuencia: los coeficientes siguen siendo insesgados, pero el intervalo de predicción tiene el mismo ancho para todos (unos 6,5 M). Es demasiado amplio para un técnico y demasiado estrecho para un director (ver punto 5).

Normalidad. Con el cargo en el modelo, los residuos son casi normales (W = 0,994). El gráfico Q-Q solo se separa de la diagonal en los extremos. Con 1.470 observaciones, las pruebas t y F son válidas.

Multicolinealidad. Todos los GVIF son menores que 2,4, lejos del umbral de 5. Los coeficientes de experiencia y antigüedad son estables y se pueden interpretar por separado.

Independencia. Durbin-Watson es cercano a 2: cada fila es un empleado distinto y no hay dependencia entre observaciones.

Observaciones influyentes. La distancia de Cook máxima es 0,020. Ningún empleado cambia los resultados por sí solo, lo que respalda la decisión de conservar los salarios atípicos.

4. Transformación de la variable dependiente

La heterocedasticidad justifica probar el logaritmo del ingreso. Con el logaritmo, cada coeficiente pasa a ser un efecto porcentual, y el error crece en proporción al salario en lugar de ser un monto fijo.

# MM2 con logaritmo del ingreso
mm2_log <- lm(log(Ingreso) ~ Experiencia + Antiguedad + Cargo + Genero, data = base)
summary(mm2_log)
## 
## Call:
## lm(formula = log(Ingreso) ~ Experiencia + Antiguedad + Cargo + 
##     Genero, data = base)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.91136 -0.19016 -0.00042  0.19026  0.95805 
## 
## Coefficients:
##                               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)                  14.698050   0.022542 652.039  < 2e-16 ***
## Experiencia                   0.026863   0.001453  18.486  < 2e-16 ***
## Antiguedad                    0.004199   0.001546   2.715  0.00670 ** 
## CargoDirector_Investigación   1.249595   0.039880  31.334  < 2e-16 ***
## CargoDirector_Manofactura     0.661772   0.029940  22.103  < 2e-16 ***
## CargoEjecutivo_Ventas         0.663894   0.023843  27.845  < 2e-16 ***
## CargoGerente                  1.225932   0.039175  31.294  < 2e-16 ***
## CargoInvestigador_Cientifico -0.002849   0.024001  -0.119  0.90554    
## CargoRecursos_Humanos         0.174528   0.042703   4.087 4.61e-05 ***
## CargoRepresentante_Salud      0.662313   0.031192  21.234  < 2e-16 ***
## CargoRepresentante_Ventas    -0.109106   0.035710  -3.055  0.00229 ** 
## GeneroM                       0.009571   0.015045   0.636  0.52477    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.281 on 1458 degrees of freedom
## Multiple R-squared:  0.8225, Adjusted R-squared:  0.8212 
## F-statistic: 614.3 on 11 and 1458 DF,  p-value: < 2.2e-16
# Efectos en porcentaje: (exp(b) - 1) * 100
round((exp(coef(mm2_log)[-1]) - 1) * 100, 1)
##                  Experiencia                   Antiguedad 
##                          2.7                          0.4 
##  CargoDirector_Investigación    CargoDirector_Manofactura 
##                        248.9                         93.8 
##        CargoEjecutivo_Ventas                 CargoGerente 
##                         94.2                        240.7 
## CargoInvestigador_Cientifico        CargoRecursos_Humanos 
##                         -0.3                         19.1 
##     CargoRepresentante_Salud    CargoRepresentante_Ventas 
##                         93.9                        -10.3 
##                      GeneroM 
##                          1.0
# Supuestos del modelo transformado
bptest(mm2_log)
## 
##  studentized Breusch-Pagan test
## 
## data:  mm2_log
## BP = 125.78, df = 11, p-value < 2.2e-16
shapiro.test(residuals(mm2_log))
## 
##  Shapiro-Wilk normality test
## 
## data:  residuals(mm2_log)
## W = 0.99861, p-value = 0.2911
resettest(mm2_log)
## 
##  RESET test
## 
## data:  mm2_log
## RESET = 22.171, df1 = 2, df2 = 1456, p-value = 3.274e-10

Interpretación del modelo transformado.

  • Experiencia: cada año adicional se asocia con un ingreso 2,7 % mayor, manteniendo constantes las demás variables.
  • Antigüedad: cada año en la empresa suma un 0,4 %.
  • Cargo, frente a un Técnico de Laboratorio:
    • Director de Investigación: +249 %. Gerente: +241 %. Ambos ganan unas 3,5 veces lo que gana un técnico con la misma trayectoria.
    • Director de Manufactura, Ejecutivo de Ventas y Representante de Salud: alrededor de +94 %, casi el doble.
    • Recursos Humanos: +19 %.
    • Representante de Ventas: −10 %, ahora significativo (p = 0,002). En pesos la diferencia no se notaba, pero en términos relativos ganan menos que un técnico.
    • Investigador Científico: sin diferencia (p = 0,91).
  • Género: +1 % para los hombres, no significativo (p = 0,52).

Efecto sobre los supuestos.

  • El estadístico de Breusch-Pagan baja de 279,6 a 125,8. Sigue habiendo heterocedasticidad, pero ahora en sentido contrario: en logaritmo, los cargos altos son los más homogéneos.
  • La normalidad se cumple (W = 0,999, p = 0,29).
  • La linealidad no mejora (RESET, p < 0,001).

El R² y el AIC del modelo en logaritmo no se comparan con los del modelo original, porque la respuesta está en otra escala. La comparación se hace con el MAE en pesos.

5. Comparación de modelos y escenarios

Validación cruzada con las mismas particiones

Todos los modelos se evalúan con las mismas cinco particiones del Punto 1. En los modelos con selección paso a paso, el stepwise se aplica dentro de cada partición de entrenamiento. En los modelos en logaritmo, la predicción se devuelve a pesos antes de calcular el error. Además del MAE, se mide la cobertura del intervalo de predicción del 95 %: el porcentaje de empleados de prueba cuyo salario real queda dentro del intervalo.

# Validación cruzada general: ajusta en entrenamiento y evalúa en prueba
# ajuste: función que recibe los datos de entrenamiento y devuelve un modelo lm
cv_modelo <- function(ajuste, logaritmo = FALSE) {
  sapply(1:k, function(i) {
    entrenamiento <- base[particion != i, ]
    prueba        <- base[particion == i, ]
    modelo <- ajuste(entrenamiento)
    p <- predict(modelo, newdata = prueba, interval = "prediction", level = 0.95)
    if (logaritmo) p <- exp(p)   # volver a pesos
    c(MAE       = mean(abs(prueba$Ingreso - p[, "fit"])),
      Cobertura = mean(prueba$Ingreso >= p[, "lwr"] & prueba$Ingreso <= p[, "upr"]))
  })
}

modelos <- list(
  "S1: Experiencia"         = function(d) lm(Ingreso ~ Experiencia, data = d),
  "S2: Antigüedad"          = function(d) lm(Ingreso ~ Antiguedad, data = d),
  "S3: Género"              = function(d) lm(Ingreso ~ Genero, data = d),
  "MM1: sin cargo"          = function(d) lm(Ingreso ~ Experiencia + Antiguedad + Genero, data = d),
  "MM1 stepwise"            = function(d) step(lm(Ingreso ~ Experiencia + Antiguedad + Genero,
                                                  data = d), trace = 0),
  "MM2: con cargo"          = function(d) lm(Ingreso ~ Experiencia + Antiguedad + Cargo + Genero,
                                             data = d),
  "MM2 stepwise (ampliado)" = function(d) step(lm(Ingreso ~ Experiencia + Antiguedad + Edad +
                                                    Educacion + Cargo + Genero, data = d),
                                               trace = 0)
)

# Modelos en pesos
cv_pesos <- lapply(modelos, cv_modelo)

# Modelos en logaritmo
cv_log <- list(
  "S1 log"  = cv_modelo(function(d) lm(log(Ingreso) ~ Experiencia, data = d), logaritmo = TRUE),
  "MM2 log" = cv_modelo(function(d) lm(log(Ingreso) ~ Experiencia + Antiguedad + Cargo + Genero,
                                       data = d), logaritmo = TRUE)
)

todos <- c(cv_pesos, cv_log)

comparacion <- data.frame(
  Modelo    = c("Referencia: mediana", names(todos)),
  MAE       = c(mean(mae_mediana), sapply(todos, function(x) mean(x["MAE", ]))),
  Desv_Est  = c(sd(mae_mediana),   sapply(todos, function(x) sd(x["MAE", ]))),
  Cobertura = c(NA,                sapply(todos, function(x) mean(x["Cobertura", ])))
) %>%
  mutate(Mejora_vs_mediana = 1 - MAE / mean(mae_mediana)) %>%
  arrange(MAE)
rownames(comparacion) <- NULL

kable(comparacion, digits = c(0, 0, 0, 3, 3), format.args = list(big.mark = "."),
      caption = "Comparación de todos los modelos en validación cruzada (MAE en pesos)")
Comparación de todos los modelos en validación cruzada (MAE en pesos)
Modelo MAE Desv_Est Cobertura Mejora_vs_mediana
MM2: con cargo 1.304.404 37.467 0.950 0.610
MM2 stepwise (ampliado) 1.307.409 41.285 0.948 0.609
MM2 log 1.331.006 61.774 0.958 0.602
S1: Experiencia 2.218.163 140.867 0.931 0.337
MM1: sin cargo 2.220.597 133.745 0.929 0.337
MM1 stepwise 2.221.129 137.586 0.927 0.337
S1 log 2.300.671 192.244 0.958 0.313
S2: Antigüedad 2.963.646 135.804 0.938 0.115
Referencia: mediana 3.347.858 207.527 NA 0.000
S3: Género 3.644.874 173.204 0.913 -0.089
# MAE de todos los modelos
ggplot(comparacion, aes(x = reorder(Modelo, -MAE), y = MAE)) +
  geom_col(fill = ifelse(comparacion$Modelo == "MM2: con cargo", azul, gris)) +
  geom_text(aes(label = millones(MAE)), hjust = -0.1, size = 3.5) +
  coord_flip() +
  scale_y_continuous(labels = millones, expand = expansion(mult = c(0, 0.15))) +
  labs(title = "Error absoluto medio en validación cruzada",
       subtitle = "Mismas cinco particiones para todos los modelos",
       x = NULL, y = "MAE") +
  tema

Resultados.

  • MM2 (con cargo): MAE de 1,30 M, un 61 % menor que la referencia de la mediana (3,35 M) y un 41 % menor que el mejor modelo simple (2,22 M). Además es el más estable entre particiones (desviación de 37 mil pesos).
  • MM2 con stepwise dentro del entrenamiento: 1,31 M, prácticamente igual. Llegar al MM2 por selección automática no cambia el resultado.
  • MM2 en logaritmo: 1,33 M, algo peor en promedio.
  • MM1 (sin cargo): 2,22 M, lo mismo que la regresión simple con experiencia. Sin el cargo, la antigüedad y el género no agregan capacidad predictiva.
  • Cobertura: el MM2 en pesos cubre el 95,0 % de los casos de prueba en total. Es justo lo que debería cubrir un intervalo del 95 %, pero ese promedio esconde diferencias por cargo, como se ve a continuación.
# Cobertura y ancho del intervalo del 95 % por cargo, en validación cruzada
detalle <- do.call(rbind, lapply(1:k, function(i) {
  entrenamiento <- base[particion != i, ]
  prueba        <- base[particion == i, ]
  p_lin <- predict(lm(Ingreso ~ Experiencia + Antiguedad + Cargo + Genero, data = entrenamiento),
                   newdata = prueba, interval = "prediction")
  p_log <- exp(predict(lm(log(Ingreso) ~ Experiencia + Antiguedad + Cargo + Genero,
                          data = entrenamiento), newdata = prueba, interval = "prediction"))
  data.frame(Cargo     = prueba$Cargo,
             Real      = prueba$Ingreso,
             Pred      = p_lin[, "fit"],
             Cub_pesos = prueba$Ingreso >= p_lin[, "lwr"] & prueba$Ingreso <= p_lin[, "upr"],
             Cub_log   = prueba$Ingreso >= p_log[, "lwr"] & prueba$Ingreso <= p_log[, "upr"],
             Limite_negativo = p_lin[, "lwr"] < 0)
}))

detalle %>%
  group_by(Cargo) %>%
  summarise(n = n(),
            Cobertura_pesos = mean(Cub_pesos),
            Cobertura_log   = mean(Cub_log),
            Limite_inferior_negativo = mean(Limite_negativo)) %>%
  arrange(Cobertura_pesos) %>%
  kable(digits = 3, caption = "Cobertura del intervalo de predicción del 95 % por cargo")
Cobertura del intervalo de predicción del 95 % por cargo
Cargo n Cobertura_pesos Cobertura_log Limite_inferior_negativo
Director_Manofactura 145 0.890 0.979 0.000
Director_Investigación 80 0.900 1.000 0.000
Recursos_Humanos 52 0.904 0.865 0.135
Representante_Salud 131 0.916 0.977 0.000
Ejecutivo_Ventas 326 0.923 0.985 0.000
Gerente 102 0.951 1.000 0.000
Investigador_Cientifico 292 0.990 0.942 0.589
Tecnico_Laboratorio 259 0.996 0.923 0.556
Representante_Ventas 83 1.000 0.916 0.819

Qué muestra la cobertura por cargo.

  • MM2 en pesos: el intervalo mide unos 6,5 M para todos.
    • En los cargos de menor salario cubre entre el 99 % y el 100 % de los casos, porque es demasiado ancho. En más de la mitad de los técnicos e investigadores su límite inferior es negativo.
    • En Director de Manufactura, Director de Investigación, Recursos Humanos, Representante de Salud y Ejecutivo de Ventas cubre solo entre el 89 % y el 92 %, por debajo del 95 % prometido. Solo en Gerente la cobertura es la correcta (95 %).
  • MM2 en logaritmo: el intervalo se ajusta al nivel salarial de cada cargo.
    • Para los cargos bajos es más estrecho y nunca es negativo.
    • Para gerentes y directores se vuelve demasiado amplio, con límites superiores por encima del salario máximo de la empresa (20 M).

Elección del mejor modelo: MM2 en pesos.

  • Precisión: tiene el menor MAE de todos los modelos.
  • Coherencia: los signos son los esperados y los efectos son estables.
  • Interpretabilidad: los coeficientes se leen directamente en pesos.

Para los intervalos, conviene usar la versión en logaritmo en los cargos de menor salario, donde el modelo en pesos da límites imposibles.

Escenarios hipotéticos

Se plantean dos casos realistas para Recursos Humanos:

  • Escenario A: contratación de una Técnica de Laboratorio junior. Mujer, 3 años de experiencia, 1 año en la empresa. Es el perfil típico de un nuevo ingreso en uno de los cargos más numerosos.
  • Escenario B: revisión salarial de un Gerente con trayectoria. Hombre, 22 años de experiencia, 10 en la empresa. La mediana de experiencia de los gerentes es de 24 años.
# Perfiles hipotéticos
escenarios_mm2 <- data.frame(
  Experiencia = c(3, 22),
  Antiguedad  = c(1, 10),
  Cargo  = factor(c("Tecnico_Laboratorio", "Gerente"), levels = levels(base$Cargo)),
  Genero = factor(c("F", "M"), levels = levels(base$Genero))
)

# Predicción e intervalo del 95 %: MM2 en pesos y en logaritmo
p_pesos <- predict(mm2, newdata = escenarios_mm2, interval = "prediction", level = 0.95)
p_log   <- exp(predict(mm2_log, newdata = escenarios_mm2, interval = "prediction", level = 0.95))

data.frame(
  Escenario  = rep(c("A: Técnica junior", "B: Gerente senior"), 2),
  Modelo     = rep(c("MM2 pesos", "MM2 log"), each = 2),
  Prediccion = c(p_pesos[, "fit"], p_log[, "fit"]),
  Limite_inf = c(p_pesos[, "lwr"], p_log[, "lwr"]),
  Limite_sup = c(p_pesos[, "upr"], p_log[, "upr"])
) %>%
  kable(digits = 0, format.args = list(big.mark = "."),
        caption = "Predicciones e intervalos de predicción del 95 % (pesos)")
Predicciones e intervalos de predicción del 95 % (pesos)
Escenario Modelo Prediccion Limite_inf Limite_sup
A: Técnica junior MM2 pesos 2.163.341 -1.092.912 5.419.594
B: Gerente senior MM2 pesos 16.687.739 13.423.200 19.952.278
A: Técnica junior MM2 log 2.630.903 1.513.759 4.572.491
B: Gerente senior MM2 log 15.658.524 8.996.880 27.252.711
# Referencia real: salarios observados en perfiles parecidos
base %>%
  filter((Cargo == "Tecnico_Laboratorio" & Experiencia <= 5) |
         (Cargo == "Gerente" & Experiencia >= 18 & Experiencia <= 26)) %>%
  group_by(Cargo) %>%
  summarise(n = n(), Mediana = median(Ingreso), Minimo = min(Ingreso), Maximo = max(Ingreso))
Cargo n Mediana Minimo Maximo
Tecnico_Laboratorio 88 2519000 1102000 4841000
Gerente 54 18051000 14756000 19926000

Escenario A (Técnica junior).

  • MM2 en pesos: predice 2,16 M, con un intervalo de −1,09 M a 5,42 M. El límite inferior negativo no tiene sentido.
  • MM2 en logaritmo: predice 2,63 M, con un intervalo de 1,51 M a 4,57 M.
  • Salarios reales: los técnicos con hasta 5 años de experiencia ganan entre 1,10 M y 4,84 M, con mediana de 2,52 M. El intervalo en logaritmo coincide casi exactamente con ese rango.
  • Uso: Recursos Humanos podría ofrecer alrededor de 2,5 M, con un rango de negociación de 1,5 a 4,6 M.

Escenario B (Gerente senior).

  • MM2 en pesos: predice 16,69 M, con un intervalo de 13,42 M a 19,95 M.
  • MM2 en logaritmo: el intervalo llega hasta 27,3 M, por encima de cualquier salario de la empresa.
  • Salarios reales: los gerentes con 18 a 26 años de experiencia ganan entre 14,76 M y 19,93 M, con mediana de 18,05 M. El intervalo en pesos coincide con ese rango.
  • Uso: si el gerente gana hoy 12 M, quedaría por debajo del intervalo y su salario merecería revisión, por ejemplo por riesgo de que se vaya.

D. Punto 3. Análisis crítico

# Error del MM2 por cargo en validación cruzada, absoluto y relativo a la mediana del cargo
error_cargo <- detalle %>%
  group_by(Cargo) %>%
  summarise(n = n(),
            MAE = mean(abs(Real - Pred)),
            Mediana_ingreso = median(Real),
            Error_relativo = MAE / Mediana_ingreso,
            Sesgo_medio = mean(Pred - Real)) %>%
  arrange(Error_relativo)

kable(error_cargo, digits = c(0, 0, 0, 0, 3, 0), format.args = list(big.mark = "."),
      caption = "Error de predicción del MM2 por cargo (validación cruzada)")
Error de predicción del MM2 por cargo (validación cruzada)
Cargo n MAE Mediana_ingreso Error_relativo Sesgo_medio
Gerente 102 1.601.940 17.454.500 0.092 11.240
Director_Investigación 80 1.951.305 16.510.000 0.118 8.782
Representante_Salud 131 1.609.033 6.811.000 0.236 27.124
Ejecutivo_Ventas 326 1.604.476 6.231.000 0.257 909
Representante_Ventas 83 666.557 2.579.000 0.258 6.344
Director_Manofactura 145 1.695.786 6.447.000 0.263 4.642
Investigador_Cientifico 292 842.882 2.887.500 0.292 882
Tecnico_Laboratorio 259 912.448 2.886.000 0.316 -2.392
Recursos_Humanos 52 1.547.504 3.093.000 0.500 2.937
# Proporción de predicciones que se equivocan en más de 2 millones
mean(abs(detalle$Real - detalle$Pred) > 2e6)
## [1] 0.229932
# Empleados actuales cuyo salario queda fuera del intervalo del 95 %
fuera <- predict(mm2, interval = "prediction")
fuera_rango <- base$Ingreso < fuera[, "lwr"] | base$Ingreso > fuera[, "upr"]
sum(fuera_rango)
## [1] 72
# Error relativo por cargo
ggplot(error_cargo, aes(x = reorder(Cargo, -Error_relativo), y = Error_relativo)) +
  geom_col(fill = azul) +
  geom_text(aes(label = percent(Error_relativo, accuracy = 1)), hjust = -0.1, size = 3.5) +
  coord_flip() +
  scale_y_continuous(labels = percent, expand = expansion(mult = c(0, 0.15))) +
  labs(title = "Error del MM2 como porcentaje del salario mediano del cargo",
       x = NULL, y = "MAE / mediana del cargo") +
  tema

¿Quién utilizaría el modelo y para qué decisión?

El área de Recursos Humanos, para tres decisiones:

  1. Definir bandas salariales por cargo y nivel de experiencia.
  2. Preparar ofertas para nuevos ingresos, con un rango de negociación (escenario A).
  3. Detectar salarios fuera de rango entre los empleados actuales (escenario B). Con el MM2, 72 empleados tienen un salario fuera de su intervalo de predicción del 95 %. Son los casos que convendría revisar primero.

¿Qué significa el error de predicción? ¿Es aceptable?

  • En promedio: el MM2 se equivoca en 1,30 M por empleado, frente a 3,35 M si se usara la mediana para todos. Casi 1 de cada 4 predicciones (23 %) se equivoca en más de 2 M.
  • Depende del cargo. Medido frente al salario típico de cada cargo, el error relativo es de:
    • Alrededor del 10 % en Gerentes y Directores de Investigación. Es aceptable para fijar bandas.
    • Del 24 % al 32 % en los demás cargos. Sirve como referencia, pero no para decidir un salario puntual.
    • El 50 % en Recursos Humanos, el cargo con menos empleados (52). No es aceptable.
  • Sin sesgo sistemático. El sesgo medio por cargo es cercano a cero: el modelo no sobreestima ni subestima de forma sistemática a ningún cargo.

¿Qué consecuencias tendría sobreestimar o subestimar?

  • Sobreestimar lleva a ofrecer más de lo necesario. Eso implica un costo laboral mayor y posibles inequidades internas: un nuevo empleado ganaría más que compañeros con la misma trayectoria. El costo es acumulativo, porque los salarios rara vez se bajan.
  • Subestimar lleva a ofertas que el candidato rechaza o a salarios por debajo del mercado interno. La base sugiere que esto tiene costo: quienes dejaron la empresa tenían una mediana de ingreso de 3,2 M, frente a 5,2 M de quienes se quedaron. Es una asociación, no una prueba de que el salario bajo cause la salida, pero indica que subestimar puede costar rotación.
  • En una empresa, subestimar suele ser más costoso en cargos críticos (gerentes y directores), y sobreestimar en cargos masivos (técnicos, ejecutivos de ventas), donde el error se multiplica por cientos de personas.

¿Cuáles son las principales limitaciones y cuándo no debería usarse?

Limitaciones.

  • Una sola empresa en un solo momento. Los coeficientes reflejan la política salarial de esta empresa, no la del mercado. Además, la base es un corte transversal.
  • Techo salarial. El ingreso máximo es de 20 M. El modelo no puede predecir salarios por encima de ese valor.
  • Información faltante. La base no tiene historial salarial, nivel jerárquico dentro del cargo ni desempeño histórico. Rendimiento solo toma los valores 3 y 4, sin casos de bajo rendimiento.
  • Intervalos mal calibrados por cargo. El intervalo del MM2 en pesos tiene el mismo ancho para todos.
  • Asociación, no causalidad. El modelo describe cómo están los salarios hoy, no cómo deberían estar. Si existieran sesgos en la asignación de cargos, el modelo los reproduciría.

No debería usarse para:

  • Otras empresas o sectores con estructuras salariales distintas.
  • Cargos que no estén entre los nueve de la base, o perfiles con más de 40 años de experiencia.
  • Fijar un salario de forma automática, sin revisión humana.
  • Decidir aumentos. El porcentaje de aumento depende del rendimiento, no del cargo ni de la experiencia.
  • Recursos Humanos como cargo, por su error relativo del 50 %.
  • Usar el género como criterio salarial. Se incluyó solo como control.

¿Se recomienda aplicarlo? ¿Qué información adicional se necesitaría?

Sí, como herramienta de referencia y con condiciones:

  • Usar el MM2 para la predicción puntual. Para los intervalos, usar la versión en logaritmo en los cargos de menor salario y la versión en pesos en los cargos altos.
  • Usarlo para construir bandas y priorizar revisiones, no para fijar salarios individuales sin análisis humano.
  • Reestimarlo cada año, a medida que cambie la política salarial.

Información adicional que lo mejoraría:

  • Salarios de mercado por cargo (encuestas salariales del sector).
  • Historial salarial y fecha de ingreso al cargo actual.
  • Nivel jerárquico dentro del cargo (junior, senior).
  • Evaluaciones de desempeño de varios años, que incluyan calificaciones bajas.
  • Certificaciones y habilidades específicas.
  • Más observaciones de Recursos Humanos.

E. Conclusiones

  1. El cargo y la experiencia explican el salario. Juntos explican el 88 % de la variabilidad del ingreso. Edad, educación, horas extra, rendimiento y porcentaje de aumento no agregan capacidad predictiva.
  2. El mejor modelo es el MM2 en pesos. Su error promedio es de 1,30 M, un 61 % menor que predecir la mediana y un 41 % menor que el mejor modelo simple.
  3. El error depende del cargo. Es de alrededor del 10 % del salario en los cargos altos y del 24 % al 32 % en los demás. Es útil para bandas salariales y para detectar casos atípicos, no para fijar salarios exactos.
  4. No hay evidencia suficiente de una brecha salarial por género entre personas con el mismo cargo y la misma trayectoria (p = 0,058), aunque el resultado está en el límite y conviene seguirlo.
  5. El principal problema estadístico es la heterocedasticidad por cargo. No sesga los coeficientes, pero hace que los intervalos del modelo en pesos sean poco confiables en los extremos. La transformación logarítmica corrige el problema en los cargos bajos, pero no en los altos.

Recomendación: aplicar el modelo como apoyo a Recursos Humanos para definir rangos salariales y priorizar revisiones, siempre con revisión humana, sin usar el género como criterio y reestimándolo con información de mercado y de desempeño.