Práctico 05. Estandarización de coeficientes

Métodos estadísticos para Ciencias Sociales III

Fecha de publicación

14 de septiembre de 2026

1 Presentación

1.1 Objetivo de la práctica

En el práctico anterior estimamos modelos de regresión múltiple y quedó planteada una pregunta que no pudimos responder: si un modelo tiene varios predictores medidos en unidades distintas, ¿cuál de ellos pesa más?

Esta guía responde esa pregunta mediante la estandarización de variables. Al terminar, usted debería ser capaz de:

  1. Explicar por qué dos coeficientes de regresión no siempre son comparables entre sí
  2. Calcular un puntaje Z a mano y con la función scale()
  3. Estimar un modelo con variables estandarizadas e interpretar sus coeficientes
  4. Verificar qué cambia y qué permanece idéntico al estandarizar
  5. Convertir un coeficiente de una escala a la otra sin reestimar el modelo
  6. Decidir cuándo conviene reportar coeficientes estandarizados y cuándo no

Esta mañana trabajaremos el procedimiento en R. En la sesión de la tarde revisaremos con más detalle sus fundamentos y sus límites.

1.2 Los datos

Continuamos con el estatus social subjetivo en ELSOC 2016, el mismo modelo del práctico anterior. Más adelante incorporaremos un segundo ejemplo con una variable construida a partir de una escala.

2 Preparación

2.1 Librerías

pacman::p_load(dplyr,
               sjmisc,
               sjPlot,
               kableExtra,
               summarytools,
               psych,
               ggplot2)
rm(list = ls())
options(scipen = 999)

# Ajustes para que las tablas de summarytools se vean bien en Quarto
st_options(bootstrap.css = FALSE,
           plain.ascii = FALSE,
           style = "rmarkdown",
           dfSummary.silent = TRUE,
           footnote = NA,
           subtitle.emphasis = FALSE)

2.2 Carga y procesamiento

load(url("https://github.com/Kevin-carrasco/metod3-unab/raw/refs/heads/main/files/data/elsoc16-22.RData"))
proc_data <- elsoc_long_2016_2022.2 %>%
  filter(ola == 1) %>%
  select(d01_01, m01, m29, m0_edad) %>%
  set_na(., na = c(-999, -888, -777, -666)) %>%
  rename("estatus" = d01_01,
         "educacion" = m01,
         "ingreso" = m29,
         "edad" = m0_edad) %>%
  na.omit() %>%
  filter(ingreso >= 50000, ingreso <= 5000000) %>%
  mutate(ing100 = ingreso / 100000) %>%
  select(estatus, educacion, ing100, edad)

dim(proc_data)
[1] 2301    4

2.3 El modelo del práctico anterior

Retomamos el modelo con el que cerramos el práctico anterior:

modelo <- lm(estatus ~ educacion + ing100 + edad, data = proc_data)
tab_model(modelo,
          show.se = TRUE,
          show.ci = FALSE,
          digits = 4,
          p.style = "stars",
          dv.labels = c("Estatus social subjetivo"),
          pred.labels = c("(Intercepto)", "Nivel educacional",
                          "Ingreso (cientos de miles)", "Edad"),
          string.pred = "Predictores",
          string.est = "β")
  Estatus social subjetivo
Predictores β std. Error
(Intercepto) 2.9215 *** 0.1465
Nivel educacional 0.1611 *** 0.0165
Ingreso (cientos de miles) 0.0483 *** 0.0061
Edad 0.0063 ** 0.0021
Observations 2301
R2 / R2 adjusted 0.113 / 0.111
* p<0.05   ** p<0.01   *** p<0.001

Los tres predictores son estadísticamente significativos y el modelo explica algo más de un 11% de la varianza. Hasta aquí, todo conocido.

3 El problema

3.1 ¿Cuál predictor pesa más?

Observe la columna de coeficientes. El de educación es aproximadamente veinticinco veces mayor que el de edad. ¿Significa eso que la educación es veinticinco veces más importante?

La respuesta es que no, y la razón está en las unidades de medida de cada variable. Revisemos los descriptivos:

dfSummary(proc_data,
          varnumbers = FALSE,
          valid.col = FALSE,
          graph.magnif = 0.75,
          headings = FALSE) %>%
  print(method = "render")
Variable Stats / Values Freqs (% of Valid) Graph Missing
estatus [numeric]
Mean (sd) : 4.3 (1.5)
min ≤ med ≤ max:
0 ≤ 5 ≤ 10
IQR (CV) : 2 (0.4)
11 distinct values 0 (0.0%)
educacion [numeric]
Mean (sd) : 5.2 (2.2)
min ≤ med ≤ max:
1 ≤ 5 ≤ 10
IQR (CV) : 3 (0.4)
1 : 31 ( 1.3% )
2 : 272 ( 11.8% )
3 : 238 ( 10.3% )
4 : 325 ( 14.1% )
5 : 677 ( 29.4% )
6 : 75 ( 3.3% )
7 : 296 ( 12.9% )
8 : 135 ( 5.9% )
9 : 221 ( 9.6% )
10 : 31 ( 1.3% )
0 (0.0%)
ing100 [numeric]
Mean (sd) : 5.9 (5.6)
min ≤ med ≤ max:
0.5 ≤ 4.5 ≤ 50
IQR (CV) : 4.2 (0.9)
178 distinct values 0 (0.0%)
edad [numeric]
Mean (sd) : 46.1 (15.1)
min ≤ med ≤ max:
18 ≤ 46 ≤ 88
IQR (CV) : 23 (0.3)
63 distinct values 0 (0.0%)

Un año adicional de edad es un cambio muy pequeño dentro de una variable que recorre más de cincuenta años. Un nivel educacional adicional, en cambio, es un cambio grande dentro de una escala de diez categorías. Comparar ambos coeficientes es como comparar una distancia medida en milímetros con otra medida en kilómetros.

3.2 Una comprobación

Para ver que el problema es de unidades y no sustantivo, hagamos un experimento: expresemos la edad en décadas en lugar de años.

proc_data$edad_decadas <- proc_data$edad / 10

modelo_decadas <- lm(estatus ~ educacion + ing100 + edad_decadas,
                     data = proc_data)

c(edad_en_anios = coef(modelo)[4],
  edad_en_decadas = coef(modelo_decadas)[4])
          edad_en_anios.edad edad_en_decadas.edad_decadas 
                 0.006267146                  0.062671459 

El coeficiente se multiplicó por diez sin que nada haya cambiado en los datos ni en la relación entre las variables. Lo único que cambió fue la unidad en que medimos el predictor.

c(r2_original = summary(modelo)$r.squared,
  r2_decadas = summary(modelo_decadas)$r.squared)
r2_original  r2_decadas 
  0.1126182   0.1126182 

El ajuste del modelo es idéntico. Esto confirma que la magnitud de un coeficiente, por sí sola, no informa nada sobre la importancia de un predictor.

proc_data$edad_decadas <- NULL # eliminamos la variable auxiliar

4 La estandarización

4.1 El puntaje Z

La solución consiste en llevar todas las variables a una escala común. El procedimiento es el puntaje Z: a cada valor se le resta el promedio de su variable y se divide por su desviación estándar.

\[z_x=\frac{x-\bar{x}}{sd(x)}\]

El resultado se interpreta en desviaciones estándar: un valor de \(z = 1\) indica que el caso está una desviación estándar por sobre el promedio, y uno de \(z = -0{,}5\), media desviación estándar por debajo.

4.2 Cálculo manual

Hagámoslo primero a mano, para una sola variable:

media_educacion <- mean(proc_data$educacion)
de_educacion <- sd(proc_data$educacion)

proc_data$educacion_z <- (proc_data$educacion - media_educacion) / de_educacion

Comparemos los primeros casos:

head(proc_data %>% select(educacion, educacion_z), 8) %>%
  kable(digits = 3,
        col.names = c("Educación (original)", "Educación (estandarizada)")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = FALSE)
Educación (original) Educación (estandarizada)
2 -1.449
4 -0.532
5 -0.074
5 -0.074
5 -0.074
9 1.760
9 1.760
4 -0.532

Verifiquemos que la variable transformada tiene las propiedades esperadas:

c(media = mean(proc_data$educacion_z), de = sd(proc_data$educacion_z))
                   media                       de 
0.0000000000000001779002 1.0000000000000000000000 

El promedio es cero (salvo error de redondeo computacional) y la desviación estándar es uno.

4.3 La función scale()

Naturalmente, no haremos esto variable por variable. La función scale() estandariza todas las columnas de una base de datos:

NotaEstandarizar una base completa

datos_std <- data.frame(scale(datos))

La función data.frame() es necesaria porque scale() devuelve una matriz, y lm() espera un data.frame.

proc_data$educacion_z <- NULL # eliminamos la variable que creamos a mano

proc_data_std <- data.frame(scale(proc_data))

Comparemos los descriptivos antes y después:

descr(proc_data,
      stats = c("mean", "sd", "min", "med", "max", "n.valid"),
      transpose = TRUE,
      order = "preserve") %>%
  kable(digits = 2, caption = "Variables originales") %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = FALSE)
Variables originales
Mean Std.Dev Min Median Max N.Valid
estatus 4.33 1.53 0.0 5.0 10 2301
educacion 5.16 2.18 1.0 5.0 10 2301
ing100 5.88 5.58 0.5 4.5 50 2301
edad 46.07 15.14 18.0 46.0 88 2301
descr(proc_data_std,
      stats = c("mean", "sd", "min", "med", "max", "n.valid"),
      transpose = TRUE,
      order = "preserve") %>%
  kable(digits = 2, caption = "Variables estandarizadas") %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = FALSE)
Variables estandarizadas
Mean Std.Dev Min Median Max N.Valid
estatus 0 1 -2.83 0.44 3.72 2301
educacion 0 1 -1.91 -0.07 2.22 2301
ing100 0 1 -0.96 -0.25 7.91 2301
edad 0 1 -1.85 0.00 2.77 2301

Note que todas las variables quedaron con media 0 y desviación estándar 1, incluida la variable dependiente. El mínimo y el máximo, en cambio, son distintos para cada una, porque dependen de cuán alejados estén los valores extremos respecto del promedio.

AdvertenciaDos paquetes, una misma función

Tanto sjmisc como summarytools incluyen una función llamada descr, y hacen cosas distintas. Cuando dos paquetes cargados definen el mismo nombre, R usa el del paquete cargado más recientemente, que aquí es summarytools.

De ahí el argumento transpose = TRUE: sin él, summarytools::descr ubica los estadísticos en las filas y las variables en las columnas, que es lo contrario de lo habitual. El argumento order = "preserve" evita además que las variables se ordenen alfabéticamente.

Si quiere asegurarse de cuál función está usando, escriba el nombre del paquete de forma explícita, como en sjmisc::descr() o summarytools::descr().

AdvertenciaUna advertencia sobre scale()

La función estandariza todas las columnas de la base, sin distinguir. Si su base contiene identificadores, variables categóricas o cualquier columna que no corresponda estandarizar, debe seleccionar antes las variables del modelo. Por eso en el procesamiento inicial nos quedamos únicamente con las cuatro variables de interés.

5 El modelo estandarizado

5.1 Estimación

La sintaxis es exactamente la misma; lo único que cambia es la base de datos:

modelo_std <- lm(estatus ~ educacion + ing100 + edad, data = proc_data_std)

coef(modelo_std)
              (Intercept)                 educacion                    ing100 
0.00000000000000004624409 0.23011065196029412138756 0.17646974878240034856347 
                     edad 
0.06215195141068993039957 

5.2 Comparación

tab_model(list(modelo, modelo_std),
          show.se = TRUE,
          show.ci = FALSE,
          digits = 3,
          p.style = "stars",
          dv.labels = c("No estandarizado", "Estandarizado"),
          pred.labels = c("(Intercepto)", "Nivel educacional",
                          "Ingreso (cientos de miles)", "Edad"),
          string.pred = "Predictores",
          string.est = "β")
  No estandarizado Estandarizado
Predictores β std. Error β std. Error
(Intercepto) 2.922 *** 0.146 0.000 0.020
Nivel educacional 0.161 *** 0.016 0.230 *** 0.024
Ingreso (cientos de miles) 0.048 *** 0.006 0.176 *** 0.022
Edad 0.006 ** 0.002 0.062 ** 0.021
Observations 2301 2301
R2 / R2 adjusted 0.113 / 0.111 0.113 / 0.111
* p<0.05   ** p<0.01   *** p<0.001

5.3 Interpretación

Los coeficientes estandarizados se leen en desviaciones estándar de la variable dependiente por cada desviación estándar de la independiente.

round(coef(modelo_std), 3)
(Intercept)   educacion      ing100        edad 
      0.000       0.230       0.176       0.062 
  • Educación: por cada desviación estándar adicional de nivel educacional, el estatus subjetivo aumenta el valor del coeficiente en desviaciones estándar, manteniendo constantes el ingreso y la edad
  • Ingreso: la misma lectura, por cada desviación estándar adicional de ingreso del hogar
  • Edad: ídem

Y ahora sí pueden compararse entre sí. Observe que la distancia entre educación e ingreso es mucho menor de lo que sugerían los coeficientes originales: en escala bruta el de educación parecía más de tres veces mayor, y estandarizados resultan bastante cercanos.

ImportanteEl hallazgo

En la escala original, el coeficiente del ingreso parecía casi despreciable. Estandarizado, resulta tener un peso comparable al de la educación. La impresión inicial era un artefacto de las unidades de medida.

6 Qué cambia y qué no

6.1 El ajuste no cambia

c(original = summary(modelo)$r.squared,
  estandarizado = summary(modelo_std)$r.squared)
     original estandarizado 
    0.1126182     0.1126182 

Estandarizar no altera el poder explicativo del modelo. Es la misma información expresada en otra escala.

6.2 La inferencia tampoco

round(summary(modelo)$coefficients[, 3:4], 4)
            t value Pr(>|t|)
(Intercept) 19.9484   0.0000
educacion    9.7820   0.0000
ing100       7.9194   0.0000
edad         2.9574   0.0031
round(summary(modelo_std)$coefficients[, 3:4], 4)
            t value Pr(>|t|)
(Intercept)  0.0000   1.0000
educacion    9.7820   0.0000
ing100       7.9194   0.0000
edad         2.9574   0.0031

Los valores \(t\) y los valores p de los predictores son idénticos. Un coeficiente significativo lo sigue siendo, y uno que no lo era tampoco pasa a serlo. La estandarización responde a la pregunta de la importancia relativa, no a la de significación.

6.3 El intercepto sí cambia

c(original = coef(modelo)[1], estandarizado = coef(modelo_std)[1])
     original.(Intercept) estandarizado.(Intercept) 
2.92152986247441726064267 0.00000000000000004624409 

En el modelo estandarizado el intercepto es cero, porque todas las variables fueron centradas en su promedio. Deja de tener cualquier interpretación sustantiva.

6.4 Una alternativa con tab_model

La librería sjPlot puede mostrar ambas columnas en una sola tabla, sin necesidad de estimar un segundo modelo:

tab_model(modelo,
          show.std = "std",
          show.se = TRUE,
          show.ci = FALSE,
          digits = 3,
          p.style = "stars",
          dv.labels = c("Estatus subjetivo"),
          pred.labels = c("(Intercepto)", "Nivel educacional",
                          "Ingreso (cientos de miles)", "Edad"),
          string.pred = "Predictores")
  Estatus subjetivo
Predictores Estimates std. Error std. Beta standardized std. Error
(Intercepto) 2.922 *** 0.146 0.000 0.020
Nivel educacional 0.161 *** 0.016 0.230 0.024
Ingreso (cientos de miles) 0.048 *** 0.006 0.176 0.022
Edad 0.006 ** 0.002 0.062 0.021
Observations 2301
R2 / R2 adjusted 0.113 / 0.111
* p<0.05   ** p<0.01   *** p<0.001

Esta es la forma habitual de reportarlo en un artículo cuando interesan ambas lecturas.

7 Un segundo ejemplo

Hasta aquí trabajamos con variables medidas directamente. El caso donde la estandarización resulta más necesaria es el de las variables construidas, cuyas unidades son arbitrarias.

7.1 Construcción de una escala

El módulo de salud y bienestar de ELSOC incluye nueve ítems sobre la frecuencia de distintos estados anímicos durante las últimas dos semanas, con categorías de respuesta que van desde “nunca” hasta “todos los días”.

animo <- elsoc_long_2016_2022.2 %>%
  filter(ola == 1) %>%
  select(s11_01, s11_02, s11_03, s11_04, s11_05,
         s11_06, s11_07, s11_08, s11_09,
         educacion = m01,
         ingreso = m29,
         edad = m0_edad) %>%
  set_na(., na = c(-999, -888, -777, -666)) %>%
  na.omit() %>%
  filter(ingreso >= 50000, ingreso <= 5000000)

Recodificamos los ítems para que la categoría más baja sea 0:

animo <- animo %>%
  mutate(across(s11_01:s11_09, ~ .x - 1))

Antes de sumar los ítems conviene verificar que efectivamente miden lo mismo. Una medida habitual de consistencia interna es el alfa de Cronbach:

psych::alpha(animo %>% select(s11_01:s11_09))$total$raw_alpha
[1] 0.8520755

El valor obtenido es alto, lo que respalda la decisión de combinarlos en un solo puntaje. La construcción y validación de escalas es materia de la última unidad del curso; aquí nos basta con esta verificación mínima.

animo <- animo %>%
  mutate(ing100 = ingreso / 100000,
         sintomas = rowSums(across(s11_01:s11_09))) %>%
  select(sintomas, educacion, ing100, edad)

summary(animo$sintomas)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  0.000   2.000   5.000   6.121   9.000  36.000 

7.2 El problema de las unidades arbitrarias

¿Cuánto es “un punto” en esta escala? La respuesta es que depende enteramente de cómo la construimos: si hubiéramos promediado en lugar de sumar, o si la escala tuviera doce ítems en lugar de nueve, el mismo fenómeno produciría coeficientes de magnitud muy distinta.

modelo_animo <- lm(sintomas ~ educacion + ing100 + edad, data = animo)

animo_std <- data.frame(scale(animo))
modelo_animo_std <- lm(sintomas ~ educacion + ing100 + edad, data = animo_std)
tab_model(list(modelo_animo, modelo_animo_std),
          show.se = TRUE,
          show.ci = FALSE,
          digits = 3,
          p.style = "stars",
          dv.labels = c("No estandarizado", "Estandarizado"),
          pred.labels = c("(Intercepto)", "Nivel educacional",
                          "Ingreso", "Edad"),
          string.pred = "Predictores",
          string.est = "β")
  No estandarizado Estandarizado
Predictores β std. Error β std. Error
(Intercepto) 8.581 *** 0.582 -0.000 0.021
Nivel educacional -0.294 *** 0.066 -0.111 *** 0.025
Ingreso -0.086 *** 0.024 -0.083 *** 0.023
Edad -0.010 0.008 -0.025 0.022
Observations 2285 2285
R2 / R2 adjusted 0.026 / 0.024 0.026 / 0.024
* p<0.05   ** p<0.01   *** p<0.001

Observe nuevamente el patrón: en escala original el coeficiente de educación parece más de tres veces mayor que el de ingreso, y estandarizados resultan mucho más cercanos. Note también que la edad no alcanza significación estadística en ninguno de los dos modelos, lo que confirma que estandarizar no modifica la inferencia.

8 Ejercicios

  1. Cargue la base ELSOC y filtre por la ola 4 (2019). Seleccione d01_01, m01, m29 y m0_edad, recodifique los valores perdidos, aplique el mismo criterio de limpieza al ingreso y exprese el ingreso en cientos de miles.

  2. Estime el modelo de estatus subjetivo con los tres predictores y reporte sus coeficientes.

8.1 Estandarización

  1. Estandarice a mano la variable de edad, creando una nueva columna. Verifique que su media es cero y su desviación estándar uno.

  2. Estandarice la base completa con scale() y confirme que su cálculo manual coincide con la columna correspondiente.

  3. Estime el modelo estandarizado y presente una tabla comparativa con ambos modelos.

8.2 Interpretación

  1. Interprete el coeficiente estandarizado de cada predictor, redactando una oración completa para cada uno.

  2. Ordene los tres predictores según su peso relativo y justifique por qué ese orden no puede establecerse a partir de los coeficientes originales.

  3. Verifique que el \(R^2\) y los valores p son idénticos en ambos modelos, y explique por qué ocurre esto.

8.3 Reporte

  1. Redacte un párrafo de máximo 250 palabras que reporte el modelo, interprete el predictor de mayor peso relativo y señale explícitamente qué agrega la lectura estandarizada respecto de la no estandarizada.

  2. Señale una situación de investigación en la que no convendría reportar coeficientes estandarizados, y justifique brevemente.